33 #include <QTextStream> 
   34 #include <QRegularExpression> 
   37 QgsProcessingModelAlgorithm::QgsProcessingModelAlgorithm( 
const QString &name, 
const QString &group, 
const QString &groupId )
 
   38   : mModelName( name.isEmpty() ? QObject::tr( 
"model" ) : name )
 
   39   , mModelGroup( group )
 
   40   , mModelGroupId( groupId )
 
   43 void QgsProcessingModelAlgorithm::initAlgorithm( 
const QVariantMap & )
 
   47 QString QgsProcessingModelAlgorithm::name()
 const 
   52 QString QgsProcessingModelAlgorithm::displayName()
 const 
   57 QString QgsProcessingModelAlgorithm::group()
 const 
   62 QString QgsProcessingModelAlgorithm::groupId()
 const 
   67 QIcon QgsProcessingModelAlgorithm::icon()
 const 
   72 QString QgsProcessingModelAlgorithm::svgIconPath()
 const 
   77 QString QgsProcessingModelAlgorithm::shortHelpString()
 const 
   79   if ( mHelpContent.empty() )
 
   85 QString QgsProcessingModelAlgorithm::shortDescription()
 const 
   87   return mHelpContent.value( QStringLiteral( 
"SHORT_DESCRIPTION" ) ).toString();
 
   90 QString QgsProcessingModelAlgorithm::helpUrl()
 const 
   92   return mHelpContent.value( QStringLiteral( 
"HELP_URL" ) ).toString();
 
   95 QVariantMap QgsProcessingModelAlgorithm::parametersForChildAlgorithm( 
const QgsProcessingModelChildAlgorithm &child, 
const QVariantMap &modelParameters, 
const QVariantMap &results, 
const QgsExpressionContext &expressionContext, QString &error )
 const 
  100     const QgsProcessingModelChildParameterSources paramSources = child.parameterSources().value( def->name() );
 
  102     QString expressionText;
 
  103     QVariantList paramParts;
 
  104     for ( 
const QgsProcessingModelChildParameterSource &source : paramSources )
 
  106       switch ( source.source() )
 
  108         case QgsProcessingModelChildParameterSource::StaticValue:
 
  109           paramParts << source.staticValue();
 
  112         case QgsProcessingModelChildParameterSource::ModelParameter:
 
  113           paramParts << modelParameters.value( source.parameterName() );
 
  116         case QgsProcessingModelChildParameterSource::ChildOutput:
 
  118           QVariantMap linkedChildResults = results.value( source.outputChildId() ).toMap();
 
  119           paramParts << linkedChildResults.value( source.outputName() );
 
  123         case QgsProcessingModelChildParameterSource::Expression:
 
  126           paramParts << exp.evaluate( &expressionContext );
 
  127           if ( exp.hasEvalError() )
 
  129             error = QObject::tr( 
"Could not evaluate expression for parameter %1 for %2: %3" ).arg( def->name(), child.description(), exp.evalErrorString() );
 
  133         case QgsProcessingModelChildParameterSource::ExpressionText:
 
  139         case QgsProcessingModelChildParameterSource::ModelOutput:
 
  144     if ( ! expressionText.isEmpty() )
 
  146       return expressionText;
 
  148     else if ( paramParts.count() == 1 )
 
  149       return paramParts.at( 0 );
 
  155   QVariantMap childParams;
 
  156   const QList< const QgsProcessingParameterDefinition * > childParameterDefinitions = child.algorithm()->parameterDefinitions();
 
  159     if ( !def->isDestination() )
 
  161       if ( !child.parameterSources().contains( def->name() ) )
 
  164       const QVariant value = evaluateSources( def );
 
  165       childParams.insert( def->name(), value );
 
  172       bool isFinalOutput = 
false;
 
  173       QMap<QString, QgsProcessingModelOutput> outputs = child.modelOutputs();
 
  174       QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
 
  175       for ( ; outputIt != outputs.constEnd(); ++outputIt )
 
  177         if ( outputIt->childOutputName() == destParam->
name() )
 
  179           QString paramName = child.childId() + 
':' + outputIt.key();
 
  180           bool foundParam = 
false;
 
  184           if ( modelParameters.contains( paramName ) )
 
  186             value = modelParameters.value( paramName );
 
  195               if ( modelParameters.contains( modelParam->name() ) )
 
  197                 value = modelParameters.value( modelParam->name() );
 
  210               value = QVariant::fromValue( fromVar );
 
  213             childParams.insert( destParam->
name(), value );
 
  215           isFinalOutput = 
true;
 
  220       bool hasExplicitDefinition = 
false;
 
  221       if ( !isFinalOutput && child.parameterSources().contains( def->name() ) )
 
  224         const QVariant value = evaluateSources( def );
 
  225         if ( value.isValid() )
 
  227           childParams.insert( def->name(), value );
 
  228           hasExplicitDefinition = 
true;
 
  232       if ( !isFinalOutput && !hasExplicitDefinition )
 
  237         bool required = 
true;
 
  240           required = childOutputIsRequired( child.childId(), destParam->
name() );
 
  252 const QgsProcessingParameterDefinition *QgsProcessingModelAlgorithm::modelParameterFromChildIdAndOutputName( 
const QString &childId, 
const QString &childOutputName )
 const 
  256     if ( !definition->isDestination() )
 
  259     const QString modelChildId = definition->
metadata().value( QStringLiteral( 
"_modelChildId" ) ).toString();
 
  260     const QString modelOutputName = definition->metadata().value( QStringLiteral( 
"_modelChildOutputName" ) ).toString();
 
  262     if ( modelChildId == childId && modelOutputName == childOutputName )
 
  268 bool QgsProcessingModelAlgorithm::childOutputIsRequired( 
const QString &childId, 
const QString &outputName )
 const 
  271   QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
 
  272   for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
 
  274     if ( childIt->childId() == childId || !childIt->isActive() )
 
  278     QMap<QString, QgsProcessingModelChildParameterSources> candidateChildParams = childIt->parameterSources();
 
  279     QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator childParamIt = candidateChildParams.constBegin();
 
  280     for ( ; childParamIt != candidateChildParams.constEnd(); ++childParamIt )
 
  282       const auto constValue = childParamIt.value();
 
  283       for ( 
const QgsProcessingModelChildParameterSource &source : constValue )
 
  285         if ( source.source() == QgsProcessingModelChildParameterSource::ChildOutput
 
  286              && source.outputChildId() == childId
 
  287              && source.outputName() == outputName )
 
  299   QSet< QString > toExecute;
 
  300   QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
 
  301   QSet< QString > broken;
 
  302   for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
 
  304     if ( childIt->isActive() )
 
  306       if ( childIt->algorithm() )
 
  307         toExecute.insert( childIt->childId() );
 
  309         broken.insert( childIt->childId() );
 
  313   if ( !broken.empty() )
 
  314     throw QgsProcessingException( QCoreApplication::translate( 
"QgsProcessingModelAlgorithm", 
"Cannot run model, the following algorithms are not available on this system: %1" ).arg( broken.values().join( QLatin1String( 
", " ) ) ) );
 
  316   QElapsedTimer totalTime;
 
  322   QVariantMap childResults;
 
  323   QVariantMap childInputs;
 
  327   QVariantMap finalResults;
 
  328   QSet< QString > executed;
 
  329   bool executedAlg = 
true;
 
  330   while ( executedAlg && executed.count() < toExecute.count() )
 
  333     for ( 
const QString &childId : std::as_const( toExecute ) )
 
  338       if ( executed.contains( childId ) )
 
  341       bool canExecute = 
true;
 
  342       const QSet< QString > dependencies = dependsOnChildAlgorithms( childId );
 
  343       for ( 
const QString &dependency : dependencies )
 
  345         if ( !executed.contains( dependency ) )
 
  357       const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms[ childId ];
 
  358       std::unique_ptr< QgsProcessingAlgorithm > childAlg( child.algorithm()->create( child.configuration() ) );
 
  361       if ( feedback && !skipGenericLogging )
 
  362         feedback->
pushDebugInfo( QObject::tr( 
"Prepare algorithm: %1" ).arg( childId ) );
 
  366                  << createExpressionContextScopeForChildAlgorithm( childId, context, parameters, childResults );
 
  370       QVariantMap childParams = parametersForChildAlgorithm( child, parameters, childResults, expContext, error );
 
  371       if ( !error.isEmpty() )
 
  374       if ( feedback && !skipGenericLogging )
 
  375         feedback->
