First steps to model/save restore in c++

Models now save to QVariantMap, using QgsXmlUtils to save to
an xml based format (with extension .model3)
This commit is contained in:
Nyall Dawson 2017-06-20 17:26:57 +10:00
parent 179a377da0
commit 9a2f14b931
9 changed files with 611 additions and 48 deletions

View File

@ -139,6 +139,20 @@ class QgsProcessingModelAlgorithm : QgsProcessingAlgorithm
.. seealso:: setOutputChildId()
%End
QVariant toVariant() const;
%Docstring
Saves this source to a QVariant.
.. seealso:: loadVariant()
:rtype: QVariant
%End
bool loadVariant( const QVariantMap &map );
%Docstring
Loads this source from a QVariantMap.
.. seealso:: toVariant()
:rtype: bool
%End
};
class Component
@ -192,6 +206,18 @@ Copies are protected to avoid slicing
%End
void saveCommonProperties( QVariantMap &map ) const;
%Docstring
Saves the component properties to a QVariantMap.
.. seealso:: restoreCommonProperties()
%End
void restoreCommonProperties( const QVariantMap &map );
%Docstring
Restores the component properties from a QVariantMap.
.. seealso:: saveCommonProperties()
%End
};
class ModelParameter : QgsProcessingModelAlgorithm::Component
@ -227,6 +253,20 @@ Copies are protected to avoid slicing
.. seealso:: parameterName()
%End
QVariant toVariant() const;
%Docstring
Saves this parameter to a QVariant.
.. seealso:: loadVariant()
:rtype: QVariant
%End
bool loadVariant( const QVariantMap &map );
%Docstring
Loads this parameter from a QVariantMap.
.. seealso:: toVariant()
:rtype: bool
%End
};
@ -273,6 +313,20 @@ Copies are protected to avoid slicing
.. seealso:: outputName()
%End
QVariant toVariant() const;
%Docstring
Saves this output to a QVariant.
.. seealso:: loadVariant()
:rtype: QVariant
%End
bool loadVariant( const QVariantMap &map );
%Docstring
Loads this output from a QVariantMap.
.. seealso:: toVariant()
:rtype: bool
%End
};
class ChildAlgorithm : QgsProcessingModelAlgorithm::Component
@ -461,6 +515,20 @@ Copies are protected to avoid slicing
.. seealso:: modelOutputs()
%End
QVariant toVariant() const;
%Docstring
Saves this child to a QVariant.
.. seealso:: loadVariant()
:rtype: QVariant
%End
bool loadVariant( const QVariant &child );
%Docstring
Loads this child from a QVariant.
.. seealso:: toVariant()
:rtype: bool
%End
};
QgsProcessingModelAlgorithm( const QString &name = QString(), const QString &group = QString() );
@ -484,6 +552,18 @@ Copies are protected to avoid slicing
virtual QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) const;
void setName( const QString &name );
%Docstring
Sets the model ``name``.
.. seealso:: name()
%End
void setGroup( const QString &group );
%Docstring
Sets the model ``group``.
.. seealso:: group()
%End
QMap<QString, QgsProcessingModelAlgorithm::ChildAlgorithm> childAlgorithms() const;
%Docstring
Returns the map of child algorithms contained in the model. The keys
@ -651,6 +731,20 @@ Copies are protected to avoid slicing
:rtype: QgsProcessingModelAlgorithm.ModelParameter
%End
bool toFile( const QString &path ) const;
%Docstring
Writes the model to a file, at the specified ``path``.
.. seealso:: fromFile()
:rtype: bool
%End
bool fromFile( const QString &path );
%Docstring
Reads the model from a file, at the specified ``path``.
.. seealso:: toFile()
:rtype: bool
%End
};

View File

