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() .. seealso:: setOutputChildId()
%End %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 class Component
@ -192,6 +206,18 @@ Copies are protected to avoid slicing
%End %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 class ModelParameter : QgsProcessingModelAlgorithm::Component
@ -227,6 +253,20 @@ Copies are protected to avoid slicing
.. seealso:: parameterName() .. seealso:: parameterName()
%End %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() .. seealso:: outputName()
%End %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 class ChildAlgorithm : QgsProcessingModelAlgorithm::Component
@ -461,6 +515,20 @@ Copies are protected to avoid slicing
.. seealso:: modelOutputs() .. seealso:: modelOutputs()
%End %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() ); 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, virtual QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) const; 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; QMap<QString, QgsProcessingModelAlgorithm::ChildAlgorithm> childAlgorithms() const;
%Docstring %Docstring
Returns the map of child algorithms contained in the model. The keys 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 :rtype: QgsProcessingModelAlgorithm.ModelParameter
%End %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('Open model', 'AddModelFromFileAction'), lastDir,
self.tr('Processing model files (*.model *.MODEL)', 'AddModelFromFileAction')) self.tr('Processing model files (*.model *.MODEL)', 'AddModelFromFileAction'))
if filename: if filename:
try: settings.setValue('Processing/lastModelsDir',
settings.setValue('Processing/lastModelsDir', QFileInfo(filename).absoluteDir().absolutePath())
QFileInfo(filename).absoluteDir().absolutePath())
ModelerAlgorithm.fromFile(filename) alg = ModelerAlgorithm()
except WrongModelException: if not alg.fromFile(filename):
QMessageBox.warning( QMessageBox.warning(
self.toolbox, self.toolbox,
self.tr('Error reading model', 'AddModelFromFileAction'), self.tr('Error reading model', 'AddModelFromFileAction'),
self.tr('The selected file does not contain a valid model', 'AddModelFromFileAction')) self.tr('The selected file does not contain a valid model', 'AddModelFromFileAction'))
return 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)) destFilename = os.path.join(ModelerUtils.modelsFolders()[0], os.path.basename(filename))
shutil.copyfile(filename, destFilename) shutil.copyfile(filename, destFilename)
QgsApplication.processingRegistry().providerById('model').refreshAlgorithms() QgsApplication.processingRegistry().providerById('model').refreshAlgorithms()

View File

@ -365,15 +365,6 @@ class ModelerAlgorithm(QgsProcessingModelAlgorithm):
model._name = model.modeler_name model._name = model.modeler_name
return model 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): def toPython(self):
s = ['##%s=name' % self.name()] s = ['##%s=name' % self.name()]
for param in list(self.parameterComponents().values()): for param in list(self.parameterComponents().values()):

View File

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

View File

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

View File

