mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-15 00:04:00 -04:00
Merge pull request #4770 from nyalldawson/proc
More processing model porting, better invalid geometry feedback
This commit is contained in:
commit
6efa7652ba
@ -142,7 +142,6 @@ Destination project
|
||||
.. seealso:: invalidGeometryCheck()
|
||||
%End
|
||||
|
||||
|
||||
void setInvalidGeometryCallback( SIP_PYCALLABLE / AllowNone / );
|
||||
%Docstring
|
||||
Sets a callback function to use when encountering an invalid geometry and
|
||||
@ -198,16 +197,29 @@ Destination project
|
||||
%Docstring
|
||||
Sets the default ``encoding`` to use for newly created files.
|
||||
.. seealso:: defaultEncoding()
|
||||
%End
|
||||
|
||||
QgsProcessingFeedback *feedback();
|
||||
%Docstring
|
||||
Returns the associated feedback object.
|
||||
.. seealso:: setFeedback()
|
||||
:rtype: QgsProcessingFeedback
|
||||
%End
|
||||
|
||||
void setFeedback( QgsProcessingFeedback *feedback );
|
||||
%Docstring
|
||||
Sets an associated ``feedback`` object. This allows context related functions
|
||||
to report feedback and errors to users and processing logs. While ideally this feedback
|
||||
object should outlive the context, only a weak pointer to ``feedback`` is stored
|
||||
and no errors will occur if feedback is deleted before the context.
|
||||
Ownership of ``feedback`` is not transferred.
|
||||
.. seealso:: setFeedback()
|
||||
%End
|
||||
|
||||
private:
|
||||
QgsProcessingContext( const QgsProcessingContext &other );
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
QFlags<QgsProcessingContext::Flag> operator|(QgsProcessingContext::Flag f1, QFlags<QgsProcessingContext::Flag> f2);
|
||||
|
||||
|
||||
|
@ -566,9 +566,15 @@ Copies are protected to avoid slicing
|
||||
|
||||
virtual QString svgIconPath() const;
|
||||
|
||||
virtual QString shortHelpString() const;
|
||||
|
||||
virtual QString helpUrl() const;
|
||||
|
||||
|
||||
virtual bool canExecute( QString *errorMessage /Out/ = 0 ) const;
|
||||
|
||||
virtual QString asPythonCommand( const QVariantMap ¶meters, QgsProcessingContext &context ) const;
|
||||
|
||||
|
||||
void setName( const QString &name );
|
||||
%Docstring
|
||||
@ -767,6 +773,35 @@ Copies are protected to avoid slicing
|
||||
:rtype: bool
|
||||
%End
|
||||
|
||||
QVariantMap &helpContent();
|
||||
%Docstring
|
||||
Returns the model's help contents (a free-form map of values describing the algorithm's
|
||||
use and metadata).
|
||||
.. seealso:: setHelpContent()
|
||||
:rtype: QVariantMap
|
||||
%End
|
||||
|
||||
|
||||
void setHelpContent( const QVariantMap &contents );
|
||||
%Docstring
|
||||
Sets the model's help ``contents`` (a free-form map of values describing the algorithm's
|
||||
use and metadata).
|
||||
.. seealso:: helpContent()
|
||||
%End
|
||||
|
||||
QString sourceFilePath() const;
|
||||
%Docstring
|
||||
Returns the source file path for the model, if available.
|
||||
.. seealso:: setSourceFilePath()
|
||||
:rtype: str
|
||||
%End
|
||||
|
||||
void setSourceFilePath( const QString &path );
|
||||
%Docstring
|
||||
Sets the source file ``path`` for the model, if available.
|
||||
.. seealso:: sourceFilePath()
|
||||
%End
|
||||
|
||||
protected:
|
||||
|
||||
virtual QVariantMap processAlgorithm( const QVariantMap ¶meters,
|
||||
|
@ -151,6 +151,13 @@ class QgsProcessingUtils
|
||||
:rtype: str
|
||||
%End
|
||||
|
||||
static QString formatHelpMapAsHtml( const QVariantMap &map, const QgsProcessingAlgorithm *algorithm );
|
||||
%Docstring
|
||||
Returns a HTML formatted version of the help text encoded in a variant ``map`` for
|
||||
a specified ``algorithm``.
|
||||
:rtype: str
|
||||
%End
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -75,7 +75,7 @@ class AutoincrementalField(QgisAlgorithm):
|
||||
fields, source.wkbType(), source.sourceCrs())
|
||||
|
||||
features = source.getFeatures()
|
||||
total = total = 100.0 / source.featureCount() if source.featureCount() else 0
|
||||
total = 100.0 / source.featureCount() if source.featureCount() else 0
|
||||
for current, input_feature in enumerate(features):
|
||||
if feedback.isCanceled():
|
||||
break
|
||||
|
@ -341,7 +341,3 @@ def executeAlgorithm(alg, parameters, context=None, feedback=None, model=None):
|
||||
return result, ok
|
||||
#self.convertUnsupportedFormats(context, feedback)
|
||||
#self.runPostExecutionScript(feedback)
|
||||
|
||||
def helpUrl(self):
|
||||
return QgsHelp.helpUrl("processing_algs/{}/{}".format(
|
||||
self.provider().id(), self.id())).toString()
|
||||
|
@ -38,7 +38,7 @@ from qgis.utils import iface
|
||||
from qgis.core import (QgsProject,
|
||||
QgsProcessingFeedback,
|
||||
QgsSettings)
|
||||
|
||||
from qgis.gui import QgsHelp
|
||||
|
||||
from processing.core.ProcessingConfig import ProcessingConfig
|
||||
|
||||
@ -189,7 +189,7 @@ class AlgorithmDialogBase(BASE, WIDGET):
|
||||
|
||||
def setInfo(self, msg, error=False, escape_html=True):
|
||||
if error:
|
||||
self.txtLog.append('<span style="color:red"><br>{}<br></span>'.format(msg, quote=False))
|
||||
self.txtLog.append('<span style="color:red">{}</span><br />'.format(msg, quote=False))
|
||||
elif escape_html:
|
||||
self.txtLog.append(html.escape(msg))
|
||||
else:
|
||||
@ -255,6 +255,10 @@ class AlgorithmDialogBase(BASE, WIDGET):
|
||||
|
||||
def openHelp(self):
|
||||
algHelp = self.alg.helpUrl()
|
||||
if not algHelp:
|
||||
algHelp = QgsHelp.helpUrl("processing_algs/{}/{}".format(
|
||||
self.alg.provider().id(), self.alg.id())).toString()
|
||||
|
||||
if algHelp not in [None, ""]:
|
||||
webbrowser.open(algHelp)
|
||||
|
||||
|
@ -33,7 +33,8 @@ from qgis.PyQt import uic
|
||||
from qgis.PyQt.QtWidgets import QDialog, QTreeWidgetItem
|
||||
|
||||
from qgis.core import (QgsMessageLog,
|
||||
QgsProcessingUtils)
|
||||
QgsProcessingUtils,
|
||||
QgsProcessingParameterDefinition)
|
||||
from processing.modeler.ModelerAlgorithm import ModelerAlgorithm
|
||||
|
||||
pluginPath = os.path.split(os.path.dirname(__file__))[0]
|
||||
@ -55,7 +56,7 @@ class HelpEditionDialog(BASE, WIDGET):
|
||||
self.alg = alg
|
||||
self.descriptions = {}
|
||||
if isinstance(self.alg, ModelerAlgorithm):
|
||||
self.descriptions = self.alg.helpContent
|
||||
self.descriptions = self.alg.helpContent()
|
||||
else:
|
||||
if self.alg.descriptionFile is not None:
|
||||
helpfile = alg.descriptionFile + '.help'
|
||||
@ -90,7 +91,7 @@ class HelpEditionDialog(BASE, WIDGET):
|
||||
s += '<h3>' + param.description() + '</h3>\n'
|
||||
s += '<p>' + self.getDescription(param.name()) + '</p>\n'
|
||||
s += self.tr('<h2>Outputs</h2>\n')
|
||||
for out in self.alg.outputs:
|
||||
for out in self.alg.outputDefinitions():
|
||||
s += '<h3>' + out.description() + '</h3>\n'
|
||||
s += '<p>' + self.getDescription(out.name()) + '</p>\n'
|
||||
return s
|
||||
@ -101,11 +102,14 @@ class HelpEditionDialog(BASE, WIDGET):
|
||||
parametersItem = TreeDescriptionItem(self.tr('Input parameters'), None)
|
||||
self.tree.addTopLevelItem(parametersItem)
|
||||
for param in self.alg.parameterDefinitions():
|
||||
if param.flags() & QgsProcessingParameterDefinition.FlagHidden or param.isDestination():
|
||||
continue
|
||||
|
||||
item = TreeDescriptionItem(param.description(), param.name())
|
||||
parametersItem.addChild(item)
|
||||
outputsItem = TreeDescriptionItem(self.tr('Outputs'), None)
|
||||
self.tree.addTopLevelItem(outputsItem)
|
||||
for out in self.alg.outputs:
|
||||
for out in self.alg.outputDefinitions():
|
||||
item = TreeDescriptionItem(out.description(), out.name())
|
||||
outputsItem.addChild(item)
|
||||
item = TreeDescriptionItem(self.tr('Algorithm created by'), self.ALG_CREATOR)
|
||||
|
@ -174,14 +174,9 @@ class CompoundValue(object):
|
||||
|
||||
class ModelerAlgorithm(QgsProcessingModelAlgorithm):
|
||||
|
||||
CANVAS_SIZE = 4000
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.descriptionFile = None
|
||||
self.helpContent = {}
|
||||
|
||||
# Geoalgorithms in this model. A dict of Algorithm objects, with names as keys
|
||||
self.algs = {}
|
||||
|
||||
@ -229,23 +224,6 @@ class ModelerAlgorithm(QgsProcessingModelAlgorithm):
|
||||
v = value
|
||||
return param.evaluateForModeler(v, self)
|
||||
|
||||
def asPythonCommand(self, parameters, context):
|
||||
if self.descriptionFile:
|
||||
return QgsProcessingAlgorithm.asPythonCommand(self, parameters, context)
|
||||
else:
|
||||
return None
|
||||
|
||||
def helpUrl(self):
|
||||
try:
|
||||
return getHtmlFromDescriptionsDict(self, self.helpContent)
|
||||
except:
|
||||
return None
|
||||
|
||||
def shortHelpString(self):
|
||||
if 'ALG_DESC' in self.helpContent:
|
||||
return str(self.helpContent['ALG_DESC'])
|
||||
return None
|
||||
|
||||
def toPython(self):
|
||||
s = ['##%s=name' % self.name()]
|
||||
for param in list(self.parameterComponents().values()):
|
||||
|
@ -108,7 +108,7 @@ class ModelerAlgorithmProvider(QgsProcessingProvider):
|
||||
alg = ModelerAlgorithm()
|
||||
if alg.fromFile(fullpath):
|
||||
if alg.name():
|
||||
alg.descriptionFile = fullpath
|
||||
alg.setSourceFilePath(fullpath)
|
||||
self.algs.append(alg)
|
||||
else:
|
||||
QgsMessageLog.logMessage(self.tr('Could not load model {0}', 'ModelerAlgorithmProvider').format(descriptionFile),
|
||||
|
@ -279,7 +279,7 @@ class ModelerDialog(BASE, WIDGET):
|
||||
dlg = HelpEditionDialog(alg)
|
||||
dlg.exec_()
|
||||
if dlg.descriptions:
|
||||
self.model.helpContent = dlg.descriptions
|
||||
self.model.setHelpContent(dlg.descriptions)
|
||||
self.hasChanged = True
|
||||
|
||||
def runModel(self):
|
||||
@ -439,8 +439,8 @@ class ModelerDialog(BASE, WIDGET):
|
||||
return
|
||||
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
|
||||
if self.model.sourceFilePath() is not None and not saveAs:
|
||||
filename = self.model.sourceFilePath()
|
||||
else:
|
||||
filename, filter = QFileDialog.getSaveFileName(self,
|
||||
self.tr('Save Model'),
|
||||
@ -449,7 +449,7 @@ class ModelerDialog(BASE, WIDGET):
|
||||
if filename:
|
||||
if not filename.endswith('.model3'):
|
||||
filename += '.model3'
|
||||
self.model.descriptionFile = filename
|
||||
self.model.setSourceFilePath(filename)
|
||||
if filename:
|
||||
if not self.model.toFile(filename):
|
||||
if saveAs:
|
||||
@ -492,8 +492,8 @@ class ModelerDialog(BASE, WIDGET):
|
||||
|
||||
def repaintModel(self, controls=True):
|
||||
self.scene = ModelerScene(self, dialog=self)
|
||||
self.scene.setSceneRect(QRectF(0, 0, ModelerAlgorithm.CANVAS_SIZE,
|
||||
ModelerAlgorithm.CANVAS_SIZE))
|
||||
self.scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE,
|
||||
self.CANVAS_SIZE))
|
||||
self.scene.paintModel(self.model, controls)
|
||||
self.view.setScene(self.scene)
|
||||
|
||||
@ -512,6 +512,7 @@ class ModelerDialog(BASE, WIDGET):
|
||||
if isinstance(pos, QPoint):
|
||||
pos = QPointF(pos)
|
||||
component = QgsProcessingModelAlgorithm.ModelParameter(dlg.param.name())
|
||||
component.setDescription(dlg.param.name())
|
||||
component.setPosition(pos)
|
||||
self.model.addModelParameter(dlg.param, component)
|
||||
self.repaintModel()
|
||||
|
@ -196,6 +196,7 @@ class ModelerGraphicItem(QGraphicsItem):
|
||||
if dlg.param is not None:
|
||||
self.model.removeModelParameter(self.element.parameterName())
|
||||
self.element.setParameterName(dlg.param.name())
|
||||
self.element.setDescription(dlg.param.name())
|
||||
self.model.addModelParameter(dlg.param, self.element)
|
||||
self.text = dlg.param.description()
|
||||
self.update()
|
||||
|
@ -49,8 +49,7 @@ class TestAlg(QgsProcessingAlgorithm):
|
||||
return 'testalg'
|
||||
|
||||
def processAlgorithm(self, parameters, context, feedback):
|
||||
raise GeoAlgorithmExecutionException(
|
||||
self.tr('Exception while processing'))
|
||||
raise GeoAlgorithmExecutionException('Exception while processing')
|
||||
return {}
|
||||
|
||||
|
||||
|
@ -71,27 +71,13 @@ def createContext(feedback=None):
|
||||
"""
|
||||
context = QgsProcessingContext()
|
||||
context.setProject(QgsProject.instance())
|
||||
context.setFeedback(feedback)
|
||||
|
||||
invalid_features_method = ProcessingConfig.getSetting(ProcessingConfig.FILTER_INVALID_GEOMETRIES)
|
||||
if invalid_features_method is None:
|
||||
invalid_features_method = QgsFeatureRequest.GeometryAbortOnInvalid
|
||||
context.setInvalidGeometryCheck(invalid_features_method)
|
||||
|
||||
def raise_invalid_geometry_error(f, feedback=feedback):
|
||||
if feedback:
|
||||
feedback.pushInfo(QCoreApplication.translate("FeatureIterator",
|
||||
'Feature with id {} has invalid geometry, skipping feature.'.format(f.id())))
|
||||
|
||||
if context.invalidGeometryCheck() == QgsFeatureRequest.GeometrySkipInvalid:
|
||||
context.setInvalidGeometryCallback(raise_invalid_geometry_error)
|
||||
|
||||
def raise_transform_error(f, feedback=feedback):
|
||||
if feedback:
|
||||
feedback.pushInfo(QCoreApplication.translate("FeatureIterator",
|
||||
'Encountered a transform error when reprojecting feature with id {}.'.format(f.id())))
|
||||
|
||||
context.setTransformErrorCallback(raise_transform_error)
|
||||
|
||||
settings = QgsSettings()
|
||||
context.setDefaultEncoding(settings.value("/Processing/encoding", "System"))
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "qgsfeaturerequest.h"
|
||||
#include "qgsmaplayerlistutils.h"
|
||||
#include "qgsexception.h"
|
||||
#include "qgsprocessingfeedback.h"
|
||||
|
||||
/**
|
||||
* \class QgsProcessingContext
|
||||
@ -50,7 +51,15 @@ class CORE_EXPORT QgsProcessingContext
|
||||
/**
|
||||
* Constructor for QgsProcessingContext.
|
||||
*/
|
||||
QgsProcessingContext() = default;
|
||||
QgsProcessingContext()
|
||||
{
|
||||
auto callback = [ = ]( const QgsFeature & feature )
|
||||
{
|
||||
if ( mFeedback )
|
||||
mFeedback->reportError( QObject::tr( "Encountered a transform error when reprojecting feature with id %1." ).arg( feature.id() ) );
|
||||
};
|
||||
mTransformErrorCallback = callback;
|
||||
}
|
||||
|
||||
//! QgsProcessingContext cannot be copied
|
||||
QgsProcessingContext( const QgsProcessingContext &other ) = delete;
|
||||
@ -176,17 +185,34 @@ class CORE_EXPORT QgsProcessingContext
|
||||
{
|
||||
mInvalidGeometryCheck = check;
|
||||
|
||||
if ( mInvalidGeometryCheck == QgsFeatureRequest::GeometryAbortOnInvalid )
|
||||
switch ( mInvalidGeometryCheck )
|
||||
{
|
||||
auto callback = []( const QgsFeature & feature )
|
||||
case QgsFeatureRequest::GeometryAbortOnInvalid:
|
||||
{
|
||||
throw QgsProcessingException( QObject::tr( "Feature (%1) has invalid geometry. Please fix the geometry or change the Processing setting to the \"Ignore invalid input features\" option." ).arg( feature.id() ) );
|
||||
};
|
||||
mInvalidGeometryCallback = callback;
|
||||
auto callback = []( const QgsFeature & feature )
|
||||
{
|
||||
throw QgsProcessingException( QObject::tr( "Feature (%1) has invalid geometry. Please fix the geometry or change the Processing setting to the \"Ignore invalid input features\" option." ).arg( feature.id() ) );
|
||||
};
|
||||
mInvalidGeometryCallback = callback;
|
||||
break;
|
||||
}
|
||||
|
||||
case QgsFeatureRequest::GeometrySkipInvalid:
|
||||
{
|
||||
auto callback = [ = ]( const QgsFeature & feature )
|
||||
{
|
||||
if ( mFeedback )
|
||||
mFeedback->reportError( QObject::tr( "Feature (%1) has invalid geometry and has been skipped. Please fix the geometry or change the Processing setting to the \"Ignore invalid input features\" option." ).arg( feature.id() ) );
|
||||
};
|
||||
mInvalidGeometryCallback = callback;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets a callback function to use when encountering an invalid geometry and
|
||||
* invalidGeometryCheck() is set to GeometryAbortOnInvalid. This function will be
|
||||
@ -268,6 +294,22 @@ class CORE_EXPORT QgsProcessingContext
|
||||
*/
|
||||
void setDefaultEncoding( const QString &encoding ) { mDefaultEncoding = encoding; }
|
||||
|
||||
/**
|
||||
* Returns the associated feedback object.
|
||||
* \see setFeedback()
|
||||
*/
|
||||
QgsProcessingFeedback *feedback() { return mFeedback; }
|
||||
|
||||
/**
|
||||
* Sets an associated \a feedback object. This allows context related functions
|
||||
* to report feedback and errors to users and processing logs. While ideally this feedback
|
||||
* object should outlive the context, only a weak pointer to \a feedback is stored
|
||||
* and no errors will occur if feedback is deleted before the context.
|
||||
* Ownership of \a feedback is not transferred.
|
||||
* \see setFeedback()
|
||||
*/
|
||||
void setFeedback( QgsProcessingFeedback *feedback ) { mFeedback = feedback; }
|
||||
|
||||
private:
|
||||
|
||||
QgsProcessingContext::Flags mFlags = 0;
|
||||
@ -282,15 +324,13 @@ class CORE_EXPORT QgsProcessingContext
|
||||
QString mDefaultEncoding;
|
||||
QMap< QString, LayerDetails > mLayersToLoadOnCompletion;
|
||||
|
||||
QPointer< QgsProcessingFeedback > mFeedback;
|
||||
|
||||
#ifdef SIP_RUN
|
||||
QgsProcessingContext( const QgsProcessingContext &other );
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsProcessingContext::Flags )
|
||||
|
||||
#endif // QGSPROCESSINGPARAMETERS_H
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "qgsprocessingmodelalgorithm.h"
|
||||
#include "qgsprocessingregistry.h"
|
||||
#include "qgsprocessingfeedback.h"
|
||||
#include "qgsprocessingutils.h"
|
||||
#include "qgsxmlutils.h"
|
||||
#include "qgsexception.h"
|
||||
#include <QFile>
|
||||
@ -292,6 +293,18 @@ QString QgsProcessingModelAlgorithm::svgIconPath() const
|
||||
return QgsApplication::iconPath( QStringLiteral( "processingModel.svg" ) );
|
||||
}
|
||||
|
||||
QString QgsProcessingModelAlgorithm::shortHelpString() const
|
||||
{
|
||||
if ( mHelpContent.contains( QStringLiteral( "ALG_DESC" ) ) )
|
||||
return mHelpContent.value( QStringLiteral( "ALG_DESC" ) ).toString();
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString QgsProcessingModelAlgorithm::helpUrl() const
|
||||
{
|
||||
return QgsProcessingUtils::formatHelpMapAsHtml( mHelpContent, this );
|
||||
}
|
||||
|
||||
QVariantMap QgsProcessingModelAlgorithm::parametersForChildAlgorithm( const ChildAlgorithm &child, const QVariantMap &modelParameters, const QMap< QString, QVariantMap > &results ) const
|
||||
{
|
||||
QVariantMap childParams;
|
||||
@ -468,6 +481,26 @@ QVariantMap QgsProcessingModelAlgorithm::processAlgorithm( const QVariantMap &pa
|
||||
return finalResults;
|
||||
}
|
||||
|
||||
QString QgsProcessingModelAlgorithm::sourceFilePath() const
|
||||
{
|
||||
return mSourceFile;
|
||||
}
|
||||
|
||||
void QgsProcessingModelAlgorithm::setSourceFilePath( const QString &sourceFile )
|
||||
{
|
||||
mSourceFile = sourceFile;
|
||||
}
|
||||
|
||||
QVariantMap QgsProcessingModelAlgorithm::helpContent() const
|
||||
{
|
||||
return mHelpContent;
|
||||
}
|
||||
|
||||
void QgsProcessingModelAlgorithm::setHelpContent( const QVariantMap &helpContent )
|
||||
{
|
||||
mHelpContent = helpContent;
|
||||
}
|
||||
|
||||
void QgsProcessingModelAlgorithm::setName( const QString &name )
|
||||
{
|
||||
mModelName = name;
|
||||
@ -559,6 +592,7 @@ QVariant QgsProcessingModelAlgorithm::toVariant() const
|
||||
QVariantMap map;
|
||||
map.insert( QStringLiteral( "model_name" ), mModelName );
|
||||
map.insert( QStringLiteral( "model_group" ), mModelGroup );
|
||||
map.insert( QStringLiteral( "help" ), mHelpContent );
|
||||
|
||||
QVariantMap childMap;
|
||||
QMap< QString, ChildAlgorithm >::const_iterator childIt = mChildAlgorithms.constBegin();
|
||||
@ -592,6 +626,7 @@ bool QgsProcessingModelAlgorithm::loadVariant( const QVariant &model )
|
||||
|
||||
mModelName = map.value( QStringLiteral( "model_name" ) ).toString();
|
||||
mModelGroup = map.value( QStringLiteral( "model_group" ) ).toString();
|
||||
mHelpContent = map.value( QStringLiteral( "help" ) ).toMap();
|
||||
|
||||
mChildAlgorithms.clear();
|
||||
QVariantMap childMap = map.value( QStringLiteral( "children" ) ).toMap();
|
||||
@ -880,6 +915,14 @@ bool QgsProcessingModelAlgorithm::canExecute( QString *errorMessage ) const
|
||||
return true;
|
||||
}
|
||||
|
||||
QString QgsProcessingModelAlgorithm::asPythonCommand( const QVariantMap ¶meters, QgsProcessingContext &context ) const
|
||||
{
|
||||
if ( mSourceFile.isEmpty() )
|
||||
return QString(); // temporary model - can't run as python command
|
||||
|
||||
return QgsProcessingAlgorithm::asPythonCommand( parameters, context );
|
||||
}
|
||||
|
||||
|
||||
bool QgsProcessingModelAlgorithm::ChildParameterSource::operator==( const QgsProcessingModelAlgorithm::ChildParameterSource &other ) const
|
||||
{
|
||||
|
@ -569,8 +569,11 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
|
||||
QString group() const override;
|
||||
QIcon icon() const override;
|
||||
QString svgIconPath() const override;
|
||||
QString shortHelpString() const override;
|
||||
QString helpUrl() const override;
|
||||
|
||||
bool canExecute( QString *errorMessage SIP_OUT = nullptr ) const override;
|
||||
QString asPythonCommand( const QVariantMap ¶meters, QgsProcessingContext &context ) const override;
|
||||
|
||||
/**
|
||||
* Sets the model \a name.
|
||||
@ -757,6 +760,39 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
|
||||
*/
|
||||
bool fromFile( const QString &path );
|
||||
|
||||
/**
|
||||
* Returns the model's help contents (a free-form map of values describing the algorithm's
|
||||
* use and metadata).
|
||||
* \see setHelpContent()
|
||||
*/
|
||||
QVariantMap &helpContent() { return mHelpContent; }
|
||||
|
||||
/**
|
||||
* Returns the model's help contents (a free-form map of values describing the algorithm's
|
||||
* use and metadata).
|
||||
* \see setHelpContent()
|
||||
*/
|
||||
SIP_SKIP QVariantMap helpContent() const;
|
||||
|
||||
/**
|
||||
* Sets the model's help \a contents (a free-form map of values describing the algorithm's
|
||||
* use and metadata).
|
||||
* \see helpContent()
|
||||
*/
|
||||
void setHelpContent( const QVariantMap &contents );
|
||||
|
||||
/**
|
||||
* Returns the source file path for the model, if available.
|
||||
* \see setSourceFilePath()
|
||||
*/
|
||||
QString sourceFilePath() const;
|
||||
|
||||
/**
|
||||
* Sets the source file \a path for the model, if available.
|
||||
* \see sourceFilePath()
|
||||
*/
|
||||
void setSourceFilePath( const QString &path );
|
||||
|
||||
protected:
|
||||
|
||||
QVariantMap processAlgorithm( const QVariantMap ¶meters,
|
||||
@ -772,6 +808,11 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
|
||||
//! Map of parameter name to model parameter component
|
||||
QMap< QString, ModelParameter > mParameterComponents;
|
||||
|
||||
QVariantMap mHelpContent;
|
||||
|
||||
//! Model source file
|
||||
QString mSourceFile;
|
||||
|
||||
void dependsOnChildAlgorithmsRecursive( const QString &childId, QSet<QString> &depends ) const;
|
||||
void dependentChildAlgorithmsRecursive( const QString &childId, QSet<QString> &depends ) const;
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "qgsvectorfilewriter.h"
|
||||
#include "qgsmemoryproviderutils.h"
|
||||
#include "qgsprocessingparameters.h"
|
||||
#include "qgsprocessingalgorithm.h"
|
||||
|
||||
QList<QgsRasterLayer *> QgsProcessingUtils::compatibleRasterLayers( QgsProject *project, bool sort )
|
||||
{
|
||||
@ -428,6 +429,38 @@ QString QgsProcessingUtils::generateTempFilename( const QString &basename )
|
||||
return path + '/' + basename;
|
||||
}
|
||||
|
||||
QString QgsProcessingUtils::formatHelpMapAsHtml( const QVariantMap &map, const QgsProcessingAlgorithm *algorithm )
|
||||
{
|
||||
auto getText = [map]( const QString & key )->QString
|
||||
{
|
||||
if ( map.contains( key ) )
|
||||
return map.value( key ).toString();
|
||||
return QString();
|
||||
};
|
||||
|
||||
QString s = QObject::tr( "<html><body><h2>Algorithm description</h2>\n " );
|
||||
s += QStringLiteral( "<p>" ) + getText( QStringLiteral( "ALG_DESC" ) ) + QStringLiteral( "</p>\n" );
|
||||
s += QObject::tr( "<h2>Input parameters</h2>\n" );
|
||||
|
||||
Q_FOREACH ( const QgsProcessingParameterDefinition *def, algorithm->parameterDefinitions() )
|
||||
{
|
||||
s += QStringLiteral( "<h3>" ) + def->description() + QStringLiteral( "</h3>\n" );
|
||||
s += QStringLiteral( "<p>" ) + getText( def->name() ) + QStringLiteral( "</p>\n" );
|
||||
}
|
||||
s += QObject::tr( "<h2>Outputs</h2>\n" );
|
||||
Q_FOREACH ( const QgsProcessingOutputDefinition *def, algorithm->outputDefinitions() )
|
||||
{
|
||||
s += QStringLiteral( "<h3>" ) + def->description() + QStringLiteral( "</h3>\n" );
|
||||
s += QStringLiteral( "<p>" ) + getText( def->name() ) + QStringLiteral( "</p>\n" );
|
||||
}
|
||||
s += "<br>";
|
||||
s += QObject::tr( "<p align=\"right\">Algorithm author: %1</p>" ).arg( getText( QStringLiteral( "ALG_CREATOR" ) ) );
|
||||
s += QObject::tr( "<p align=\"right\">Help author: %1</p>" ).arg( getText( QStringLiteral( "ALG_HELP_CREATOR" ) ) );
|
||||
s += QObject::tr( "<p align=\"right\">Algorithm version: %1</p>" ).arg( getText( QStringLiteral( "ALG_VERSION" ) ) );
|
||||
s += QStringLiteral( "</body></html>" );
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// QgsProcessingFeatureSource
|
||||
|
@ -183,6 +183,12 @@ class CORE_EXPORT QgsProcessingUtils
|
||||
*/
|
||||
static QString generateTempFilename( const QString &basename );
|
||||
|
||||
/**
|
||||
* Returns a HTML formatted version of the help text encoded in a variant \a map for
|
||||
* a specified \a algorithm.
|
||||
*/
|
||||
static QString formatHelpMapAsHtml( const QVariantMap &map, const QgsProcessingAlgorithm *algorithm );
|
||||
|
||||
private:
|
||||
|
||||
static bool canUseLayer( const QgsRasterLayer *layer );
|
||||
|
@ -3732,6 +3732,8 @@ void TestQgsProcessing::modelerAlgorithm()
|
||||
|
||||
// to/from XML
|
||||
QgsProcessingModelAlgorithm alg5( "test", "testGroup" );
|
||||
alg5.helpContent().insert( "author", "me" );
|
||||
alg5.helpContent().insert( "usage", "run" );
|
||||
QgsProcessingModelAlgorithm::ChildAlgorithm alg5c1;
|
||||
alg5c1.setChildId( "cx1" );
|
||||
alg5c1.setAlgorithmId( "buffer" );
|
||||
@ -3772,6 +3774,7 @@ void TestQgsProcessing::modelerAlgorithm()
|
||||
QVERIFY( alg6.loadVariant( QgsXmlUtils::readVariant( doc.firstChildElement() ) ) );
|
||||
QCOMPARE( alg6.name(), QStringLiteral( "test" ) );
|
||||
QCOMPARE( alg6.group(), QStringLiteral( "testGroup" ) );
|
||||
QCOMPARE( alg6.helpContent(), alg5.helpContent() );
|
||||
QgsProcessingModelAlgorithm::ChildAlgorithm alg6c1 = alg6.childAlgorithm( "cx1" );
|
||||
QCOMPARE( alg6c1.childId(), QStringLiteral( "cx1" ) );
|
||||
QCOMPARE( alg6c1.algorithmId(), QStringLiteral( "buffer" ) );
|
||||
|
Loading…
x
Reference in New Issue
Block a user