@ -56,22 +56,16 @@ class AddModelFromFileAction(ToolboxAction):
self.tr('Open model', 'AddModelFromFileAction'), lastDir,
self.tr('Processing model files (*.model *.MODEL)', 'AddModelFromFileAction'))
if filename:
try:
settings.setValue('Processing/lastModelsDir',
QFileInfo(filename).absoluteDir().absolutePath())
settings.setValue('Processing/lastModelsDir',
QFileInfo(filename).absoluteDir().absolutePath())
ModelerAlgorithm.fromFile(filename)
except WrongModelException:
alg = ModelerAlgorithm()
if not alg.fromFile(filename):
QMessageBox.warning(
self.toolbox,
self.tr('Error reading model', 'AddModelFromFileAction'),
self.tr('The selected file does not contain a valid model', 'AddModelFromFileAction'))
return
except:
QMessageBox.warning(self.toolbox,
self.tr('Error reading model', 'AddModelFromFileAction'),
self.tr('Cannot read file', 'AddModelFromFileAction'))
return
destFilename = os.path.join(ModelerUtils.modelsFolders()[0], os.path.basename(filename))
shutil.copyfile(filename, destFilename)
QgsApplication.processingRegistry().providerById('model').refreshAlgorithms()

View File

@ -365,15 +365,6 @@ class ModelerAlgorithm(QgsProcessingModelAlgorithm):
model._name = model.modeler_name
return model
@staticmethod
def fromFile(filename):
with open(filename) as f:
s = f.read()
alg = ModelerAlgorithm.fromJson(s)
if alg:
alg.descriptionFile = filename
return alg
def toPython(self):
s = ['##%s=name' % self.name()]
for param in list(self.parameterComponents().values()):

View File

@ -27,10 +27,13 @@ __revision__ = '$Format:%H$'
import os
from qgis.PyQt.QtXml import QDomDocument
from qgis.core import (QgsApplication,
QgsProcessingProvider,
QgsMessageLog,
QgsProcessingUtils)
QgsProcessingUtils,
QgsXmlUtils)
from processing.core.ProcessingConfig import ProcessingConfig, Setting
from processing.modeler.ModelerUtils import ModelerUtils
@ -98,13 +101,15 @@ class ModelerAlgorithmProvider(QgsProcessingProvider):
return
for path, subdirs, files in os.walk(folder):
for descriptionFile in files:
if descriptionFile.endswith('model'):
if descriptionFile.endswith('model3'):
try:
fullpath = os.path.join(path, descriptionFile)
alg = ModelerAlgorithm.fromFile(fullpath)
if alg.name():
alg.descriptionFile = fullpath
self.algs.append(alg)
alg = ModelerAlgorithm()
if alg.fromFile(fullpath):
if alg.name():
alg.descriptionFile = fullpath
self.algs.append(alg)
else:
QgsMessageLog.logMessage(self.tr('Could not load model {0}', 'ModelerAlgorithmProvider').format(descriptionFile),
self.tr('Processing'), QgsMessageLog.CRITICAL)

View File