@ -17,6 +17,9 @@
#include "qgsprocessingmodelalgorithm.h" #include "qgsprocessingmodelalgorithm.h"
#include "qgsprocessingregistry.h" #include "qgsprocessingregistry.h"
#include "qgsxmlutils.h"
#include <QFile>
#include <QTextStream>
QgsProcessingModelAlgorithm::ChildAlgorithm::ChildAlgorithm( const QString &algorithmId ) QgsProcessingModelAlgorithm::ChildAlgorithm::ChildAlgorithm( const QString &algorithmId )
: mAlgorithmId( algorithmId ) : mAlgorithmId( algorithmId )
@ -78,6 +81,22 @@ QgsProcessingModelAlgorithm::Component::Component( const QString &description )
: mDescription( 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 QStringList QgsProcessingModelAlgorithm::ChildAlgorithm::dependencies() const
{ {
return mDependencies; return mDependencies;
@ -113,6 +132,77 @@ void QgsProcessingModelAlgorithm::ChildAlgorithm::setModelOutputs( const QMap<QS
mModelOutputs = modelOutputs; 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 bool QgsProcessingModelAlgorithm::ChildAlgorithm::parametersCollapsed() const
{ {
return mParametersCollapsed; return mParametersCollapsed;
@ -201,6 +291,16 @@ QVariantMap QgsProcessingModelAlgorithm::processAlgorithm( const QVariantMap &pa
return QVariantMap(); 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 QMap<QString, QgsProcessingModelAlgorithm::ChildAlgorithm> QgsProcessingModelAlgorithm::childAlgorithms() const
{ {
return mChildAlgorithms; return mChildAlgorithms;
@ -227,6 +327,99 @@ QgsProcessingModelAlgorithm::ModelParameter &QgsProcessingModelAlgorithm::parame
return mParameterComponents[ name ]; 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 ) void QgsProcessingModelAlgorithm::setChildAlgorithms( const QMap<QString, ChildAlgorithm> &childAlgorithms )
{ {
mChildAlgorithms = childAlgorithms; mChildAlgorithms = childAlgorithms;
@ -484,13 +677,91 @@ QgsProcessingModelAlgorithm::ChildParameterSource::Source QgsProcessingModelAlgo
return mSource; 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::ModelOutput::ModelOutput( const QString &description )
: QgsProcessingModelAlgorithm::Component( 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::ModelParameter::ModelParameter( const QString &parameterName )
: QgsProcessingModelAlgorithm::Component() : QgsProcessingModelAlgorithm::Component()
, mParameterName( parameterName ) , 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; } 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: private:
Source mSource = StaticValue; Source mSource = StaticValue;
@ -194,6 +206,18 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
//! Copies are protected to avoid slicing //! Copies are protected to avoid slicing
Component &operator=( const QgsProcessingModelAlgorithm::Component &other ) = default; 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: private:
//! Position of component within model //! Position of component within model
@ -232,6 +256,18 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
*/ */
void setParameterName( const QString &name ) { mParameterName = name; } 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: private:
QString mParameterName; QString mParameterName;
@ -277,6 +313,18 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
*/ */
void setOutputName( const QString &name ) { mOutputName = name; } 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: private:
QString mChildId; QString mChildId;
@ -456,6 +504,18 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
*/ */
void setModelOutputs( const QMap<QString, QgsProcessingModelAlgorithm::ModelOutput> &outputs ); 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: private:
QString mId; QString mId;
@ -494,6 +554,18 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
QVariantMap processAlgorithm( const QVariantMap &parameters, QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) const override; 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 * Returns the map of child algorithms contained in the model. The keys
* are the child algorithm ids (see QgsProcessingModelAlgorithm::ChildAlgorithm::childId()). * 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 ); 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: private:
QString mModelName; QString mModelName;
@ -662,6 +746,24 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
void dependsOnChildAlgorithmsRecursive( const QString &childId, QSet<QString> &depends ) const; void dependsOnChildAlgorithmsRecursive( const QString &childId, QSet<QString> &depends ) const;
void dependentChildAlgorithmsRecursive( 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 #endif // QGSPROCESSINGMODELALGORITHM_H

View File

@ -132,6 +132,19 @@ QDomElement QgsXmlUtils::writeVariant( const QVariant &value, QDomDocument &doc
break; 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::Int:
case QVariant::Bool: case QVariant::Bool:
case QVariant::Double: case QVariant::Double:
@ -193,6 +206,17 @@ QVariant QgsXmlUtils::readVariant( const QDomElement &element )
} }
return list; 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 else
{ {
return QVariant(); return QVariant();

View File

@ -32,6 +32,7 @@
#include "qgsgeometry.h" #include "qgsgeometry.h"
#include "qgsvectorfilewriter.h" #include "qgsvectorfilewriter.h"
#include "qgsexpressioncontext.h" #include "qgsexpressioncontext.h"
#include "qgsxmlutils.h"
class DummyAlgorithm : public QgsProcessingAlgorithm class DummyAlgorithm : public QgsProcessingAlgorithm
{ {
@ -3065,6 +3066,11 @@ void TestQgsProcessing::modelerAlgorithm()
QCOMPARE( alg.name(), QStringLiteral( "test" ) ); QCOMPARE( alg.name(), QStringLiteral( "test" ) );
QCOMPARE( alg.displayName(), QStringLiteral( "test" ) ); QCOMPARE( alg.displayName(), QStringLiteral( "test" ) );
QCOMPARE( alg.group(), QStringLiteral( "testGroup" ) ); 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 // child algorithms
QMap<QString, QgsProcessingModelAlgorithm::ChildAlgorithm> algs; QMap<QString, QgsProcessingModelAlgorithm::ChildAlgorithm> algs;
@ -3299,6 +3305,90 @@ void TestQgsProcessing::modelerAlgorithm()
c10.addParameterSource( "y", QgsProcessingModelAlgorithm::ChildParameterSource::fromModelParameter( "p1" ) ); c10.addParameterSource( "y", QgsProcessingModelAlgorithm::ChildParameterSource::fromModelParameter( "p1" ) );
alg4.setChildAlgorithm( c10 ); alg4.setChildAlgorithm( c10 );
QVERIFY( alg4.childAlgorithmsDependOnParameter( "p1" ) ); 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 ) QGSTEST_MAIN( TestQgsProcessing )