setProgressText( QObject::tr( 
"Running %1 [%2/%3]" ).arg( child.description() ).arg( executed.count() + 1 ).arg( toExecute.count() ) );
 
  379       for ( 
auto childParamIt = childParams.constBegin(); childParamIt != childParams.constEnd(); ++childParamIt )
 
  381         params << QStringLiteral( 
"%1: %2" ).arg( childParamIt.key(),
 
  382                child.algorithm()->parameterDefinition( childParamIt.key() )->valueAsPythonString( childParamIt.value(), context ) );
 
  385       if ( feedback && !skipGenericLogging )
 
  387         feedback->
pushInfo( QObject::tr( 
"Input Parameters:" ) );
 
  388         feedback->
pushCommandInfo( QStringLiteral( 
"{ %1 }" ).arg( params.join( QLatin1String( 
", " ) ) ) );
 
  391       QElapsedTimer childTime;
 
  396       QThread *modelThread = QThread::currentThread();
 
  398       auto prepareOnMainThread = [modelThread, &ok, &childAlg, &childParams, &context, &modelFeedback]
 
  400         Q_ASSERT_X( QThread::currentThread() == qApp->thread(), 
"QgsProcessingModelAlgorithm::processAlgorithm", 
"childAlg->prepare() must be run on the main thread" );
 
  401         ok = childAlg->prepare( childParams, context, &modelFeedback );
 
  402         context.pushToThread( modelThread );
 
  406       if ( modelThread == qApp->thread() )
 
  407         ok = childAlg->prepare( childParams, context, &modelFeedback );
 
  410         context.pushToThread( qApp->thread() );
 
  411         QMetaObject::invokeMethod( qApp, prepareOnMainThread, Qt::BlockingQueuedConnection );
 
  414       Q_ASSERT_X( QThread::currentThread() == context.thread(), 
"QgsProcessingModelAlgorithm::processAlgorithm", 
"context was not transferred back to model thread" );
 
  428           auto runOnMainThread = [modelThread, &context, &modelFeedback, &results, &childAlg, &childParams]
 
  430             Q_ASSERT_X( QThread::currentThread() == qApp->thread(), 
"QgsProcessingModelAlgorithm::processAlgorithm", 
"childAlg->runPrepared() must be run on the main thread" );
 
  431             results = childAlg->runPrepared( childParams, context, &modelFeedback );
 
  432             context.pushToThread( modelThread );
 
  435           if ( feedback && !skipGenericLogging && modelThread != qApp->thread() )
 
  436             feedback->
pushWarning( QObject::tr( 
"Algorithm “%1” cannot be run in a background thread, switching to main thread for this step" ).arg( childAlg->displayName() ) );
 
  438           context.pushToThread( qApp->thread() );
 
  439           QMetaObject::invokeMethod( qApp, runOnMainThread, Qt::BlockingQueuedConnection );
 
  444           results = childAlg->runPrepared( childParams, context, &modelFeedback );
 
  453       Q_ASSERT_X( QThread::currentThread() == context.thread(), 
"QgsProcessingModelAlgorithm::processAlgorithm", 
"context was not transferred back to model thread" );
 
  456       auto postProcessOnMainThread = [modelThread, &ppRes, &childAlg, &context, &modelFeedback]
 
  458         Q_ASSERT_X( QThread::currentThread() == qApp->thread(), 
"QgsProcessingModelAlgorithm::processAlgorithm", 
"childAlg->postProcess() must be run on the main thread" );
 
  459         ppRes = childAlg->postProcess( context, &modelFeedback );
 
  460         context.pushToThread( modelThread );
 
  464       if ( modelThread == qApp->thread() )
 
  465         ppRes = childAlg->postProcess( context, &modelFeedback );
 
  468         context.pushToThread( qApp->thread() );
 
  469         QMetaObject::invokeMethod( qApp, postProcessOnMainThread, Qt::BlockingQueuedConnection );
 
  472       Q_ASSERT_X( QThread::currentThread() == context.thread(), 
"QgsProcessingModelAlgorithm::processAlgorithm", 
"context was not transferred back to model thread" );
 
  474       if ( !ppRes.isEmpty() )
 
  477       childResults.insert( childId, results );
 
  481       QMap<QString, QgsProcessingModelOutput> outputs = child.modelOutputs();
 
  482       QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
 
  483       for ( ; outputIt != outputs.constEnd(); ++outputIt )
 
  485         switch ( mInternalVersion )
 
  487           case QgsProcessingModelAlgorithm::InternalVersion::Version1:
 
  488             finalResults.insert( childId + 
':' + outputIt->name(), results.value( outputIt->childOutputName() ) );
 
  490           case QgsProcessingModelAlgorithm::InternalVersion::Version2:
 
  493               finalResults.insert( modelParam->name(), results.value( outputIt->childOutputName() ) );
 
  499       executed.insert( childId );
 
  501       std::function< void( 
const QString &, 
const QString & )> pruneAlgorithmBranchRecursive;
 
  502       pruneAlgorithmBranchRecursive = [&]( 
const QString & id, 
const QString &branch = QString() )
 
  504         const QSet<QString> toPrune = dependentChildAlgorithms( 
id, branch );
 
  505         for ( 
const QString &targetId : toPrune )
 
  507           if ( executed.contains( targetId ) )
 
  510           executed.insert( targetId );
 
  511           pruneAlgorithmBranchRecursive( targetId, branch );
 
  521           pruneAlgorithmBranchRecursive( childId, outputDef->name() );
 
  529         for ( 
const QString &candidateId : std::as_const( toExecute ) )
 
  531           if ( executed.contains( candidateId ) )
 
  536           const QgsProcessingModelChildAlgorithm &candidate = mChildAlgorithms[ candidateId ];
 
  537           const QMap<QString, QgsProcessingModelChildParameterSources> candidateParams = candidate.parameterSources();
 
  538           QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = candidateParams.constBegin();
 
  540           for ( ; paramIt != candidateParams.constEnd(); ++paramIt )
 
  542             for ( 
const QgsProcessingModelChildParameterSource &source : paramIt.value() )
 
  544               if ( source.source() == QgsProcessingModelChildParameterSource::ChildOutput && source.outputChildId() == childId )
 
  547                 if ( !results.contains( source.outputName() ) )
 
  552                   executed.insert( candidateId );
 
  554                   pruneAlgorithmBranchRecursive( candidateId, QString() );
 
  565       childAlg.reset( 
nullptr );
 
  566       modelFeedback.setCurrentStep( executed.count() );
 
  567       if ( feedback && !skipGenericLogging )
 
  568         feedback->
pushInfo( QObject::tr( 
"OK. Execution took %1 s (%n output(s)).", 
nullptr, results.count() ).arg( childTime.elapsed() / 1000.0 ) );
 
  575     feedback->