@ -42,7 +42,8 @@ from qgis.core import (QgsApplication,
QgsSettings,
QgsMessageLog,
QgsProcessingUtils,
QgsProcessingModelAlgorithm)
QgsProcessingModelAlgorithm,
QgsXmlUtils)
from qgis.gui import QgsMessageBar
from processing.gui.HelpEditionDialog import HelpEditionDialog
from processing.gui.AlgorithmDialog import AlgorithmDialog
@ -52,6 +53,7 @@ from processing.modeler.ModelerParametersDialog import ModelerParametersDialog
from processing.modeler.ModelerUtils import ModelerUtils
from processing.modeler.ModelerScene import ModelerScene
from processing.modeler.WrongModelException import WrongModelException
from qgis.PyQt.QtXml import QDomDocument
pluginPath = os.path.split(os.path.dirname(__file__))[0]
WIDGET, BASE = uic.loadUiType(
@ -435,25 +437,21 @@ class ModelerDialog(BASE, WIDGET):
self, self.tr('Warning'), self.tr('Please enter group and model names before saving')
)
return
self.model._name = str(self.textName.text())
self.model._group = str(self.textGroup.text())
self.model.setName(str(self.textName.text()))
self.model.setGroup(str(self.textGroup.text()))
if self.model.descriptionFile is not None and not saveAs:
filename = self.model.descriptionFile
else:
filename, filter = QFileDialog.getSaveFileName(self,
self.tr('Save Model'),
ModelerUtils.modelsFolders()[0],
self.tr('Processing models (*.model)'))
self.tr('Processing models (*.model3)'))
if filename:
if not filename.endswith('.model'):
filename += '.model'
if not filename.endswith('.model3'):
filename += '.model3'
self.model.descriptionFile = filename
if filename:
text = self.model.toJson()
try:
with codecs.open(filename, 'w', encoding='utf-8') as fout:
fout.write(text)
except:
if not self.model.toFile(filename):
if saveAs:
QMessageBox.warning(self, self.tr('I/O error'),
self.tr('Unable to save edits. Reason:\n {0}').format(str(sys.exc_info()[1])))
@ -475,28 +473,22 @@ class ModelerDialog(BASE, WIDGET):
ModelerUtils.modelsFolders()[0],
self.tr('Processing models (*.model *.MODEL)'))
if filename:
try:
alg = ModelerAlgorithm.fromFile(filename)
alg = ModelerAlgorithm()
if alg.fromFile(filename):
self.model = alg
self.textGroup.setText(alg._group)
self.textName.setText(alg._name)
self.textGroup.setText(alg.group())
self.textName.setText(alg.name())
self.repaintModel()
self.view.centerOn(0, 0)
self.hasChanged = False
except WrongModelException as e:
QgsMessageLog.logMessage(self.tr('Could not load model {0}\n{1}').format(filename, e.msg),
else:
QgsMessageLog.logMessage(self.tr('Could not load model {0}').format(filename),
self.tr('Processing'),
QgsMessageLog.CRITICAL)
QMessageBox.critical(self, self.tr('Could not open model'),
self.tr('The selected model could not be loaded.\n'
'See the log for more information.'))
except Exception as e:
QgsMessageLog.logMessage(self.tr('Could not load model {0}\n{1}').format(filename, e.args[0]),
self.tr('Processing'), QgsMessageLog.CRITICAL)
QMessageBox.critical(self, self.tr('Could not open model'),
self.tr('The selected model could not be loaded.\n'
'See the log for more information.'))
def repaintModel(self, controls=True):
self.scene = ModelerScene(self, dialog=self)

View File

@ -17,6 +17,9 @@
#include "qgsprocessingmodelalgorithm.h"
#include "qgsprocessingregistry.h"
#include "qgsxmlutils.h"
#include <QFile>
#include <QTextStream>
QgsProcessingModelAlgorithm::ChildAlgorithm::ChildAlgorithm( const QString &algorithmId )
: mAlgorithmId( algorithmId )
@ -78,6 +81,22 @@ QgsProcessingModelAlgorithm::Component::Component( const QString &description )
: mDescription( description )
{}
void QgsProcessingModelAlgorithm::Component::saveCommonProperties( QVariantMap &map ) const
{
map.insert( QStringLiteral( "component_pos_x" ), mPosition.x() );
map.insert( QStringLiteral( "component_pos_y" ), mPosition.y() );
map.insert( QStringLiteral( "component_description" ), mDescription );
}
void QgsProcessingModelAlgorithm::Component::restoreCommonProperties( const QVariantMap &map )
{
QPointF pos;
pos.setX( map.value( QStringLiteral( "component_pos_x" ) ).toDouble() );
pos.setY( map.value( QStringLiteral( "component_pos_y" ) ).toDouble() );
mPosition = pos;
mDescription = map.value( QStringLiteral( "component_description" ) ).toString();
}
QStringList QgsProcessingModelAlgorithm::ChildAlgorithm::dependencies() const
{
return mDependencies;
@ -113,6 +132,77 @@ void QgsProcessingModelAlgorithm::ChildAlgorithm::setModelOutputs( const QMap<QS
mModelOutputs = modelOutputs;
}
QVariant QgsProcessingModelAlgorithm::ChildAlgorithm::toVariant() const
{
QVariantMap map;
map.insert( QStringLiteral( "id" ), mId );
map.insert( QStringLiteral( "alg_id" ), mAlgorithmId );
map.insert( QStringLiteral( "active" ), mActive );
map.insert( QStringLiteral( "dependencies" ), mDependencies );
map.insert( QStringLiteral( "parameters_collapsed" ), mParametersCollapsed );
map.insert( QStringLiteral( "outputs_collapsed" ), mOutputsCollapsed );
saveCommonProperties( map );
QVariantMap paramMap;
QMap< QString, QgsProcessingModelAlgorithm::ChildParameterSource >::const_iterator paramIt = mParams.constBegin();
for ( ; paramIt != mParams.constEnd(); ++paramIt )
{
paramMap.insert( paramIt.key(), paramIt.value().toVariant() );
}
map.insert( "params", paramMap );
QVariantMap outputMap;
QMap< QString, QgsProcessingModelAlgorithm::ModelOutput >::const_iterator outputIt = mModelOutputs.constBegin();
for ( ; outputIt != mModelOutputs.constEnd(); ++outputIt )
{
outputMap.insert( outputIt.key(), outputIt.value().toVariant() );
}
map.insert( "outputs", outputMap );
return map;
}
bool QgsProcessingModelAlgorithm::ChildAlgorithm::loadVariant( const QVariant &child )
{
QVariantMap map = child.toMap();
mId = map.value( QStringLiteral( "id" ) ).toString();
mAlgorithmId = map.value( QStringLiteral( "alg_id" ) ).toString();
mActive = map.value( QStringLiteral( "active" ) ).toBool();
mDependencies = map.value( QStringLiteral( "dependencies" ) ).toStringList();
mParametersCollapsed = map.value( QStringLiteral( "parameters_collapsed" ) ).toBool();
mOutputsCollapsed = map.value( QStringLiteral( "outputs_collapsed" ) ).toBool();
restoreCommonProperties( map );
mParams.clear();
QVariantMap paramMap = map.value( QStringLiteral( "params" ) ).toMap();
QVariantMap::const_iterator paramIt = paramMap.constBegin();
for ( ; paramIt != paramMap.constEnd(); ++paramIt )
{
ChildParameterSource param;
if ( !param.loadVariant( paramIt.value().toMap() ) )
return false;
mParams.insert( paramIt.key(), param );
}
mModelOutputs.clear();
QVariantMap outputMap = map.value( QStringLiteral( "outputs" ) ).toMap();
QVariantMap::const_iterator outputIt = outputMap.constBegin();
for ( ; outputIt != outputMap.constEnd(); ++outputIt )
{
ModelOutput output;
if ( !output.loadVariant( outputIt.value().toMap() ) )
return false;
mModelOutputs.insert( outputIt.key(), output );
}
return true;
}
bool QgsProcessingModelAlgorithm::ChildAlgorithm::parametersCollapsed() const
{
return mParametersCollapsed;
@ -201,6 +291,16 @@ QVariantMap QgsProcessingModelAlgorithm::processAlgorithm( const QVariantMap &pa
return QVariantMap();
}
void QgsProcessingModelAlgorithm::setName( const QString &name )
{
mModelName = name;
}
void QgsProcessingModelAlgorithm::setGroup( const QString &group )
{
mModelGroup = group;
}
QMap<QString, QgsProcessingModelAlgorithm::ChildAlgorithm> QgsProcessingModelAlgorithm::childAlgorithms() const
{
return mChildAlgorithms;
@ -227,6 +327,99 @@ QgsProcessingModelAlgorithm::ModelParameter &QgsProcessingModelAlgorithm::parame
return mParameterComponents[ name ];
}
QVariant QgsProcessingModelAlgorithm::toVariant() const
{
QVariantMap map;
map.insert( QStringLiteral( "model_name" ), mModelName );
map.insert( QStringLiteral( "model_group" ), mModelGroup );
QVariantMap childMap;
QMap< QString, ChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
for ( ; childIt != mChildAlgorithms.constEnd(); ++childIt )
{
childMap.insert( childIt.key(), childIt.value().toVariant() );
}
map.insert( "children", childMap );
QVariantMap paramMap;
QMap< QString, ModelParameter >::const_iterator paramIt = mParameterComponents.constBegin();
for ( ; paramIt != mParameterComponents.constEnd(); ++paramIt )
{
paramMap.insert( paramIt.key(), paramIt.value().toVariant() );
}
map.insert( "parameters", paramMap );
return map;
}
bool QgsProcessingModelAlgorithm::loadVariant( const QVariant &model )
{
QVariantMap map = model.toMap();
mModelName = map.value( QStringLiteral( "model_name" ) ).toString();
mModelGroup = map.value( QStringLiteral( "model_group" ) ).toString();
mChildAlgorithms.clear();
QVariantMap childMap = map.value( QStringLiteral( "children" ) ).toMap();
QVariantMap::const_iterator childIt = childMap.constBegin();
for ( ; childIt != childMap.constEnd(); ++childIt )
{
ChildAlgorithm child;
if ( !child.loadVariant( childIt.value() ) )
return false;
mChildAlgorithms.insert( child.childId(), child );
}
mParameterComponents.clear();
QVariantMap paramMap = map.value( QStringLiteral( "parameters" ) ).toMap();
QVariantMap::const_iterator paramIt = paramMap.constBegin();
for ( ; paramIt != paramMap.constEnd(); ++paramIt )
{
ModelParameter param;
if ( !param.loadVariant( paramIt.value().toMap() ) )
return false;
mParameterComponents.insert( param.parameterName(), param );
}
return true;
}
bool QgsProcessingModelAlgorithm::toFile( const QString &path ) const
{
QDomDocument doc = QDomDocument( "model" );
QDomElement elem = QgsXmlUtils::writeVariant( toVariant(), doc );
doc.appendChild( elem );
QFile file( path );
if ( file.open( QFile::WriteOnly | QFile::Truncate ) )
{
QTextStream stream( &file );
doc.save( stream, 2 );
file.close();
return true;
}
return false;
}
bool QgsProcessingModelAlgorithm::fromFile( const QString &path )
{
QDomDocument doc;
QFile file( path );
if ( file.open( QFile::ReadOnly ) )
{
if ( !doc.setContent( &file ) )
return false;
file.close();
}
QVariant props = QgsXmlUtils::readVariant( doc.firstChildElement() );
return loadVariant( props );
}
void QgsProcessingModelAlgorithm::setChildAlgorithms( const QMap<QString, ChildAlgorithm> &childAlgorithms )
{
mChildAlgorithms = childAlgorithms;
@ -484,13 +677,91 @@ QgsProcessingModelAlgorithm::ChildParameterSource::Source QgsProcessingModelAlgo
return mSource;
}
QVariant QgsProcessingModelAlgorithm::ChildParameterSource::toVariant() const
{
QVariantMap map;
map.insert( QStringLiteral( "source" ), mSource );
switch ( mSource )
{
case ModelParameter:
map.insert( QStringLiteral( "parameter_name" ), mParameterName );
break;
case ChildOutput:
map.insert( QStringLiteral( "child_id" ), mChildId );
map.insert( QStringLiteral( "output_name" ), mOutputName );
break;
case StaticValue:
map.insert( QStringLiteral( "static_value" ), mStaticValue );
break;
}
return map;
}
bool QgsProcessingModelAlgorithm::ChildParameterSource::loadVariant( const QVariantMap &map )
{
mSource = static_cast< Source >( map.value( QStringLiteral( "source" ) ).toInt() );
switch ( mSource )
{
case ModelParameter:
mParameterName = map.value( QStringLiteral( "parameter_name" ) ).toString();
break;
case ChildOutput:
mChildId = map.value( QStringLiteral( "child_id" ) ).toString();
mOutputName = map.value( QStringLiteral( "output_name" ) ).toString();
break;
case StaticValue:
mStaticValue = map.value( QStringLiteral( "static_value" ) );
break;
}
return true;
}
QgsProcessingModelAlgorithm::ModelOutput::ModelOutput( const QString &description )
: QgsProcessingModelAlgorithm::Component( description )
{}
QVariant QgsProcessingModelAlgorithm::ModelOutput::toVariant() const
{
QVariantMap map;
map.insert( QStringLiteral( "child_id" ), mChildId );
map.insert( QStringLiteral( "output_name" ), mOutputName );
saveCommonProperties( map );
return map;
}
bool QgsProcessingModelAlgorithm::ModelOutput::loadVariant( const QVariantMap &map )
{
mChildId = map.value( QStringLiteral( "child_id" ) ).toString();
mOutputName = map.value( QStringLiteral( "output_name" ) ).toString();
restoreCommonProperties( map );
return true;
}
QgsProcessingModelAlgorithm::ModelParameter::ModelParameter( const QString &parameterName )
: QgsProcessingModelAlgorithm::Component()
, mParameterName( parameterName )
{
}
QVariant QgsProcessingModelAlgorithm::ModelParameter::toVariant() const
{
QVariantMap map;
map.insert( QStringLiteral( "name" ), mParameterName );
//TODO - parameter definition
saveCommonProperties( map );
return map;
}
bool QgsProcessingModelAlgorithm::ModelParameter::loadVariant( const QVariantMap &map )
{
mParameterName = map.value( QStringLiteral( "name" ) ).toString();
restoreCommonProperties( map );
return true;
}

View File

@ -140,6 +140,18 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
*/
void setOutputName( const QString &name ) { mOutputName = name; mSource = ChildOutput; }
/**
* Saves this source to a QVariant.
* \see loadVariant()
*/
QVariant toVariant() const;
/**
* Loads this source from a QVariantMap.
* \see toVariant()
*/
bool loadVariant( const QVariantMap &map );
private:
Source mSource = StaticValue;
@ -194,6 +206,18 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
//! Copies are protected to avoid slicing
Component &operator=( const QgsProcessingModelAlgorithm::Component &other ) = default;
/**
* Saves the component properties to a QVariantMap.
* \see restoreCommonProperties()
*/
void saveCommonProperties( QVariantMap &map ) const;
/**
* Restores the component properties from a QVariantMap.
* \see saveCommonProperties()
*/
void restoreCommonProperties( const QVariantMap &map );
private:
//! Position of component within model
@ -232,6 +256,18 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
*/
void setParameterName( const QString &name ) { mParameterName = name; }
/**
* Saves this parameter to a QVariant.
* \see loadVariant()
*/
QVariant toVariant() const;
/**
* Loads this parameter from a QVariantMap.
* \see toVariant()
*/
bool loadVariant( const QVariantMap &map );
private:
QString mParameterName;
@ -277,6 +313,18 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
*/
void setOutputName( const QString &name ) { mOutputName = name; }
/**
* Saves this output to a QVariant.
* \see loadVariant()
*/
QVariant toVariant() const;
/**
* Loads this output from a QVariantMap.
* \see toVariant()
*/
bool loadVariant( const QVariantMap &map );
private:
QString mChildId;
@ -456,6 +504,18 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
*/
void setModelOutputs( const QMap<QString, QgsProcessingModelAlgorithm::ModelOutput> &outputs );
/**
* Saves this child to a QVariant.
* \see loadVariant()
*/
QVariant toVariant() const;
/**
* Loads this child from a QVariant.
* \see toVariant()
*/
bool loadVariant( const QVariant &child );
private:
QString mId;
@ -494,6 +554,18 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) const override;
/**
* Sets the model \a name.
* \see name()
*/
void setName( const QString &name );
/**
* Sets the model \a group.
* \see group()
*/
void setGroup( const QString &group );
/**
* Returns the map of child algorithms contained in the model. The keys
* are the child algorithm ids (see QgsProcessingModelAlgorithm::ChildAlgorithm::childId()).
@ -650,6 +722,18 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
*/
QgsProcessingModelAlgorithm::ModelParameter &parameterComponent( const QString &name );
/**
* Writes the model to a file, at the specified \a path.
* \see fromFile()
*/
bool toFile( const QString &path ) const;
/**
* Reads the model from a file, at the specified \a path.
* \see toFile()
*/
bool fromFile( const QString &path );
private:
QString mModelName;
@ -662,6 +746,24 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
void dependsOnChildAlgorithmsRecursive( const QString &childId, QSet<QString> &depends ) const;
void dependentChildAlgorithmsRecursive( const QString &childId, QSet<QString> &depends ) const;
/**
* Saves this model to a QVariantMap, wrapped in a QVariant.
* You can use QgsXmlUtils::writeVariant to save it to an XML document.
*
* \see loadVariant()
*/
QVariant toVariant() const;
/**
* Loads this model from a QVariantMap, wrapped in a QVariant.
* You can use QgsXmlUtils::readVariant to load it from an XML document.
*
* \see toVariant()
*/
bool loadVariant( const QVariant &model );
friend class TestQgsProcessing;
};
#endif // QGSPROCESSINGMODELALGORITHM_H

View File

@ -132,6 +132,19 @@ QDomElement QgsXmlUtils::writeVariant( const QVariant &value, QDomDocument &doc
break;
}
case QVariant::StringList:
{
QStringList list = value.toStringList();
Q_FOREACH ( const QString &value, list )
{
QDomElement valueElement = writeVariant( value, doc );
element.appendChild( valueElement );
element.setAttribute( QStringLiteral( "type" ), QStringLiteral( "StringList" ) );
}
break;
}
case QVariant::Int:
case QVariant::Bool:
case QVariant::Double:
@ -193,6 +206,17 @@ QVariant QgsXmlUtils::readVariant( const QDomElement &element )
}
return list;
}
else if ( type == QLatin1String( "StringList" ) )
{
QStringList list;
QDomNodeList values = element.childNodes();
for ( int i = 0; i < values.count(); ++i )
{
QDomElement elem = values.at( i ).toElement();
list.append( readVariant( elem ).toString() );
}
return list;
}
else
{
return QVariant();

View File

@ -32,6 +32,7 @@
#include "qgsgeometry.h"
#include "qgsvectorfilewriter.h"
#include "qgsexpressioncontext.h"
#include "qgsxmlutils.h"
class DummyAlgorithm : public QgsProcessingAlgorithm
{
@ -3065,6 +3066,11 @@ void TestQgsProcessing::modelerAlgorithm()
QCOMPARE( alg.name(), QStringLiteral( "test" ) );
QCOMPARE( alg.displayName(), QStringLiteral( "test" ) );
QCOMPARE( alg.group(), QStringLiteral( "testGroup" ) );
alg.setName( QStringLiteral( "test2" ) );
QCOMPARE( alg.name(), QStringLiteral( "test2" ) );
QCOMPARE( alg.displayName(), QStringLiteral( "test2" ) );
alg.setGroup( QStringLiteral( "group2" ) );
QCOMPARE( alg.group(), QStringLiteral( "group2" ) );
// child algorithms
QMap<QString, QgsProcessingModelAlgorithm::ChildAlgorithm> algs;
@ -3299,6 +3305,90 @@ void TestQgsProcessing::modelerAlgorithm()
c10.addParameterSource( "y", QgsProcessingModelAlgorithm::ChildParameterSource::fromModelParameter( "p1" ) );
alg4.setChildAlgorithm( c10 );
QVERIFY( alg4.childAlgorithmsDependOnParameter( "p1" ) );
// to/from XML
QgsProcessingModelAlgorithm alg5( "test", "testGroup" );
QgsProcessingModelAlgorithm::ChildAlgorithm alg5c1;
alg5c1.setChildId( "cx1" );
alg5c1.setAlgorithmId( "buffer" );
alg5c1.addParameterSource( "x", QgsProcessingModelAlgorithm::ChildParameterSource::fromModelParameter( "p1" ) );
alg5c1.addParameterSource( "y", QgsProcessingModelAlgorithm::ChildParameterSource::fromChildOutput( "cx2", "out3" ) );
alg5c1.addParameterSource( "z", QgsProcessingModelAlgorithm::ChildParameterSource::fromStaticValue( 5 ) );
alg5c1.setActive( true );
alg5c1.setOutputsCollapsed( true );
alg5c1.setParametersCollapsed( true );
alg5c1.setDescription( "child 1" );
alg5c1.setPosition( QPointF( 1, 2 ) );
QMap<QString, QgsProcessingModelAlgorithm::ModelOutput> alg5c1outputs;
QgsProcessingModelAlgorithm::ModelOutput alg5c1out1;
alg5c1out1.setDescription( QStringLiteral( "my output" ) );
alg5c1out1.setPosition( QPointF( 3, 4 ) );
alg5c1outputs.insert( QStringLiteral( "a" ), alg5c1out1 );
alg5c1.setModelOutputs( alg5c1outputs );
alg5.addChildAlgorithm( alg5c1 );
QgsProcessingModelAlgorithm::ChildAlgorithm alg5c2;
alg5c2.setChildId( "cx2" );
alg5c2.setActive( false );
alg5c2.setOutputsCollapsed( false );
alg5c2.setParametersCollapsed( false );
alg5c2.setDependencies( QStringList() << "a" << "b" );
alg5.addChildAlgorithm( alg5c2 );
QMap<QString, QgsProcessingModelAlgorithm::ModelParameter> alg5pComponents;
QgsProcessingModelAlgorithm::ModelParameter alg5pc1;
alg5pc1.setParameterName( QStringLiteral( "my_param" ) );
alg5pc1.setPosition( QPointF( 11, 12 ) );
alg5pComponents.insert( QStringLiteral( "my_param" ), alg5pc1 );
alg5.setParameterComponents( alg5pComponents );
QDomDocument doc = QDomDocument( "model" );
QDomElement elem = QgsXmlUtils::writeVariant( alg5.toVariant(), doc );
doc.appendChild( elem );
QgsProcessingModelAlgorithm alg6;
QVERIFY( alg6.loadVariant( QgsXmlUtils::readVariant( doc.firstChildElement() ) ) );
QCOMPARE( alg6.name(), QStringLiteral( "test" ) );
QCOMPARE( alg6.group(), QStringLiteral( "testGroup" ) );
QgsProcessingModelAlgorithm::ChildAlgorithm alg6c1 = alg6.childAlgorithm( "cx1" );
QCOMPARE( alg6c1.childId(), QStringLiteral( "cx1" ) );
QCOMPARE( alg6c1.algorithmId(), QStringLiteral( "buffer" ) );
QVERIFY( alg6c1.isActive() );
QVERIFY( alg6c1.outputsCollapsed() );
QVERIFY( alg6c1.parametersCollapsed() );
QCOMPARE( alg6c1.description(), QStringLiteral( "child 1" ) );
QCOMPARE( alg6c1.position().x(), 1.0 );
QCOMPARE( alg6c1.position().y(), 2.0 );
QCOMPARE( alg6c1.parameterSources().count(), 3 );
QCOMPARE( alg6c1.parameterSources().value( "x" ).source(), QgsProcessingModelAlgorithm::ChildParameterSource::ModelParameter );
QCOMPARE( alg6c1.parameterSources().value( "x" ).parameterName(), QStringLiteral( "p1" ) );
QCOMPARE( alg6c1.parameterSources().value( "y" ).source(), QgsProcessingModelAlgorithm::ChildParameterSource::ChildOutput );
QCOMPARE( alg6c1.parameterSources().value( "y" ).outputChildId(), QStringLiteral( "cx2" ) );
QCOMPARE( alg6c1.parameterSources().value( "y" ).outputName(), QStringLiteral( "out3" ) );
QCOMPARE( alg6c1.parameterSources().value( "z" ).source(), QgsProcessingModelAlgorithm::ChildParameterSource::StaticValue );
QCOMPARE( alg6c1.parameterSources().value( "z" ).staticValue().toInt(), 5 );
QCOMPARE( alg6c1.modelOutputs().count(), 1 );
QCOMPARE( alg6c1.modelOutputs().value( QStringLiteral( "a" ) ).description(), QStringLiteral( "my output" ) );
QCOMPARE( alg6c1.modelOutput( "a" ).description(), QStringLiteral( "my output" ) );
QCOMPARE( alg6c1.modelOutput( "a" ).position().x(), 3.0 );
QCOMPARE( alg6c1.modelOutput( "a" ).position().y(), 4.0 );
QgsProcessingModelAlgorithm::ChildAlgorithm alg6c2 = alg6.childAlgorithm( "cx2" );
QCOMPARE( alg6c2.childId(), QStringLiteral( "cx2" ) );
QVERIFY( !alg6c2.isActive() );
QVERIFY( !alg6c2.outputsCollapsed() );
QVERIFY( !alg6c2.parametersCollapsed() );
QCOMPARE( alg6c2.dependencies(), QStringList() << "a" << "b" );
QCOMPARE( alg6.parameterComponents().count(), 1 );
QCOMPARE( alg6.parameterComponents().value( QStringLiteral( "my_param" ) ).parameterName(), QStringLiteral( "my_param" ) );
QCOMPARE( alg6.parameterComponent( "my_param" ).parameterName(), QStringLiteral( "my_param" ) );
QCOMPARE( alg6.parameterComponent( "my_param" ).position().x(), 11.0 );
QCOMPARE( alg6.parameterComponent( "my_param" ).position().y(), 12.0 );
}
QGSTEST_MAIN( TestQgsProcessing )