Skip to content

Commit

Permalink
Merge pull request #4770 from nyalldawson/proc
Browse files Browse the repository at this point in the history
More processing model porting, better invalid geometry feedback
  • Loading branch information
nyalldawson committed Jun 24, 2017
2 parents b7e66b4 + d667bf5 commit 6efa765
Show file tree
Hide file tree
Showing 19 changed files with 262 additions and 73 deletions.
22 changes: 17 additions & 5 deletions python/core/processing/qgsprocessingcontext.sip
Expand Up @@ -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
Expand Down Expand Up @@ -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);


Expand Down
35 changes: 35 additions & 0 deletions python/core/processing/qgsprocessingmodelalgorithm.sip
Expand Up @@ -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 &parameters, QgsProcessingContext &context ) const;


void setName( const QString &name );
%Docstring
Expand Down Expand Up @@ -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 &parameters,
Expand Down
7 changes: 7 additions & 0 deletions python/core/processing/qgsprocessingutils.sip
Expand Up @@ -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

};


Expand Down
Expand Up @@ -75,7 +75,7 @@ def processAlgorithm(self, parameters, context, feedback):
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
Expand Down
4 changes: 0 additions & 4 deletions python/plugins/processing/core/GeoAlgorithm.py
Expand Up @@ -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()
8 changes: 6 additions & 2 deletions python/plugins/processing/gui/AlgorithmDialogBase.py
Expand Up @@ -38,7 +38,7 @@
from qgis.core import (QgsProject,
QgsProcessingFeedback,
QgsSettings)

from qgis.gui import QgsHelp

from processing.core.ProcessingConfig import ProcessingConfig

Expand Down Expand Up @@ -189,7 +189,7 @@ def resetGUI(self):

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:
Expand Down Expand Up @@ -255,6 +255,10 @@ def splitterChanged(self, pos, index):

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)

Expand Down
12 changes: 8 additions & 4 deletions python/plugins/processing/gui/HelpEditionDialog.py
Expand Up @@ -33,7 +33,8 @@
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]
Expand All @@ -55,7 +56,7 @@ def __init__(self, alg):
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'
Expand Down Expand Up @@ -90,7 +91,7 @@ def getHtml(self):
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
Expand All @@ -101,11 +102,14 @@ def fillTree(self):
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)
Expand Down
22 changes: 0 additions & 22 deletions python/plugins/processing/modeler/ModelerAlgorithm.py
Expand Up @@ -174,14 +174,9 @@ def asPythonParameter(self):

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 = {}

Expand Down Expand Up @@ -229,23 +224,6 @@ def resolveValue(self, value, param):
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()):
Expand Down
Expand Up @@ -108,7 +108,7 @@ def loadFromFolder(self, folder):
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),
Expand Down
13 changes: 7 additions & 6 deletions python/plugins/processing/modeler/ModelerDialog.py
Expand Up @@ -279,7 +279,7 @@ def editHelp(self):
dlg = HelpEditionDialog(alg)
dlg.exec_()
if dlg.descriptions:
self.model.helpContent = dlg.descriptions
self.model.setHelpContent(dlg.descriptions)
self.hasChanged = True

def runModel(self):
Expand Down Expand Up @@ -439,8 +439,8 @@ def saveModel(self, saveAs):
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'),
Expand All @@ -449,7 +449,7 @@ def saveModel(self, saveAs):
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:
Expand Down Expand Up @@ -492,8 +492,8 @@ def openModel(self):

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)

Expand All @@ -512,6 +512,7 @@ def addInputOfType(self, paramType, pos=None):
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()
Expand Down
1 change: 1 addition & 0 deletions python/plugins/processing/modeler/ModelerGraphicItem.py
Expand Up @@ -196,6 +196,7 @@ def editElement(self):
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()
Expand Down
3 changes: 1 addition & 2 deletions python/plugins/processing/tests/QgisAlgorithmsTest.py
Expand Up @@ -49,8 +49,7 @@ def displayName(self):
return 'testalg'

def processAlgorithm(self, parameters, context, feedback):
raise GeoAlgorithmExecutionException(
self.tr('Exception while processing'))
raise GeoAlgorithmExecutionException('Exception while processing')
return {}


Expand Down
16 changes: 1 addition & 15 deletions python/plugins/processing/tools/dataobjects.py
Expand Up @@ -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"))

Expand Down

0 comments on commit 6efa765

Please sign in to comment.