pushDebugInfo( QObject::tr( 
"Model processed OK. Executed %n algorithm(s) total in %1 s.", 
nullptr, executed.count() ).arg( totalTime.elapsed() / 1000.0 ) );
 
  577   mResults = finalResults;
 
  578   mResults.insert( QStringLiteral( 
"CHILD_RESULTS" ), childResults );
 
  579   mResults.insert( QStringLiteral( 
"CHILD_INPUTS" ), childInputs );
 
  583 QString QgsProcessingModelAlgorithm::sourceFilePath()
 const 
  588 void QgsProcessingModelAlgorithm::setSourceFilePath( 
const QString &sourceFile )
 
  590   mSourceFile = sourceFile;
 
  593 bool QgsProcessingModelAlgorithm::modelNameMatchesFilePath()
 const 
  595   if ( mSourceFile.isEmpty() )
 
  598   const QFileInfo fi( mSourceFile );
 
  599   return fi.completeBaseName().compare( mModelName, Qt::CaseInsensitive ) == 0;
 
  604   QStringList fileDocString;
 
  605   fileDocString << QStringLiteral( 
"\"\"\"" );
 
  606   fileDocString << QStringLiteral( 
"Model exported as python." );
 
  607   fileDocString << QStringLiteral( 
"Name : %1" ).arg( displayName() );
 
  608   fileDocString << QStringLiteral( 
"Group : %1" ).arg( group() );
 
  609   fileDocString << QStringLiteral( 
"With QGIS : %1" ).arg( 
Qgis::versionInt() );
 
  610   fileDocString << QStringLiteral( 
"\"\"\"" );
 
  611   fileDocString << QString();
 
  614   QString indent = QString( 
' ' ).repeated( indentSize );
 
  615   QString currentIndent;
 
  617   QMap< QString, QString> friendlyChildNames;
 
  618   QMap< QString, QString> friendlyOutputNames;
 
  619   auto uniqueSafeName = []( 
const QString & name, 
bool capitalize, 
const QMap< QString, QString > &friendlyNames )->QString
 
  621     const QString base = safeName( name, capitalize );
 
  622     QString candidate = base;
 
  624     while ( friendlyNames.contains( candidate ) )
 
  627       candidate = QStringLiteral( 
"%1_%2" ).arg( base ).arg( i );
 
  632   const QString algorithmClassName = safeName( name(), 
true );
 
  634   QSet< QString > toExecute;
 
  635   for ( 
auto childIt = mChildAlgorithms.constBegin(); childIt != mChildAlgorithms.constEnd(); ++childIt )
 
  637     if ( childIt->isActive() && childIt->algorithm() )
 
  639       toExecute.insert( childIt->childId() );
 
  640       friendlyChildNames.insert( childIt->childId(), uniqueSafeName( childIt->description().isEmpty() ? childIt->childId() : childIt->description(), !childIt->description().isEmpty(), friendlyChildNames ) );
 
  643   const int totalSteps = toExecute.count();
 
  645   QStringList importLines; 
 
  646   switch ( outputType )
 
  651       const auto params = parameterDefinitions();
 
  652       importLines.reserve( params.count() + 3 );
 
  653       importLines << QStringLiteral( 
"from qgis.core import QgsProcessing" );
 
  654       importLines << QStringLiteral( 
"from qgis.core import QgsProcessingAlgorithm" );
 
  655       importLines << QStringLiteral( 
"from qgis.core import QgsProcessingMultiStepFeedback" );
 
  657       bool hasAdvancedParams = 
false;
 
  661           hasAdvancedParams = 
true;
 
  664         if ( !importString.isEmpty() && !importLines.contains( importString ) )
 
  665           importLines << importString;
 
  668       if ( hasAdvancedParams )
 
  669         importLines << QStringLiteral( 
"from qgis.core import QgsProcessingParameterDefinition" );
 
  671       lines << QStringLiteral( 
"import processing" );
 
  672       lines << QString() << QString();
 
  674       lines << QStringLiteral( 
"class %1(QgsProcessingAlgorithm):" ).arg( algorithmClassName );
 
  678       lines << indent + QStringLiteral( 
"def initAlgorithm(self, config=None):" );
 
  679       if ( params.empty() )
 
  681         lines << indent + indent + QStringLiteral( 
"pass" );
 
  685         lines.reserve( lines.size() + params.size() );
 
  688           std::unique_ptr< QgsProcessingParameterDefinition > defClone( def->clone() );
 
  690           if ( defClone->isDestination() )
 
  692             const QString uniqueChildName = defClone->metadata().value( QStringLiteral( 
"_modelChildId" ) ).toString() + 
':' + defClone->metadata().value( QStringLiteral( 
"_modelChildOutputName" ) ).toString();
 
  693             const QString friendlyName = !defClone->description().isEmpty() ? uniqueSafeName( defClone->description(), 
true, friendlyOutputNames ) : defClone->name();
 
  694             friendlyOutputNames.insert( uniqueChildName, friendlyName );
 
  695             defClone->setName( friendlyName );
 
  699             if ( !mParameterComponents.value( defClone->name() ).comment()->description().isEmpty() )
 
  700               lines << indent + indent + QStringLiteral( 
"# %1" ).arg( mParameterComponents.value( defClone->name() ).comment()->description() );
 
  705             lines << indent + indent + QStringLiteral( 
"param = %1" ).arg( defClone->asPythonString() );
 
  706             lines << indent + indent + QStringLiteral( 
"param.setFlags(param.flags() | QgsProcessingParameterDefinition.FlagAdvanced)" );
 
  707             lines << indent + indent + QStringLiteral( 
"self.addParameter(param)" );
 
  711             lines << indent + indent + QStringLiteral( 
"self.addParameter(%1)" ).arg( defClone->asPythonString() );
 
  717       lines << indent + QStringLiteral( 
"def processAlgorithm(self, parameters, context, model_feedback):" );
 
  718       currentIndent = indent + indent;
 
  720       lines << currentIndent + QStringLiteral( 
"# Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the" );
 
  721       lines << currentIndent + QStringLiteral( 
"# overall progress through the model" );
 
  722       lines << currentIndent + QStringLiteral( 
"feedback = QgsProcessingMultiStepFeedback(%1, model_feedback)" ).arg( totalSteps );
 
  730       QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
 
  731       for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
 
  733         QString name = paramIt.value().parameterName();
 
  734         if ( parameterDefinition( name ) )
 
  737           params.insert( name, parameterDefinition( name )->valueAsPythonString( parameterDefinition( name )->defaultValue(), context ) );
 
  741       if ( !params.isEmpty() )
 
  743         lines << QStringLiteral( 
"parameters = {" );
 
  744         for ( 
auto it = params.constBegin(); it != params.constEnd(); ++it )
 
  746           lines << QStringLiteral( 
"  '%1':%2," ).arg( it.key(), it.value() );
 
  748         lines << QStringLiteral( 
"}" )
 
  752       lines << QStringLiteral( 
"context = QgsProcessingContext()" )
 
  753             << QStringLiteral( 
"context.setProject(QgsProject.instance())" )
 
  754             << QStringLiteral( 
"feedback = QgsProcessingFeedback()" )
 
  763   lines << currentIndent + QStringLiteral( 
"results = {}" );
 
  764   lines << currentIndent + QStringLiteral( 
"outputs = {}" );
 
  767   QSet< QString > executed;
 
  768   bool executedAlg = 
true;
 
  770   while ( executedAlg && executed.count() < toExecute.count() )
 
  773     const auto constToExecute = toExecute;
 
  774     for ( 
const QString &childId : constToExecute )
 
  776       if ( executed.contains( childId ) )
 
  779       bool canExecute = 
true;
 
  780       const auto constDependsOnChildAlgorithms = dependsOnChildAlgorithms( childId );
 
  781       for ( 
const QString &dependency : constDependsOnChildAlgorithms )
 
  783         if ( !executed.contains( dependency ) )
 
  795       const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms[ childId ];
 
  802         if ( def->isDestination() )
 
  807           bool isFinalOutput = 
false;
 
  808           QMap<QString, QgsProcessingModelOutput> outputs = child.modelOutputs();
 
  809           QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
 
  810           for ( ; outputIt != outputs.constEnd(); ++outputIt )
 
  812             if ( outputIt->childOutputName() == destParam->
name() )
 
  814               QString paramName = child.childId() + 
':' + outputIt.key();
 
  815               paramName = friendlyOutputNames.value( paramName, paramName );
 
  816               childParams.insert( destParam->
name(), QStringLiteral( 
"parameters['%1']" ).arg( paramName ) );
 
  817               isFinalOutput = 
true;
 
  822           if ( !isFinalOutput )
 
  827             bool required = 
