mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
Port exporting model as python code to c++
This commit is contained in:
parent
d16f117b6c
commit
0a32add69e
@ -153,6 +153,12 @@ class QgsProcessingModelAlgorithm : QgsProcessingAlgorithm
|
||||
:rtype: bool
|
||||
%End
|
||||
|
||||
QString asPythonCode() const;
|
||||
%Docstring
|
||||
Attempts to convert the source to executable Python code.
|
||||
:rtype: str
|
||||
%End
|
||||
|
||||
};
|
||||
|
||||
class Component
|
||||
@ -549,6 +555,12 @@ Copies are protected to avoid slicing
|
||||
:rtype: bool
|
||||
%End
|
||||
|
||||
QString asPythonCode() const;
|
||||
%Docstring
|
||||
Attempts to convert the child to executable Python code.
|
||||
:rtype: str
|
||||
%End
|
||||
|
||||
};
|
||||
|
||||
QgsProcessingModelAlgorithm( const QString &name = QString(), const QString &group = QString() );
|
||||
@ -802,6 +814,12 @@ Copies are protected to avoid slicing
|
||||
.. seealso:: sourceFilePath()
|
||||
%End
|
||||
|
||||
QString asPythonCode() const;
|
||||
%Docstring
|
||||
Attempts to convert the model to executable Python code.
|
||||
:rtype: str
|
||||
%End
|
||||
|
||||
protected:
|
||||
|
||||
virtual QVariantMap processAlgorithm( const QVariantMap ¶meters,
|
||||
|
@ -66,47 +66,6 @@ from processing.gui.Help2Html import getHtmlFromDescriptionsDict
|
||||
pluginPath = os.path.split(os.path.dirname(__file__))[0]
|
||||
|
||||
|
||||
class Algorithm(QgsProcessingModelAlgorithm.ChildAlgorithm):
|
||||
|
||||
def __init__(self, consoleName=None):
|
||||
super().__init__(consoleName)
|
||||
|
||||
def todict(self):
|
||||
return {k: v for k, v in list(self.__dict__.items()) if not k.startswith("_")}
|
||||
|
||||
def getOutputType(self, outputName):
|
||||
output = self.algorithm().outputDefinition(outputName)
|
||||
return "output " + output.__class__.__name__.split(".")[-1][6:].lower()
|
||||
|
||||
def toPython(self):
|
||||
s = []
|
||||
params = []
|
||||
if not self.algorithm():
|
||||
return None
|
||||
|
||||
for param in self.algorithm().parameterDefinitions():
|
||||
value = self.parameterSources()[param.name()]
|
||||
|
||||
def _toString(v):
|
||||
if isinstance(v, (ValueFromInput, ValueFromOutput)):
|
||||
return v.asPythonParameter()
|
||||
elif isinstance(v, str):
|
||||
return "\\n".join(("'%s'" % v).splitlines())
|
||||
elif isinstance(v, list):
|
||||
return "[%s]" % ",".join([_toString(val) for val in v])
|
||||
else:
|
||||
return str(value)
|
||||
params.append(_toString(value))
|
||||
for out in self.algorithm().outputs:
|
||||
if not out.flags() & QgsProcessingParameterDefinition.FlagHidden:
|
||||
if out.name() in self.outputs:
|
||||
params.append(safeName(self.outputs[out.name()].description()).lower())
|
||||
else:
|
||||
params.append(str(None))
|
||||
s.append("outputs_%s=processing.run('%s', %s)" % (self.childId(), self.algorithmId(), ",".join(params)))
|
||||
return s
|
||||
|
||||
|
||||
class ValueFromInput(object):
|
||||
|
||||
def __init__(self, name=""):
|
||||
@ -124,9 +83,6 @@ class ValueFromInput(object):
|
||||
except:
|
||||
return False
|
||||
|
||||
def asPythonParameter(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class ValueFromOutput(object):
|
||||
|
||||
@ -146,9 +102,6 @@ class ValueFromOutput(object):
|
||||
def __str__(self):
|
||||
return self.alg + ":" + self.output
|
||||
|
||||
def asPythonParameter(self):
|
||||
return "outputs_%s['%s']" % (self.alg, self.output)
|
||||
|
||||
|
||||
class CompoundValue(object):
|
||||
|
||||
@ -223,33 +176,3 @@ class ModelerAlgorithm(QgsProcessingModelAlgorithm):
|
||||
else:
|
||||
v = value
|
||||
return param.evaluateForModeler(v, self)
|
||||
|
||||
def toPython(self):
|
||||
s = ['##%s=name' % self.name()]
|
||||
for param in list(self.parameterComponents().values()):
|
||||
s.append(param.param.asScriptCode())
|
||||
for alg in list(self.algs.values()):
|
||||
for name, out in list(alg.modelOutputs().items()):
|
||||
s.append('##%s=%s' % (safeName(out.description()).lower(), alg.getOutputType(name)))
|
||||
|
||||
executed = []
|
||||
toExecute = [alg for alg in list(self.algs.values()) if alg.isActive()]
|
||||
while len(executed) < len(toExecute):
|
||||
for alg in toExecute:
|
||||
if alg.childId() not in executed:
|
||||
canExecute = True
|
||||
required = self.dependsOnChildAlgorithms(alg.childId())
|
||||
for requiredAlg in required:
|
||||
if requiredAlg != alg.childId() and requiredAlg not in executed:
|
||||
canExecute = False
|
||||
break
|
||||
if canExecute:
|
||||
s.extend(alg.toPython())
|
||||
executed.append(alg.childId())
|
||||
|
||||
return '\n'.join(s)
|
||||
|
||||
|
||||
def safeName(name):
|
||||
validChars = 'abcdefghijklmnopqrstuvwxyz'
|
||||
return ''.join(c for c in name.lower() if c in validChars)
|
||||
|
@ -425,7 +425,7 @@ class ModelerDialog(BASE, WIDGET):
|
||||
if not filename.lower().endswith('.py'):
|
||||
filename += '.py'
|
||||
|
||||
text = self.model.toPython()
|
||||
text = self.model.asPythonCode()
|
||||
with codecs.open(filename, 'w', encoding='utf-8') as fout:
|
||||
fout.write(text)
|
||||
|
||||
|
@ -213,6 +213,33 @@ bool QgsProcessingModelAlgorithm::ChildAlgorithm::loadVariant( const QVariant &c
|
||||
return true;
|
||||
}
|
||||
|
||||
QString QgsProcessingModelAlgorithm::ChildAlgorithm::asPythonCode() const
|
||||
{
|
||||
QStringList lines;
|
||||
|
||||
if ( !algorithm() )
|
||||
return QString();
|
||||
|
||||
QStringList paramParts;
|
||||
QMap< QString, QgsProcessingModelAlgorithm::ChildParameterSource >::const_iterator paramIt = mParams.constBegin();
|
||||
for ( ; paramIt != mParams.constEnd(); ++paramIt )
|
||||
{
|
||||
QString part = paramIt->asPythonCode();
|
||||
if ( !part.isEmpty() )
|
||||
paramParts << QStringLiteral( "'%1':%2" ).arg( paramIt.key(), part );
|
||||
}
|
||||
|
||||
lines << QStringLiteral( "outputs['%1']=processing.run('%2', {%3}, context=context, feedback=feedback)" ).arg( mId, mAlgorithmId, paramParts.join( ',' ) );
|
||||
|
||||
QMap< QString, QgsProcessingModelAlgorithm::ModelOutput >::const_iterator outputIt = mModelOutputs.constBegin();
|
||||
for ( ; outputIt != mModelOutputs.constEnd(); ++outputIt )
|
||||
{
|
||||
lines << QStringLiteral( "results['%1']=outputs['%2']['%3']" ).arg( outputIt.key(), mId, outputIt.value().childOutputName() );
|
||||
}
|
||||
|
||||
return lines.join( '\n' );
|
||||
}
|
||||
|
||||
bool QgsProcessingModelAlgorithm::ChildAlgorithm::parametersCollapsed() const
|
||||
{
|
||||
return mParametersCollapsed;
|
||||
@ -491,6 +518,92 @@ void QgsProcessingModelAlgorithm::setSourceFilePath( const QString &sourceFile )
|
||||
mSourceFile = sourceFile;
|
||||
}
|
||||
|
||||
QString QgsProcessingModelAlgorithm::asPythonCode() const
|
||||
{
|
||||
QStringList lines;
|
||||
lines << QStringLiteral( "##%1=name" ).arg( name() );
|
||||
|
||||
QMap< QString, ModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
|
||||
for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
|
||||
{
|
||||
QString name = paramIt.value().parameterName();
|
||||
if ( parameterDefinition( name ) )
|
||||
{
|
||||
lines << parameterDefinition( name )->asScriptCode();
|
||||
}
|
||||
}
|
||||
|
||||
auto safeName = []( const QString & name )->QString
|
||||
{
|
||||
QString n = name.toLower().trimmed();
|
||||
QRegularExpression rx( "[^a-z_]" );
|
||||
n.replace( rx, QString() );
|
||||
return n;
|
||||
};
|
||||
|
||||
QMap< QString, ChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
|
||||
for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
|
||||
{
|
||||
if ( !childIt->isActive() || !childIt->algorithm() )
|
||||
continue;
|
||||
|
||||
// look through all outputs for child
|
||||
QMap<QString, QgsProcessingModelAlgorithm::ModelOutput> outputs = childIt->modelOutputs();
|
||||
QMap<QString, QgsProcessingModelAlgorithm::ModelOutput>::const_iterator outputIt = outputs.constBegin();
|
||||
for ( ; outputIt != outputs.constEnd(); ++outputIt )
|
||||
{
|
||||
const QgsProcessingOutputDefinition *output = childIt->algorithm()->outputDefinition( outputIt->childOutputName() );
|
||||
lines << QStringLiteral( "##%1=output %2" ).arg( safeName( outputIt->name() ), output->type() );
|
||||
}
|
||||
}
|
||||
|
||||
lines << QStringLiteral( "results={}" );
|
||||
|
||||
QSet< QString > toExecute;
|
||||
childIt = mChildAlgorithms.constBegin();
|
||||
for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
|
||||
{
|
||||
if ( childIt->isActive() && childIt->algorithm() )
|
||||
toExecute.insert( childIt->childId() );
|
||||
}
|
||||
|
||||
QSet< QString > executed;
|
||||
bool executedAlg = true;
|
||||
while ( executedAlg && executed.count() < toExecute.count() )
|
||||
{
|
||||
executedAlg = false;
|
||||
Q_FOREACH ( const QString &childId, toExecute )
|
||||
{
|
||||
if ( executed.contains( childId ) )
|
||||
continue;
|
||||
|
||||
bool canExecute = true;
|
||||
Q_FOREACH ( const QString &dependency, dependsOnChildAlgorithms( childId ) )
|
||||
{
|
||||
if ( !executed.contains( dependency ) )
|
||||
{
|
||||
canExecute = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !canExecute )
|
||||
continue;
|
||||
|
||||
executedAlg = true;
|
||||
|
||||
const ChildAlgorithm &child = mChildAlgorithms[ childId ];
|
||||
lines << child.asPythonCode();
|
||||
|
||||
executed.insert( childId );
|
||||
}
|
||||
}
|
||||
|
||||
lines << QStringLiteral( "return results" );
|
||||
|
||||
return lines.join( '\n' );
|
||||
}
|
||||
|
||||
QVariantMap QgsProcessingModelAlgorithm::helpContent() const
|
||||
{
|
||||
return mHelpContent;
|
||||
@ -1014,6 +1127,22 @@ bool QgsProcessingModelAlgorithm::ChildParameterSource::loadVariant( const QVari
|
||||
return true;
|
||||
}
|
||||
|
||||
QString QgsProcessingModelAlgorithm::ChildParameterSource::asPythonCode() const
|
||||
{
|
||||
switch ( mSource )
|
||||
{
|
||||
case ModelParameter:
|
||||
return QStringLiteral( "parameters['%1']" ).arg( mParameterName );
|
||||
|
||||
case ChildOutput:
|
||||
return QStringLiteral( "outputs['%1']['%2']" ).arg( mChildId, mOutputName );
|
||||
|
||||
case StaticValue:
|
||||
return mStaticValue.toString();
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QgsProcessingModelAlgorithm::ModelOutput::ModelOutput( const QString &name, const QString &description )
|
||||
: QgsProcessingModelAlgorithm::Component( description )
|
||||
, mName( name )
|
||||
|
@ -152,6 +152,11 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
|
||||
*/
|
||||
bool loadVariant( const QVariantMap &map );
|
||||
|
||||
/**
|
||||
* Attempts to convert the source to executable Python code.
|
||||
*/
|
||||
QString asPythonCode() const;
|
||||
|
||||
private:
|
||||
|
||||
Source mSource = StaticValue;
|
||||
@ -536,6 +541,11 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
|
||||
*/
|
||||
bool loadVariant( const QVariant &child );
|
||||
|
||||
/**
|
||||
* Attempts to convert the child to executable Python code.
|
||||
*/
|
||||
QString asPythonCode() const;
|
||||
|
||||
private:
|
||||
|
||||
QString mId;
|
||||
@ -793,6 +803,11 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
|
||||
*/
|
||||
void setSourceFilePath( const QString &path );
|
||||
|
||||
/**
|
||||
* Attempts to convert the model to executable Python code.
|
||||
*/
|
||||
QString asPythonCode() const;
|
||||
|
||||
protected:
|
||||
|
||||
QVariantMap processAlgorithm( const QVariantMap ¶meters,
|
||||
|
@ -4639,12 +4639,26 @@ void TestQgsProcessing::modelExecution()
|
||||
alg2c3.setAlgorithmId( "native:extractbyexpression" );
|
||||
alg2c3.addParameterSource( "INPUT", QgsProcessingModelAlgorithm::ChildParameterSource::fromChildOutput( "cx1", "OUTPUT_LAYER" ) );
|
||||
alg2c3.addParameterSource( "EXPRESSION", QgsProcessingModelAlgorithm::ChildParameterSource::fromStaticValue( "true" ) );
|
||||
alg2c3.setDependencies( QStringList() << "cx2" );
|
||||
model2.addChildAlgorithm( alg2c3 );
|
||||
params = model2.parametersForChildAlgorithm( model2.childAlgorithm( "cx3" ), modelInputs, childResults );
|
||||
QCOMPARE( params.value( "INPUT" ).toString(), QStringLiteral( "dest.shp" ) );
|
||||
QCOMPARE( params.value( "EXPRESSION" ).toString(), QStringLiteral( "true" ) );
|
||||
QCOMPARE( params.value( "OUTPUT" ).toString(), QStringLiteral( "memory:" ) );
|
||||
QCOMPARE( params.count(), 3 ); // don't want FAIL_OUTPUT set!
|
||||
|
||||
QStringList actualParts = model2.asPythonCode().split( '\n' );
|
||||
QStringList expectedParts = QStringLiteral( "##model=name\n"
|
||||
"##DIST=number\n"
|
||||
"##SOURCE_LAYER=source\n"
|
||||
"##model_out_layer=output outputVector\n"
|
||||
"results={}\n"
|
||||
"outputs['cx1']=processing.run('native:buffer', {'DISSOLVE':false,'DISTANCE':parameters['DIST'],'END_CAP_STYLE':1,'INPUT':parameters['SOURCE_LAYER'],'JOIN_STYLE':2,'SEGMENTS':16}, context=context, feedback=feedback)\n"
|
||||
"results['MODEL_OUT_LAYER']=outputs['cx1']['OUTPUT_LAYER']\n"
|
||||
"outputs['cx2']=processing.run('native:centroids', {'INPUT':outputs['cx1']['OUTPUT_LAYER']}, context=context, feedback=feedback)\n"
|
||||
"outputs['cx3']=processing.run('native:extractbyexpression', {'EXPRESSION':true,'INPUT':outputs['cx1']['OUTPUT_LAYER']}, context=context, feedback=feedback)\n"
|
||||
"return results" ).split( '\n' );
|
||||
QCOMPARE( actualParts, expectedParts );
|
||||
}
|
||||
|
||||
void TestQgsProcessing::tempUtils()
|
||||
|
Loading…
x
Reference in New Issue
Block a user