true;
 
  830               required = childOutputIsRequired( child.childId(), destParam->
name() );
 
  837               childParams.insert( destParam->
name(), QStringLiteral( 
"QgsProcessing.TEMPORARY_OUTPUT" ) );
 
  843       lines << child.asPythonCode( outputType, childParams, currentIndent.size(), indentSize, friendlyChildNames, friendlyOutputNames );
 
  845       if ( currentStep < totalSteps )
 
  848         lines << currentIndent + QStringLiteral( 
"feedback.setCurrentStep(%1)" ).arg( currentStep );
 
  849         lines << currentIndent + QStringLiteral( 
"if feedback.isCanceled():" );
 
  850         lines << currentIndent + indent + QStringLiteral( 
"return {}" );
 
  853       executed.insert( childId );
 
  857   switch ( outputType )
 
  860       lines << currentIndent + QStringLiteral( 
"return results" );
 
  864       lines << indent + QStringLiteral( 
"def name(self):" );
 
  865       lines << indent + indent + QStringLiteral( 
"return '%1'" ).arg( mModelName );
 
  867       lines << indent + QStringLiteral( 
"def displayName(self):" );
 
  868       lines << indent + indent + QStringLiteral( 
"return '%1'" ).arg( mModelName );
 
  872       lines << indent + QStringLiteral( 
"def group(self):" );
 
  873       lines << indent + indent + QStringLiteral( 
"return '%1'" ).arg( mModelGroup );
 
  875       lines << indent + QStringLiteral( 
"def groupId(self):" );
 
  876       lines << indent + indent + QStringLiteral( 
"return '%1'" ).arg( mModelGroupId );
 
  880       if ( !shortHelpString().isEmpty() )
 
  882         lines << indent + QStringLiteral( 
"def shortHelpString(self):" );
 
  883         lines << indent + indent + QStringLiteral( 
"return \"\"\"%1\"\"\"" ).arg( shortHelpString() );
 
  886       if ( !helpUrl().isEmpty() )
 
  888         lines << indent + QStringLiteral( 
"def helpUrl(self):" );
 
  889         lines << indent + indent + QStringLiteral( 
"return '%1'" ).arg( helpUrl() );
 
  894       lines << indent + QStringLiteral( 
"def createInstance(self):" );
 
  895       lines << indent + indent + QStringLiteral( 
"return %1()" ).arg( algorithmClassName );
 
  898       static QMap< QString, QString > sAdditionalImports
 
  900         { QStringLiteral( 
"QgsCoordinateReferenceSystem" ), QStringLiteral( 
"from qgis.core import QgsCoordinateReferenceSystem" ) },
 
  901         { QStringLiteral( 
"QgsExpression" ), QStringLiteral( 
"from qgis.core import QgsExpression" ) },
 
  902         { QStringLiteral( 
"QgsRectangle" ), QStringLiteral( 
"from qgis.core import QgsRectangle" ) },
 
  903         { QStringLiteral( 
"QgsReferencedRectangle" ), QStringLiteral( 
"from qgis.core import QgsReferencedRectangle" ) },
 
  904         { QStringLiteral( 
"QgsPoint" ), QStringLiteral( 
"from qgis.core import QgsPoint" ) },
 
  905         { QStringLiteral( 
"QgsReferencedPoint" ), QStringLiteral( 
"from qgis.core import QgsReferencedPoint" ) },
 
  906         { QStringLiteral( 
"QgsProperty" ), QStringLiteral( 
"from qgis.core import QgsProperty" ) },
 
  907         { QStringLiteral( 
"QgsRasterLayer" ), QStringLiteral( 
"from qgis.core import QgsRasterLayer" ) },
 
  908         { QStringLiteral( 
"QgsMeshLayer" ), QStringLiteral( 
"from qgis.core import QgsMeshLayer" ) },
 
  909         { QStringLiteral( 
"QgsVectorLayer" ), QStringLiteral( 
"from qgis.core import QgsVectorLayer" ) },
 
  910         { QStringLiteral( 
"QgsMapLayer" ), QStringLiteral( 
"from qgis.core import QgsMapLayer" ) },
 
  911         { QStringLiteral( 
"QgsProcessingFeatureSourceDefinition" ), QStringLiteral( 
"from qgis.core import QgsProcessingFeatureSourceDefinition" ) },
 
  912         { QStringLiteral( 
"QgsPointXY" ), QStringLiteral( 
"from qgis.core import QgsPointXY" ) },
 
  913         { QStringLiteral( 
"QgsReferencedPointXY" ), QStringLiteral( 
"from qgis.core import QgsReferencedPointXY" ) },
 
  914         { QStringLiteral( 
"QgsGeometry" ), QStringLiteral( 
"from qgis.core import QgsGeometry" ) },
 
  915         { QStringLiteral( 
"QgsProcessingOutputLayerDefinition" ), QStringLiteral( 
"from qgis.core import QgsProcessingOutputLayerDefinition" ) },
 
  916         { QStringLiteral( 
"QColor" ), QStringLiteral( 
"from qgis.PyQt.QtGui import QColor" ) },
 
  917         { QStringLiteral( 
"QDateTime" ), QStringLiteral( 
"from qgis.PyQt.QtCore import QDateTime" ) },
 
  918         { QStringLiteral( 
"QDate" ), QStringLiteral( 
"from qgis.PyQt.QtCore import QDate" ) },
 
  919         { QStringLiteral( 
"QTime" ), QStringLiteral( 
"from qgis.PyQt.QtCore import QTime" ) },
 
  922       for ( 
auto it = sAdditionalImports.constBegin(); it != sAdditionalImports.constEnd(); ++it )
 
  924         if ( importLines.contains( it.value() ) )
 
  931         for ( 
const QString &line : std::as_const( lines ) )
 
  933           if ( line.contains( it.key() ) )
 
  941           importLines << it.value();
 
  945       lines = fileDocString + importLines + lines;
 
  954 QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> QgsProcessingModelAlgorithm::variablesForChildAlgorithm( 
const QString &childId, 
QgsProcessingContext &context, 
const QVariantMap &modelParameters, 
const QVariantMap &results )
 const 
  956   QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> variables;
 
  958   auto safeName = []( 
const QString & name )->QString
 
  961     return s.replace( QRegularExpression( QStringLiteral( 
"[\\s'\"\\(\\):\\.]" ) ), QStringLiteral( 
"_" ) );
 
  995   for ( 
const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
 
 1000     switch ( source.source() )
 
 1002       case QgsProcessingModelChildParameterSource::ModelParameter:
 
 1004         name = source.parameterName();
 
 1005         value = modelParameters.value( source.parameterName() );
 
 1006         description = parameterDefinition( source.parameterName() )->description();
 
 1009       case QgsProcessingModelChildParameterSource::ChildOutput:
 
 1011         const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
 
 1012         name = QStringLiteral( 
"%1_%2" ).arg( child.description().isEmpty() ?
 
 1013                                               source.outputChildId() : child.description(), source.outputName() );
 
 1016           description = QObject::tr( 
"Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(),
 
 1017                         child.description() );
 
 1019         value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
 
 1023       case QgsProcessingModelChildParameterSource::Expression:
 
 1024       case QgsProcessingModelChildParameterSource::ExpressionText:
 
 1025       case QgsProcessingModelChildParameterSource::StaticValue:
 
 1026       case QgsProcessingModelChildParameterSource::ModelOutput:
 
 1029     variables.insert( safeName( name ), VariableDefinition( value, source, description ) );
 
 1033   sources = availableSourcesForChild( childId, QStringList()
 
 1040   for ( 
const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
 
 1044     QString description;
 
 1046     switch ( source.source() )
 
 1048       case QgsProcessingModelChildParameterSource::ModelParameter:
 
 1050         name = source.parameterName();
 
 1051         value = modelParameters.value( source.parameterName() );
 
 1052         description = parameterDefinition( source.parameterName() )->description();
 
 1055       case QgsProcessingModelChildParameterSource::ChildOutput:
 
 1057         const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
 
 1058         name = QStringLiteral( 
"%1_%2" ).arg( child.description().isEmpty() ?
 
 1059                                               source.outputChildId() : child.description(), source.outputName() );
 
 1060         value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
 
 1063           description = QObject::tr( 
"Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(),
 
 1064                         child.description() );
 
 1069       case QgsProcessingModelChildParameterSource::Expression:
 
 1070       case QgsProcessingModelChildParameterSource::ExpressionText:
 
 1071       case QgsProcessingModelChildParameterSource::StaticValue:
 
 1072       case QgsProcessingModelChildParameterSource::ModelOutput:
 
 1080       value = fromVar.
sink;
 
 1086     QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( qvariant_cast<QObject *>( value ) );
 
 1090     variables.insert( safeName( name ), VariableDefinition( QVariant::fromValue( 
QgsWeakMapLayerPointer( layer ) ), source, description ) );
 
 1091     variables.insert( safeName( QStringLiteral( 
"%1_minx" ).arg( name ) ), VariableDefinition( layer ? layer->
extent().
xMinimum() : QVariant(), source, QObject::tr( 
"Minimum X of %1" ).arg( description ) ) );
 
 1092     variables.insert( safeName( QStringLiteral( 
"%1_miny" ).arg( name ) ), VariableDefinition( layer ? layer->
extent().
yMinimum() : QVariant(), source, QObject::tr( 
"Minimum Y of %1" ).arg( description ) ) );
 
 1093     variables.insert( safeName( QStringLiteral( 
"%1_maxx" ).arg( name ) ), VariableDefinition( layer ? layer->
extent().
xMaximum() : QVariant(), source, QObject::tr( 
"Maximum X of %1" ).arg( description ) ) );
 
 1094     variables.insert( safeName( QStringLiteral( 
"%1_maxy" ).arg( name ) ), VariableDefinition( layer ? layer->
extent().
yMaximum() : QVariant(), source, QObject::tr( 
"Maximum Y of %1" ).arg( description ) ) );
 
 1097   sources = availableSourcesForChild( childId, QStringList()
 
 1099   for ( 
const QgsProcessingModelChildParameterSource &source : std::as_const( sources ) )
 
 1103     QString description;
 
 1105     switch ( source.source() )
 
 1107       case QgsProcessingModelChildParameterSource::ModelParameter:
 
 1109         name = source.parameterName();
 
 1110         value = modelParameters.value( source.parameterName() );
 
 1111         description = parameterDefinition( source.parameterName() )->description();
 
 1114       case QgsProcessingModelChildParameterSource::ChildOutput:
 
 1116         const QgsProcessingModelChildAlgorithm &child = mChildAlgorithms.value( source.outputChildId() );
 
 1117         name = QStringLiteral( 
"%1_%2" ).arg( child.description().isEmpty() ?
 
 1118                                               source.outputChildId() : child.description(), source.outputName() );
 
 1119         value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
 
 1122           description = QObject::tr( 
"Output '%1' from algorithm '%2'" ).arg( alg->outputDefinition( source.outputName() )->description(),
 
 1123                         child.description() );
 
 1128       case QgsProcessingModelChildParameterSource::Expression:
 
 1129       case QgsProcessingModelChildParameterSource::ExpressionText:
 
 1130       case QgsProcessingModelChildParameterSource::StaticValue:
 
 1131       case QgsProcessingModelChildParameterSource::ModelOutput:
 
 1145       value = fromVar.
sink;
 
 1151     if ( 
QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( qvariant_cast<QObject *>( value ) ) )
 
 1153       featureSource = layer;
 
 1155     if ( !featureSource )
 
 1161     variables.insert( safeName( name ), VariableDefinition( value, source, description ) );
 
 1162     variables.insert( safeName( QStringLiteral( 
"%1_minx" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->
sourceExtent().
xMinimum() : QVariant(), source, QObject::tr( 
"Minimum X of %1" ).arg( description ) ) );
 
 1163     variables.insert( safeName( QStringLiteral( 
"%1_miny" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->
sourceExtent().
yMinimum() : QVariant(), source, QObject::tr( 
"Minimum Y of %1" ).arg( description ) ) );
 
 1164     variables.insert( safeName( QStringLiteral( 
"%1_maxx" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->
sourceExtent().
xMaximum() : QVariant(), source, QObject::tr( 
"Maximum X of %1" ).arg( description ) ) );
 
 1165     variables.insert( safeName( QStringLiteral( 
"%1_maxy" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->
sourceExtent().
yMaximum() : QVariant(), source, QObject::tr( 
"Maximum Y of %1" ).arg( description ) ) );
 
 1171 QgsExpressionContextScope *QgsProcessingModelAlgorithm::createExpressionContextScopeForChildAlgorithm( 
const QString &childId, 
QgsProcessingContext &context, 
const QVariantMap &modelParameters, 
const QVariantMap &results )
 const 
 1173   std::unique_ptr< QgsExpressionContextScope > scope( 
new QgsExpressionContextScope( QStringLiteral( 
"algorithm_inputs" ) ) );
 
 1174   QMap< QString, QgsProcessingModelAlgorithm::VariableDefinition> variables = variablesForChildAlgorithm( childId, context, modelParameters, results );
 
 1175   QMap< QString, QgsProcessingModelAlgorithm::VariableDefinition>::const_iterator varIt = variables.constBegin();
 
 1176   for ( ; varIt != variables.constEnd(); ++varIt )
 
 1180   return scope.release();
 
 1183 QgsProcessingModelChildParameterSources QgsProcessingModelAlgorithm::availableSourcesForChild( 
const QString &childId, 
const QStringList ¶meterTypes, 
const QStringList &outputTypes, 
const QList<int> &dataTypes )
 const 
 1185   QgsProcessingModelChildParameterSources sources;
 
 1188   QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
 
 1189   for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
 
 1195     if ( parameterTypes.contains( def->
type() ) )
 
 1197       if ( !dataTypes.isEmpty() )
 
 1213           bool ok = sourceDef->
dataTypes().isEmpty();
 
 1214           const auto constDataTypes = sourceDef->
dataTypes();
 
 1215           for ( 
int type : constDataTypes )
 
 1230       sources << QgsProcessingModelChildParameterSource::fromModelParameter( paramIt->parameterName() );
 
 1234   QSet< QString > dependents;
 
 1235   if ( !childId.isEmpty() )
 
 1237     dependents = dependentChildAlgorithms( childId );
 
 1238     dependents << childId;
 
 1241   QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
 
 1242   for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
 
 1244     if ( dependents.contains( childIt->childId() ) )
 
 1254       if ( outputTypes.contains( out->type() ) )
 
 1256         if ( !dataTypes.isEmpty() )
 
 1262             if ( !vectorOutputIsCompatibleType( dataTypes, vectorOut->
dataType() ) )
 
 1269         sources << QgsProcessingModelChildParameterSource::fromChildOutput( childIt->childId(), out->name() );
 
 1277 QVariantMap QgsProcessingModelAlgorithm::helpContent()
 const 
 1279   return mHelpContent;
 
 1282 void QgsProcessingModelAlgorithm::setHelpContent( 
const QVariantMap &helpContent )
 
 1284   mHelpContent = helpContent;
 
 1287 void QgsProcessingModelAlgorithm::setName( 
const QString &name )
 
 1292 void QgsProcessingModelAlgorithm::setGroup( 
const QString &group )
 
 1294   mModelGroup = group;
 
 1297 bool QgsProcessingModelAlgorithm::validate( QStringList &issues )
 const 
 1302   if ( mChildAlgorithms.empty() )
 
 1305     issues << QObject::tr( 
"Model does not contain any algorithms" );
 
 1308   for ( 
auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
 
 1310     QStringList childIssues;
 
 1311     res = validateChildAlgorithm( it->childId(), childIssues ) && res;
 
 1313     for ( 
const QString &issue : std::as_const( childIssues ) )
 
 1315       issues << QStringLiteral( 
"<b>%1</b>: %2" ).arg( it->description(), issue );
 
 1321 QMap<QString, QgsProcessingModelChildAlgorithm> QgsProcessingModelAlgorithm::childAlgorithms()
 const 
 1323   return mChildAlgorithms;
 
 1326 void QgsProcessingModelAlgorithm::setParameterComponents( 
const QMap<QString, QgsProcessingModelParameter> ¶meterComponents )
 
 1328   mParameterComponents = parameterComponents;
 
 1331 void QgsProcessingModelAlgorithm::setParameterComponent( 
const QgsProcessingModelParameter &component )
 
 1333   mParameterComponents.insert( component.parameterName(), component );
 
 1336 QgsProcessingModelParameter &QgsProcessingModelAlgorithm::parameterComponent( 
const QString &name )
 
 1338   if ( !mParameterComponents.contains( name ) )
 
 1340     QgsProcessingModelParameter &component = mParameterComponents[ name ];
 
 1341     component.setParameterName( name );
 
 1344   return mParameterComponents[ name ];
 
 1347 QList< QgsProcessingModelParameter > QgsProcessingModelAlgorithm::orderedParameters()
 const 
 1349   QList< QgsProcessingModelParameter > res;
 
 1350   QSet< QString > found;
 
 1351   for ( 
const QString ¶meter : mParameterOrder )
 
 1353     if ( mParameterComponents.contains( parameter ) )
 
 1355       res << mParameterComponents.value( parameter );
 
 1361   for ( 
auto it = mParameterComponents.constBegin(); it != mParameterComponents.constEnd(); ++it )
 
 1363     if ( !found.contains( it.key() ) )
 
 1371 void QgsProcessingModelAlgorithm::setParameterOrder( 
const QStringList &order )
 
 1373   mParameterOrder = order;
 
 1376 void QgsProcessingModelAlgorithm::updateDestinationParameters()
 
 1379   QMutableListIterator<const QgsProcessingParameterDefinition *> it( mParameters );
 
 1380   while ( it.hasNext() )
 
 1390   qDeleteAll( mOutputs );
 
 1394   QSet< QString > usedFriendlyNames;
 
 1395   auto uniqueSafeName = [&usedFriendlyNames ]( 
const QString & name )->QString
 
 1397     const QString base = safeName( name, 
false );
 
 1398     QString candidate = base;
 
 1400     while ( usedFriendlyNames.contains( candidate ) )
 
 1403       candidate = QStringLiteral( 
"%1_%2" ).arg( base ).arg( i );
 
 1405     usedFriendlyNames.insert( candidate );
 
 1409   QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
 
 1410   for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
 
 1412     QMap<QString, QgsProcessingModelOutput> outputs = childIt->modelOutputs();
 
 1413     QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
 
 1414     for ( ; outputIt != outputs.constEnd(); ++outputIt )
 
 1416       if ( !childIt->isActive() || !childIt->algorithm() )
 
 1424       std::unique_ptr< QgsProcessingParameterDefinition > param( source->
clone() );
 
 1428       if ( outputIt->isMandatory() )
 
 1430       if ( mInternalVersion != InternalVersion::Version1 && !outputIt->description().isEmpty() )
 
 1432         QString friendlyName = uniqueSafeName( outputIt->description() );
 
 1433         param->setName( friendlyName );
 
 1437         param->setName( outputIt->childId() + 
':' + outputIt->name() );
 
 1440       param->metadata().insert( QStringLiteral( 
"_modelChildId" ), outputIt->childId() );
 
 1441       param->metadata().insert( QStringLiteral( 
"_modelChildOutputName" ), outputIt->name() );
 
 1442       param->metadata().insert( QStringLiteral( 
"_modelChildProvider" ), childIt->algorithm()->provider()->id() );
 
 1444       param->setDescription( outputIt->description() );
 
 1445       param->setDefaultValue( outputIt->defaultValue() );
 
 1448       if ( addParameter( param.release() ) && newDestParam )
 
 1455           newDestParam->mOriginalProvider = provider;
 
 1462 void QgsProcessingModelAlgorithm::addGroupBox( 
const QgsProcessingModelGroupBox &groupBox )
 
 1464   mGroupBoxes.insert( groupBox.uuid(), groupBox );
 
 1467 QList<QgsProcessingModelGroupBox> QgsProcessingModelAlgorithm::groupBoxes()
 const 
 1469   return mGroupBoxes.values();
 
 1472 void QgsProcessingModelAlgorithm::removeGroupBox( 
const QString &uuid )
 
 1474   mGroupBoxes.remove( uuid );
 
 1477 QVariant QgsProcessingModelAlgorithm::toVariant()
 const 
 1480   map.insert( QStringLiteral( 
"model_name" ), mModelName );
 
 1481   map.insert( QStringLiteral( 
"model_group" ), mModelGroup );
 
 1482   map.insert( QStringLiteral( 
"help" ), mHelpContent );
 
 1483   map.insert( QStringLiteral( 
"internal_version" ), 
qgsEnumValueToKey( mInternalVersion ) );
 
 1485   QVariantMap childMap;
 
 1486   QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
 
 1487   for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
 
 1489     childMap.insert( childIt.key(), childIt.value().toVariant() );
 
 1491   map.insert( QStringLiteral( 
"children" ), childMap );
 
 1493   QVariantMap paramMap;
 
 1494   QMap< QString, QgsProcessingModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
 
 1495   for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
 
 1497     paramMap.insert( paramIt.key(), paramIt.value().toVariant() );
 
 1499   map.insert( QStringLiteral( 
"parameters" ), paramMap );
 
 1501   QVariantMap paramDefMap;
 
 1506   map.insert( QStringLiteral( 
"parameterDefinitions" ), paramDefMap );
 
 1508   QVariantList groupBoxDefs;
 
 1509   for ( 
auto it = mGroupBoxes.constBegin(); it != mGroupBoxes.constEnd(); ++it )
 
 1511     groupBoxDefs.append( it.value().toVariant() );
 
 1513   map.insert( QStringLiteral( 
"groupBoxes" ), groupBoxDefs );
 
 1515   map.insert( QStringLiteral( 
"modelVariables" ), mVariables );
 
 1517   map.insert( QStringLiteral( 
"designerParameterValues" ), mDesignerParameterValues );
 
 1519   map.insert( QStringLiteral( 
"parameterOrder" ), mParameterOrder );
 
 1524 bool QgsProcessingModelAlgorithm::loadVariant( 
const QVariant &model )
 
 1526   QVariantMap map = model.toMap();
 
 1528   mModelName = map.value( QStringLiteral( 
"model_name" ) ).toString();
 
 1529   mModelGroup = map.value( QStringLiteral( 
"model_group" ) ).toString();
 
 1530   mModelGroupId = map.value( QStringLiteral( 
"model_group" ) ).toString();
 
 1531   mHelpContent = map.value( QStringLiteral( 
"help" ) ).toMap();
 
 1533   mInternalVersion = 
qgsEnumKeyToValue( map.value( QStringLiteral( 
"internal_version" ) ).toString(), InternalVersion::Version1 );
 
 1535   mVariables = map.value( QStringLiteral( 
"modelVariables" ) ).toMap();
 
 1536   mDesignerParameterValues = map.value( QStringLiteral( 
"designerParameterValues" ) ).toMap();
 
 1538   mParameterOrder = map.value( QStringLiteral( 
"parameterOrder" ) ).toStringList();
 
 1540   mChildAlgorithms.clear();
 
 1541   QVariantMap childMap = map.value( QStringLiteral( 
"children" ) ).toMap();
 
 1542   QVariantMap::const_iterator childIt = childMap.constBegin();
 
 1543   for ( ; childIt != childMap.constEnd(); ++childIt )
 
 1545     QgsProcessingModelChildAlgorithm child;
 
 1549     if ( !child.loadVariant( childIt.value() ) )
 
 1552     mChildAlgorithms.insert( child.childId(), child );
 
 1555   mParameterComponents.clear();
 
 1556   QVariantMap paramMap = map.value( QStringLiteral( 
"parameters" ) ).toMap();
 
 1557   QVariantMap::const_iterator paramIt = paramMap.constBegin();
 
 1558   for ( ; paramIt != paramMap.constEnd(); ++paramIt )
 
 1560     QgsProcessingModelParameter param;
 
 1561     if ( !param.loadVariant( paramIt.value().toMap() ) )
 
 1564     mParameterComponents.insert( param.parameterName(), param );
 
 1567   qDeleteAll( mParameters );
 
 1568   mParameters.clear();
 
 1569   QVariantMap paramDefMap = map.value( QStringLiteral( 
"parameterDefinitions" ) ).toMap();
 
 1571   auto addParam = [ = ]( 
const QVariant & value )
 
 1579       if ( param->name() == QLatin1String( 
"VERBOSE_LOG" ) )
 
 1583       param->setHelp( mHelpContent.value( param->name() ).toString() );
 
 1586       addParameter( param.release() );
 
 1590       QVariantMap map = value.toMap();
 
 1591       QString type = map.value( QStringLiteral( 
"parameter_type" ) ).toString();
 
 1592       QString name = map.value( QStringLiteral( 
"name" ) ).toString();
 
 1594       QgsMessageLog::logMessage( QCoreApplication::translate( 
"Processing", 
"Could not load parameter %1 of type %2." ).arg( name, type ), QCoreApplication::translate( 
"Processing", 
"Processing" ) );
 
 1598   QSet< QString > loadedParams;
 
 1600   for ( 
const QString &name : std::as_const( mParameterOrder ) )
 
 1602     if ( paramDefMap.contains( name ) )
 
 1604       addParam( paramDefMap.value( name ) );
 
 1605       loadedParams << name;
 
 1609   QVariantMap::const_iterator paramDefIt = paramDefMap.constBegin();
 
 1610   for ( ; paramDefIt != paramDefMap.constEnd(); ++paramDefIt )
 
 1612     if ( !loadedParams.contains( paramDefIt.key() ) )
 
 1613       addParam( paramDefIt.value() );
 
 1616   mGroupBoxes.clear();
 
 1617   const QVariantList groupBoxList = map.value( QStringLiteral( 
"groupBoxes" ) ).toList();
 
 1618   for ( 
const QVariant &groupBoxDef : groupBoxList )
 
 1620     QgsProcessingModelGroupBox groupBox;
 
 1621     groupBox.loadVariant( groupBoxDef.toMap() );
 
 1622     mGroupBoxes.insert( groupBox.uuid(), groupBox );
 
 1625   updateDestinationParameters();
 
 1630 bool QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( 
const QList<int> &acceptableDataTypes, 
QgsProcessing::SourceType outputType )
 
 1635   return ( acceptableDataTypes.empty()
 
 1636            || acceptableDataTypes.contains( outputType )
 
 1647 void QgsProcessingModelAlgorithm::reattachAlgorithms()
 const 
 1649   QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
 
 1650   for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
 
 1652     if ( !childIt->algorithm() )
 
 1653       childIt->reattach();
 
 1657 bool QgsProcessingModelAlgorithm::toFile( 
const QString &path )
 const 
 1659   QDomDocument doc = QDomDocument( QStringLiteral( 
"model" ) );
 
 1661   doc.appendChild( elem );
 
 1664   if ( file.open( QFile::WriteOnly | QFile::Truncate ) )
 
 1666     QTextStream stream( &file );
 
 1667     doc.save( stream, 2 );
 
 1674 bool QgsProcessingModelAlgorithm::fromFile( 
const QString &path )
 
 1679   if ( file.open( QFile::ReadOnly ) )
 
 1681     if ( !doc.setContent( &file ) )
 
 1692   return loadVariant( props );
 
 1695 void QgsProcessingModelAlgorithm::setChildAlgorithms( 
const QMap<QString, QgsProcessingModelChildAlgorithm> &childAlgorithms )
 
 1697   mChildAlgorithms = childAlgorithms;
 
 1698   updateDestinationParameters();
 
 1701 void QgsProcessingModelAlgorithm::setChildAlgorithm( 
const QgsProcessingModelChildAlgorithm &
algorithm )
 
 1704   updateDestinationParameters();
 
 1707 QString QgsProcessingModelAlgorithm::addChildAlgorithm( QgsProcessingModelChildAlgorithm &
algorithm )
 
 1709   if ( 
algorithm.childId().isEmpty() || mChildAlgorithms.contains( 
algorithm.childId() ) )
 
 1713   updateDestinationParameters();
 
 1717 QgsProcessingModelChildAlgorithm &QgsProcessingModelAlgorithm::childAlgorithm( 
const QString &childId )
 
 1719   return mChildAlgorithms[ childId ];
 
 1722 bool QgsProcessingModelAlgorithm::removeChildAlgorithm( 
const QString &
id )
 
 1724   if ( !dependentChildAlgorithms( 
id ).isEmpty() )
 
 1727   mChildAlgorithms.remove( 
id );
 
 1728   updateDestinationParameters();
 
 1732 void QgsProcessingModelAlgorithm::deactivateChildAlgorithm( 
const QString &
id )
 
 1734   const auto constDependentChildAlgorithms = dependentChildAlgorithms( 
id );
 
 1735   for ( 
const QString &child : constDependentChildAlgorithms )
 
 1737     childAlgorithm( child ).setActive( 
false );
 
 1739   childAlgorithm( 
id ).setActive( 
false );
 
 1740   updateDestinationParameters();
 
 1743 bool QgsProcessingModelAlgorithm::activateChildAlgorithm( 
const QString &
id )
 
 1745   const auto constDependsOnChildAlgorithms = dependsOnChildAlgorithms( 
id );
 
 1746   for ( 
const QString &child : constDependsOnChildAlgorithms )
 
 1748     if ( !childAlgorithm( child ).isActive() )
 
 1751   childAlgorithm( 
id ).setActive( 
true );
 
 1752   updateDestinationParameters();
 
 1758   if ( addParameter( definition ) )
 
 1759     mParameterComponents.insert( definition->
name(), component );
 
 1764   removeParameter( definition->
name() );
 
 1765   addParameter( definition );
 
 1768 void QgsProcessingModelAlgorithm::removeModelParameter( 
const QString &name )
 
 1770   removeParameter( name );
 
 1771   mParameterComponents.remove( name );
 
 1774 void QgsProcessingModelAlgorithm::changeParameterName( 
const QString &oldName, 
const QString &newName )
 
 1779   auto replaceExpressionVariable = [oldName, newName, &expressionContext]( 
const QString & expressionString ) -> std::tuple< bool, QString >
 
 1782     expression.prepare( &expressionContext );
 
 1783     QSet<QString> variables = expression.referencedVariables();
 
 1784     if ( variables.contains( oldName ) )
 
 1786       QString newExpression = expressionString;
 
 1787       newExpression.replace( QStringLiteral( 
"@%1" ).arg( oldName ), QStringLiteral( 
"@%2" ).arg( newName ) );
 
 1788       return { 
true, newExpression };
 
 1790     return { 
false, QString() };
 
 1793   QMap< QString, QgsProcessingModelChildAlgorithm >::iterator childIt = mChildAlgorithms.begin();
 
 1794   for ( ; childIt != mChildAlgorithms.end(); ++childIt )
 
 1796     bool changed = 
false;
 
 1797     QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
 
 1798     QMap<QString, QgsProcessingModelChildParameterSources>::iterator paramIt = childParams.begin();
 
 1799     for ( ; paramIt != childParams.end(); ++paramIt )
 
 1801       QList< QgsProcessingModelChildParameterSource > &value = paramIt.value();
 
 1802       for ( 
auto valueIt = value.begin(); valueIt != value.end(); ++valueIt )
 
 1804         switch ( valueIt->source() )
 
 1806           case QgsProcessingModelChildParameterSource::ModelParameter:
 
 1808             if ( valueIt->parameterName() == oldName )
 
 1810               valueIt->setParameterName( newName );
 
 1816           case QgsProcessingModelChildParameterSource::Expression:
 
 1818             bool updatedExpression = 
false;
 
 1819             QString newExpression;
 
 1820             std::tie( updatedExpression, newExpression ) = replaceExpressionVariable( valueIt->expression() );
 
 1821             if ( updatedExpression )
 
 1823               valueIt->setExpression( newExpression );
 
 1829           case QgsProcessingModelChildParameterSource::StaticValue:
 
 1831             if ( valueIt->staticValue().canConvert< 
QgsProperty >() )
 
 1836                 bool updatedExpression = 
false;
 
 1837                 QString newExpression;
 
 1838                 std::tie( updatedExpression, newExpression ) = replaceExpressionVariable( property.expressionString() );
 
 1839                 if ( updatedExpression )
 
 1841                   property.setExpressionString( newExpression );
 
 1842                   valueIt->setStaticValue( property );
 
 1850           case QgsProcessingModelChildParameterSource::ChildOutput:
 
 1851           case QgsProcessingModelChildParameterSource::ExpressionText:
 
 1852           case QgsProcessingModelChildParameterSource::ModelOutput:
 
 1858       childIt->setParameterSources( childParams );
 
 1862 bool QgsProcessingModelAlgorithm::childAlgorithmsDependOnParameter( 
const QString &name )
 const 
 1864   QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
 
 1865   for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
 
 1868     QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
 
 1869     QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
 
 1870     for ( ; paramIt != childParams.constEnd(); ++paramIt )
 
 1872       const auto constValue = paramIt.value();
 
 1873       for ( 
const QgsProcessingModelChildParameterSource &source : constValue )
 
 1875         if ( source.source() == QgsProcessingModelChildParameterSource::ModelParameter
 
 1876              && source.parameterName() == name )
 
 1886 bool QgsProcessingModelAlgorithm::otherParametersDependOnParameter( 
const QString &name )
 const 
 1888   const auto constMParameters = mParameters;
 
 1891     if ( def->
name() == name )
 
 1900 QMap<QString, QgsProcessingModelParameter> QgsProcessingModelAlgorithm::parameterComponents()
 const 
 1902   return mParameterComponents;
 
 1905 void QgsProcessingModelAlgorithm::dependentChildAlgorithmsRecursive( 
const QString &childId, QSet<QString> &depends, 
const QString &branch )
 const 
 1907   QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
 
 1908   for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
 
 1910     if ( depends.contains( childIt->childId() ) )
 
 1914     const QList< QgsProcessingModelChildDependency > constDependencies = childIt->dependencies();
 
 1915     bool hasDependency = 
false;
 
 1916     for ( 
const QgsProcessingModelChildDependency &dep : constDependencies )
 
 1918       if ( dep.childId == childId && ( branch.isEmpty() || dep.conditionalBranch == branch ) )
 
 1920         hasDependency = 
true;
 
 1925     if ( hasDependency )
 
 1927       depends.insert( childIt->childId() );
 
 1928       dependentChildAlgorithmsRecursive( childIt->childId(), depends, branch );
 
 1933     QMap<QString, QgsProcessingModelChildParameterSources> childParams = childIt->parameterSources();
 
 1934     QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
 
 1935     for ( ; paramIt != childParams.constEnd(); ++paramIt )
 
 1937       const auto constValue = paramIt.value();
 
 1938       for ( 
const QgsProcessingModelChildParameterSource &source : constValue )
 
 1940         if ( source.source() == QgsProcessingModelChildParameterSource::ChildOutput
 
 1941              && source.outputChildId() == childId )
 
 1943           depends.insert( childIt->childId() );
 
 1944           dependentChildAlgorithmsRecursive( childIt->childId(), depends, branch );
 
 1952 QSet<QString> QgsProcessingModelAlgorithm::dependentChildAlgorithms( 
const QString &childId, 
const QString &conditionalBranch )
 const 
 1954   QSet< QString > algs;
 
 1958   algs.insert( childId );
 
 1960   dependentChildAlgorithmsRecursive( childId, algs, conditionalBranch );
 
 1963   algs.remove( childId );
 
 1969 void QgsProcessingModelAlgorithm::dependsOnChildAlgorithmsRecursive( 
const QString &childId, QSet< QString > &depends )
 const 
 1971   const QgsProcessingModelChildAlgorithm &alg = mChildAlgorithms.value( childId );
 
 1974   const QList< QgsProcessingModelChildDependency > constDependencies = alg.dependencies();
 
 1975   for ( 
const QgsProcessingModelChildDependency &val : constDependencies )
 
 1977     if ( !depends.contains( val.childId ) )
 
 1979       depends.insert( val.childId );
 
 1980       dependsOnChildAlgorithmsRecursive( val.childId, depends );
 
 1985   QMap<QString, QgsProcessingModelChildParameterSources> childParams = alg.parameterSources();
 
 1986   QMap<QString, QgsProcessingModelChildParameterSources>::const_iterator paramIt = childParams.constBegin();
 
 1987   for ( ; paramIt != childParams.constEnd(); ++paramIt )
 
 1989     const auto constValue = paramIt.value();
 
 1990     for ( 
const QgsProcessingModelChildParameterSource &source : constValue )
 
 1992       if ( source.source() == QgsProcessingModelChildParameterSource::ChildOutput && !depends.contains( source.outputChildId() ) )
 
 1994         depends.insert( source.outputChildId() );
 
 1995         dependsOnChildAlgorithmsRecursive( source.outputChildId(), depends );
 
 2001 QSet< QString > QgsProcessingModelAlgorithm::dependsOnChildAlgorithms( 
const QString &childId )
 const 
 2003   QSet< QString > algs;
 
 2007   algs.insert( childId );
 
 2009   dependsOnChildAlgorithmsRecursive( childId, algs );
 
 2012   algs.remove( childId );
 
 2017 QList<QgsProcessingModelChildDependency> QgsProcessingModelAlgorithm::availableDependenciesForChildAlgorithm( 
const QString &childId )
 const 
 2019   QSet< QString > dependent;
 
 2020   if ( !childId.isEmpty() )
 
 2022     dependent.unite( dependentChildAlgorithms( childId ) );
 
 2023     dependent.insert( childId );
 
 2026   QList<QgsProcessingModelChildDependency> res;
 
 2027   for ( 
auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
 
 2029     if ( !dependent.contains( it->childId() ) )
 
 2032       bool hasBranches = 
false;
 
 2033       if ( it->algorithm() )
 
 2041             QgsProcessingModelChildDependency alg;
 
 2042             alg.childId = it->childId();
 
 2043             alg.conditionalBranch = def->
name();
 
 2051         QgsProcessingModelChildDependency alg;
 
 2052         alg.childId = it->childId();
 
 2060 bool QgsProcessingModelAlgorithm::validateChildAlgorithm( 
const QString &childId, QStringList &issues )
 const 
 2063   QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constFind( childId );
 
 2064   if ( childIt != mChildAlgorithms.constEnd() )
 
 2066     if ( !childIt->algorithm() )
 
 2068       issues << QObject::tr( 
"Algorithm is not available: <i>%1</i>" ).arg( childIt->algorithmId() );
 
 2077       if ( childIt->parameterSources().contains( def->
name() ) )
 
 2080         const QList< QgsProcessingModelChildParameterSource > sources = childIt->parameterSources().value( def->
name() );
 
 2081         for ( 
const QgsProcessingModelChildParameterSource &source : sources )
 
 2083           switch ( source.source() )
 
 2085             case QgsProcessingModelChildParameterSource::StaticValue:
 
 2089                 issues <<  QObject::tr( 
"Value for <i>%1</i> is not acceptable for this parameter" ).arg( def->
name() );
 
 2093             case QgsProcessingModelChildParameterSource::ModelParameter:
 
 2094               if ( !parameterComponents().contains( source.parameterName() ) )
 
 2097                 issues <<  QObject::tr( 
"Model input <i>%1</i> used for parameter <i>%2</i> does not exist" ).arg( source.parameterName(), def->
name() );
 
 2101             case QgsProcessingModelChildParameterSource::ChildOutput:
 
 2102               if ( !childAlgorithms().contains( source.outputChildId() ) )
 
 2105                 issues <<  QObject::tr( 
"Child algorithm <i>%1</i> used for parameter <i>%2</i> does not exist" ).arg( source.outputChildId(), def->
name() );
 
 2109             case QgsProcessingModelChildParameterSource::Expression:
 
 2110             case QgsProcessingModelChildParameterSource::ExpressionText:
 
 2111             case QgsProcessingModelChildParameterSource::ModelOutput:
 
 2127           issues <<  QObject::tr( 
"Parameter <i>%1</i> is mandatory" ).arg( def->
name() );
 
 2136     issues << QObject::tr( 
"Invalid child ID: <i>%1</i>" ).arg( childId );
 
 2141 bool QgsProcessingModelAlgorithm::canExecute( QString *errorMessage )
 const 
 2143   reattachAlgorithms();
 
 2144   QMap< QString, QgsProcessingModelChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
 
 2145   for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
 
 2147     if ( !childIt->algorithm() )
 
 2151         *errorMessage = QObject::tr( 
"The model you are trying to run contains an algorithm that is not available: <i>%1</i>" ).arg( childIt->algorithmId() );
 
 2159 QString QgsProcessingModelAlgorithm::asPythonCommand( 
const QVariantMap ¶meters, 
QgsProcessingContext &context )
 const 
 2161   if ( mSourceFile.isEmpty() )
 
 2176   QgsProcessingModelAlgorithm *alg = 
new QgsProcessingModelAlgorithm();
 
 2177   alg->loadVariant( toVariant() );
 
 2178   alg->setProvider( provider() );
 
 2179   alg->setSourceFilePath( sourceFilePath() );
 
 2183 QString QgsProcessingModelAlgorithm::safeName( 
const QString &name, 
bool capitalize )
 
 2185   QString n = name.toLower().trimmed();
 
 2186   const thread_local QRegularExpression rx( QStringLiteral( 
"[^\\sa-z_A-Z0-9]" ) );
 
 2187   n.replace( rx, QString() );
 
 2188   const thread_local QRegularExpression rx2( QStringLiteral( 
"^\\d*" ) ); 
 
 2189   n.replace( rx2, QString() );
 
 2191     n = n.replace( 
' ', 
'_' );
 
 2195 QVariantMap QgsProcessingModelAlgorithm::variables()
 const 
 2200 void QgsProcessingModelAlgorithm::setVariables( 
const QVariantMap &variables )
 
 2202   mVariables = variables;
 
 2205 QVariantMap QgsProcessingModelAlgorithm::designerParameterValues()
 const 
 2207   return mDesignerParameterValues;