[FEATURE][processsing] Add support for comments attached to components

This allows users to create comments attached to model components (inputs,
algorithms or outputs). Comments are shown linked to the associated component,
and can be freely moved around the model.
This commit is contained in:
Nyall Dawson 2020-03-04 15:29:49 +10:00
parent 542c183f00
commit 13fc85d740
28 changed files with 779 additions and 81 deletions

View File

@ -275,6 +275,9 @@ The ``currentIndent`` and ``indentSize`` are used to set the base line indent an
The ``friendlyChildNames`` argument gives a map of child id to a friendly algorithm name, to be used in the code to identify that algorithm instead of the raw child id.
%End
virtual QgsProcessingModelComment *comment();
virtual void setComment( const QgsProcessingModelComment &comment );
};

View File

@ -0,0 +1,56 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/processing/models/qgsprocessingmodelcomment.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
class QgsProcessingModelComment : QgsProcessingModelComponent
{
%Docstring
Represents a comment in a model.
.. versionadded:: 3.14
%End
%TypeHeaderCode
#include "qgsprocessingmodelcomment.h"
%End
public:
QgsProcessingModelComment( const QString &description = QString() );
%Docstring
Constructor for QgsProcessingModelComment with the specified ``description``.
%End
virtual QgsProcessingModelComment *clone() const /Factory/;
QVariant toVariant() const;
%Docstring
Saves this comment to a QVariant.
.. seealso:: :py:func:`loadVariant`
%End
bool loadVariant( const QVariantMap &map );
%Docstring
Loads this comment from a QVariantMap.
.. seealso:: :py:func:`toVariant`
%End
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/processing/models/qgsprocessingmodelcomment.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/

View File

@ -10,6 +10,7 @@
class QgsProcessingModelComponent
{
%Docstring
@ -86,6 +87,21 @@ in the graphical modeler.
.. seealso:: :py:func:`linksCollapsed`
%End
virtual QgsProcessingModelComment *comment();
%Docstring
Returns the comment attached to this component (may be ``None``)
.. seealso:: :py:func:`setComment`
%End
virtual void setComment( const QgsProcessingModelComment &comment );
%Docstring
Sets the ``comment`` attached to this component.
.. seealso:: :py:func:`comment`
%End
virtual QgsProcessingModelComponent *clone() const = 0 /Factory/;
%Docstring
Clones the component.

View File

@ -127,6 +127,9 @@ Loads this output from a QVariantMap.
.. seealso:: :py:func:`toVariant`
%End
virtual QgsProcessingModelComment *comment();
virtual void setComment( const QgsProcessingModelComment &comment );
};

View File

@ -63,6 +63,9 @@ Loads this parameter from a QVariantMap.
.. seealso:: :py:func:`toVariant`
%End
virtual QgsProcessingModelComment *comment();
virtual void setComment( const QgsProcessingModelComment &comment );
};

View File

@ -415,6 +415,7 @@
%Include auto_generated/processing/models/qgsprocessingmodelalgorithm.sip
%Include auto_generated/processing/models/qgsprocessingmodelchildalgorithm.sip
%Include auto_generated/processing/models/qgsprocessingmodelchildparametersource.sip
%Include auto_generated/processing/models/qgsprocessingmodelcomment.sip
%Include auto_generated/processing/models/qgsprocessingmodelcomponent.sip
%Include auto_generated/processing/models/qgsprocessingmodeloutput.sip
%Include auto_generated/processing/models/qgsprocessingmodelparameter.sip

View File

@ -147,6 +147,13 @@ Returns the best link point to use for a link originating at a specified ``other
- edge: item edge for calculated best link point
%End
virtual void editComment();
%Docstring
Called when the comment attached to the item should be edited.
The default implementation does nothing.
%End
signals:
@ -208,6 +215,11 @@ Returns the stroke color for the item for the specified ``state``.
virtual QColor textColor( State state ) const = 0;
%Docstring
Returns the label text color for the item for the specified ``state``.
%End
virtual Qt::PenStyle strokeStyle( State state ) const;
%Docstring
Returns the stroke style to use while rendering the outline of the item.
%End
virtual QPicture iconPicture() const;
@ -218,6 +230,11 @@ Returns a QPicture version of the item's icon, if available.
virtual QPixmap iconPixmap() const;
%Docstring
Returns a QPixmap version of the item's icon, if available.
%End
virtual void updateStoredComponentPosition( const QPointF &pos ) = 0;
%Docstring
Updates the position stored in the model for the associated comment
%End
};
@ -264,6 +281,8 @@ Ownership of ``parameter`` is transferred to the item.
virtual QPicture iconPicture() const;
virtual void updateStoredComponentPosition( const QPointF &pos );
protected slots:
@ -320,6 +339,8 @@ Ownership of ``child`` is transferred to the item.
virtual QString linkPointText( Qt::Edge edge, int index ) const;
virtual void updateStoredComponentPosition( const QPointF &pos );
protected slots:
@ -368,6 +389,8 @@ Ownership of ``output`` is transferred to the item.
virtual QPicture iconPicture() const;
virtual void updateStoredComponentPosition( const QPointF &pos );
protected slots:
@ -376,6 +399,62 @@ Ownership of ``output`` is transferred to the item.
};
class QgsModelCommentGraphicItem : QgsModelComponentGraphicItem
{
%Docstring
A graphic item representing a model comment in the model designer.
.. warning::
Not stable API
.. versionadded:: 3.14
%End
%TypeHeaderCode
#include "qgsmodelcomponentgraphicitem.h"
%End
public:
QgsModelCommentGraphicItem( QgsProcessingModelComment *comment /Transfer/,
QgsModelComponentGraphicItem *parentItem,
QgsProcessingModelAlgorithm *model,
QGraphicsItem *parent /TransferThis/ );
%Docstring
Constructor for QgsModelCommentGraphicItem for the specified ``comment``, with the specified ``parent`` item.
The ``model`` argument specifies the associated processing model. Ownership of ``model`` is not transferred, and
it must exist for the lifetime of this object.
Ownership of ``output`` is transferred to the item.
%End
~QgsModelCommentGraphicItem();
virtual void contextMenuEvent( QGraphicsSceneContextMenuEvent *event );
protected:
virtual QColor fillColor( State state ) const;
virtual QColor strokeColor( State state ) const;
virtual QColor textColor( State state ) const;
virtual Qt::PenStyle strokeStyle( State state ) const;
virtual void updateStoredComponentPosition( const QPointF &pos );
protected slots:
virtual void deleteComponent();
virtual void editComponent();
};
/************************************************************************
* This file has been generated automatically from *
* *

View File

@ -110,6 +110,13 @@ Creates a new graphic item for a model child algorithm.
Creates a new graphic item for a model output.
%End
virtual QgsModelComponentGraphicItem *createCommentGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelComment *comment,
QgsModelComponentGraphicItem *parentItem ) const /Factory/;
%Docstring
Creates a new graphic item for a model comment.
%End
};

View File

@ -29,7 +29,8 @@ from qgis.gui import (
QgsProcessingParameterWidgetContext,
QgsModelParameterGraphicItem,
QgsModelChildAlgorithmGraphicItem,
QgsModelOutputGraphicItem
QgsModelOutputGraphicItem,
QgsModelCommentGraphicItem
)
from processing.modeler.ModelerParameterDefinitionDialog import ModelerParameterDefinitionDialog
from processing.modeler.ModelerParametersDialog import ModelerParametersDialog
@ -59,15 +60,20 @@ class ModelerInputGraphicItem(QgsModelParameterGraphicItem):
widget_context.setModel(self.model())
return widget_context
def editComponent(self):
def edit(self, edit_comment=False):
existing_param = self.model().parameterDefinition(self.component().parameterName())
comment = self.component().comment().description()
new_param = None
if ModelerParameterDefinitionDialog.use_legacy_dialog(param=existing_param):
# boo, old api
dlg = ModelerParameterDefinitionDialog(self.model(),
param=existing_param)
dlg.setComments(comment)
if edit_comment:
dlg.switchToCommentTab()
if dlg.exec_():
new_param = dlg.param
comment = dlg.comments()
else:
# yay, use new API!
context = createContext()
@ -84,11 +90,18 @@ class ModelerInputGraphicItem(QgsModelParameterGraphicItem):
self.model().removeModelParameter(self.component().parameterName())
self.component().setParameterName(new_param.name())
self.component().setDescription(new_param.name())
self.component().comment().setDescription(comment)
self.model().addModelParameter(new_param, self.component())
self.setLabel(new_param.description())
self.requestModelRepaint.emit()
self.changed.emit()
def editComponent(self):
self.edit()
def editComment(self):
self.edit(edit_comment=True)
class ModelerChildAlgorithmGraphicItem(QgsModelChildAlgorithmGraphicItem):
"""
@ -101,10 +114,13 @@ class ModelerChildAlgorithmGraphicItem(QgsModelChildAlgorithmGraphicItem):
def __init__(self, element, model):
super().__init__(element, model, None)
def editComponent(self):
def edit(self, edit_comment=False):
elemAlg = self.component().algorithm()
dlg = ModelerParametersDialog(elemAlg, self.model(), self.component().childId(),
self.component().configuration())
dlg.setComments(self.component().comment().description())
if edit_comment:
dlg.switchToCommentTab()
if dlg.exec_():
alg = dlg.createAlgorithm()
alg.setChildId(self.component().childId())
@ -112,11 +128,18 @@ class ModelerChildAlgorithmGraphicItem(QgsModelChildAlgorithmGraphicItem):
self.requestModelRepaint.emit()
self.changed.emit()
def editComponent(self):
self.edit()
def editComment(self):
self.edit(edit_comment=True)
def updateAlgorithm(self, alg):
existing_child = self.model().childAlgorithm(alg.childId())
alg.setPosition(existing_child.position())
alg.setLinksCollapsed(Qt.TopEdge, existing_child.linksCollapsed(Qt.TopEdge))
alg.setLinksCollapsed(Qt.BottomEdge, existing_child.linksCollapsed(Qt.BottomEdge))
alg.comment().setPosition(existing_child.comment().position())
for i, out in enumerate(alg.modelOutputs().keys()):
alg.modelOutput(out).setPosition(alg.modelOutput(out).position()
or alg.position() + QPointF(
@ -136,15 +159,42 @@ class ModelerOutputGraphicItem(QgsModelOutputGraphicItem):
def __init__(self, element, model):
super().__init__(element, model, None)
def editComponent(self):
def edit(self, edit_comment=False):
child_alg = self.model().childAlgorithm(self.component().childId())
param_name = '{}:{}'.format(self.component().childId(), self.component().name())
dlg = ModelerParameterDefinitionDialog(self.model(),
param=self.model().parameterDefinition(param_name))
if dlg.exec_() and dlg.param is not None:
dlg.setComments(self.component().comment().description())
if edit_comment:
dlg.switchToCommentTab()
if dlg.exec_():
model_output = child_alg.modelOutput(self.component().name())
model_output.setDescription(dlg.param.description())
model_output.setDefaultValue(dlg.param.defaultValue())
model_output.setMandatory(not (dlg.param.flags() & QgsProcessingParameterDefinition.FlagOptional))
model_output.comment().setDescription(dlg.comments())
self.model().updateDestinationParameters()
self.requestModelRepaint.emit()
self.changed.emit()
def editComponent(self):
self.edit()
def editComment(self):
self.edit(edit_comment=True)
class ModelerCommentGraphicItem(QgsModelCommentGraphicItem):
"""
IMPORTANT! This is intentionally a MINIMAL class, only containing code which HAS TO BE HERE
because it contains Python code for compatibility with deprecated methods ONLY.
Don't add anything here -- edit the c++ base class instead!
"""
def __init__(self, model, element, parent_component):
super().__init__(element, parent_component, model, None)
def editComponent(self):
pass

View File

@ -33,7 +33,10 @@ from qgis.PyQt.QtWidgets import (QDialog,
QComboBox,
QCheckBox,
QDialogButtonBox,
QMessageBox)
QMessageBox,
QTabWidget,
QWidget,
QTextEdit)
from qgis.gui import QgsExpressionLineEdit, QgsProjectionSelectionWidget
from qgis.core import (QgsApplication,
@ -124,13 +127,20 @@ class ModelerParameterDefinitionDialog(QDialog):
settings.setValue("/Processing/modelParametersDefinitionDialogGeometry", self.saveGeometry())
super(ModelerParameterDefinitionDialog, self).closeEvent(event)
def switchToCommentTab(self):
self.tab.setCurrentIndex(1)
def setupUi(self):
type_metadata = QgsApplication.processingRegistry().parameterType(self.param.type() if self.param else self.paramType)
self.setWindowTitle(self.tr('{} Parameter Definition').format(type_metadata.name()))
self.mainLayout = QVBoxLayout()
self.tab = QTabWidget()
self.mainLayout.addWidget(self.tab)
self.setMinimumWidth(300)
self.verticalLayout = QVBoxLayout(self)
self.verticalLayout.setMargin(20)
self.verticalLayout = QVBoxLayout()
self.label = QLabel(self.tr('Parameter name'))
self.verticalLayout.addWidget(self.label)
@ -214,8 +224,8 @@ class ModelerParameterDefinitionDialog(QDialog):
if self.param is not None:
self.shapetypeCombo.setCurrentIndex(self.shapetypeCombo.findData(self.param.dataTypes()[0]))
self.verticalLayout.addWidget(self.shapetypeCombo)
elif (self.paramType == parameters.PARAMETER_MULTIPLE or
isinstance(self.param, QgsProcessingParameterMultipleLayers)):
elif (self.paramType == parameters.PARAMETER_MULTIPLE
or isinstance(self.param, QgsProcessingParameterMultipleLayers)):
self.verticalLayout.addWidget(QLabel(self.tr('Data type')))
self.datatypeCombo = QComboBox()
self.datatypeCombo.addItem(self.tr('Any Map Layer'), QgsProcessing.TypeMapLayer)
@ -229,11 +239,11 @@ class ModelerParameterDefinitionDialog(QDialog):
if self.param is not None:
self.datatypeCombo.setCurrentIndex(self.datatypeCombo.findData(self.param.layerType()))
self.verticalLayout.addWidget(self.datatypeCombo)
elif (self.paramType in (parameters.PARAMETER_NUMBER, parameters.PARAMETER_DISTANCE, parameters.PARAMETER_SCALE) or
isinstance(self.param, (QgsProcessingParameterNumber, QgsProcessingParameterDistance, QgsProcessingParameterScale))):
elif (self.paramType in (parameters.PARAMETER_NUMBER, parameters.PARAMETER_DISTANCE, parameters.PARAMETER_SCALE)
or isinstance(self.param, (QgsProcessingParameterNumber, QgsProcessingParameterDistance, QgsProcessingParameterScale))):
if (self.paramType == parameters.PARAMETER_DISTANCE or
isinstance(self.param, QgsProcessingParameterDistance)):
if (self.paramType == parameters.PARAMETER_DISTANCE
or isinstance(self.param, QgsProcessingParameterDistance)):
self.verticalLayout.addWidget(QLabel(self.tr('Linked input')))
self.parentCombo = QComboBox()
self.parentCombo.addItem('', '')
@ -281,8 +291,8 @@ class ModelerParameterDefinitionDialog(QDialog):
if default:
self.defaultTextBox.setText(str(default))
self.verticalLayout.addWidget(self.defaultTextBox)
elif (self.paramType == parameters.PARAMETER_EXPRESSION or
isinstance(self.param, QgsProcessingParameterExpression)):
elif (self.paramType == parameters.PARAMETER_EXPRESSION
or isinstance(self.param, QgsProcessingParameterExpression)):
self.verticalLayout.addWidget(QLabel(self.tr('Default value')))
self.defaultEdit = QgsExpressionLineEdit()
if self.param is not None:
@ -302,15 +312,15 @@ class ModelerParameterDefinitionDialog(QDialog):
self.parentCombo.setCurrentIndex(idx)
idx += 1
self.verticalLayout.addWidget(self.parentCombo)
elif (self.paramType == parameters.PARAMETER_POINT or
isinstance(self.param, QgsProcessingParameterPoint)):
elif (self.paramType == parameters.PARAMETER_POINT
or isinstance(self.param, QgsProcessingParameterPoint)):
self.verticalLayout.addWidget(QLabel(self.tr('Default value')))
self.defaultTextBox = QLineEdit()
if self.param is not None:
self.defaultTextBox.setText(self.param.defaultValue())
self.verticalLayout.addWidget(self.defaultTextBox)
elif (self.paramType == parameters.PARAMETER_CRS or
isinstance(self.param, QgsProcessingParameterCrs)):
elif (self.paramType == parameters.PARAMETER_CRS
or isinstance(self.param, QgsProcessingParameterCrs)):
self.verticalLayout.addWidget(QLabel(self.tr('Default value')))
self.selector = QgsProjectionSelectionWidget()
if self.param is not None:
@ -367,18 +377,37 @@ class ModelerParameterDefinitionDialog(QDialog):
self.advancedCheck.setEnabled(False)
self.advancedCheck.setChecked(False)
self.verticalLayout.addStretch()
w = QWidget()
w.setLayout(self.verticalLayout)
self.tab.addTab(w, self.tr('Properties'))
self.commentLayout = QVBoxLayout()
self.commentEdit = QTextEdit()
self.commentEdit.setAcceptRichText(False)
self.commentLayout.addWidget(self.commentEdit)
w2 = QWidget()
w2.setLayout(self.commentLayout)
self.tab.addTab(w2, self.tr('Comments'))
self.buttonBox = QDialogButtonBox(self)
self.buttonBox.setOrientation(Qt.Horizontal)
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel |
QDialogButtonBox.Ok)
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel
| QDialogButtonBox.Ok)
self.buttonBox.setObjectName('buttonBox')
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
self.verticalLayout.addStretch()
self.verticalLayout.addWidget(self.buttonBox)
self.mainLayout.addWidget(self.buttonBox)
self.setLayout(self.verticalLayout)
self.setLayout(self.mainLayout)
def setComments(self, text):
self.commentEdit.setPlainText(text)
def comments(self):
return self.commentEdit.toPlainText()
def accept(self):
description = self.nameTextBox.text()
@ -397,8 +426,8 @@ class ModelerParameterDefinitionDialog(QDialog):
i += 1
else:
name = self.param.name()
if (self.paramType == parameters.PARAMETER_TABLE_FIELD or
isinstance(self.param, QgsProcessingParameterField)):
if (self.paramType == parameters.PARAMETER_TABLE_FIELD
or isinstance(self.param, QgsProcessingParameterField)):
if self.parentCombo.currentIndex() < 0:
QMessageBox.warning(self, self.tr('Unable to define parameter'),
self.tr('Wrong or missing parameter values'))
@ -411,39 +440,39 @@ class ModelerParameterDefinitionDialog(QDialog):
self.param = QgsProcessingParameterField(name, description, defaultValue=default,
parentLayerParameterName=parent, type=datatype,
allowMultiple=self.multipleCheck.isChecked())
elif (self.paramType == parameters.PARAMETER_BAND or
isinstance(self.param, QgsProcessingParameterBand)):
elif (self.paramType == parameters.PARAMETER_BAND
or isinstance(self.param, QgsProcessingParameterBand)):
if self.parentCombo.currentIndex() < 0:
QMessageBox.warning(self, self.tr('Unable to define parameter'),
self.tr('Wrong or missing parameter values'))
return
parent = self.parentCombo.currentData()
self.param = QgsProcessingParameterBand(name, description, None, parent)
elif (self.paramType == parameters.PARAMETER_MAP_LAYER or
isinstance(self.param, QgsProcessingParameterMapLayer)):
elif (self.paramType == parameters.PARAMETER_MAP_LAYER
or isinstance(self.param, QgsProcessingParameterMapLayer)):
self.param = QgsProcessingParameterMapLayer(
name, description)
elif (self.paramType == parameters.PARAMETER_RASTER or
isinstance(self.param, QgsProcessingParameterRasterLayer)):
elif (self.paramType == parameters.PARAMETER_RASTER
or isinstance(self.param, QgsProcessingParameterRasterLayer)):
self.param = QgsProcessingParameterRasterLayer(
name, description)
elif (self.paramType == parameters.PARAMETER_TABLE or
isinstance(self.param, QgsProcessingParameterVectorLayer)):
elif (self.paramType == parameters.PARAMETER_TABLE
or isinstance(self.param, QgsProcessingParameterVectorLayer)):
self.param = QgsProcessingParameterVectorLayer(
name, description,
[self.shapetypeCombo.currentData()])
elif (self.paramType == parameters.PARAMETER_VECTOR or
isinstance(self.param, QgsProcessingParameterFeatureSource)):
elif (self.paramType == parameters.PARAMETER_VECTOR
or isinstance(self.param, QgsProcessingParameterFeatureSource)):
self.param = QgsProcessingParameterFeatureSource(
name, description,
[self.shapetypeCombo.currentData()])
elif (self.paramType == parameters.PARAMETER_MULTIPLE or
isinstance(self.param, QgsProcessingParameterMultipleLayers)):
elif (self.paramType == parameters.PARAMETER_MULTIPLE
or isinstance(self.param, QgsProcessingParameterMultipleLayers)):
self.param = QgsProcessingParameterMultipleLayers(
name, description,
self.datatypeCombo.currentData())
elif (self.paramType == parameters.PARAMETER_DISTANCE or
isinstance(self.param, QgsProcessingParameterDistance)):
elif (self.paramType == parameters.PARAMETER_DISTANCE
or isinstance(self.param, QgsProcessingParameterDistance)):
self.param = QgsProcessingParameterDistance(name, description,
self.defaultTextBox.text())
try:
@ -465,12 +494,12 @@ class ModelerParameterDefinitionDialog(QDialog):
parent = self.parentCombo.currentData()
if parent:
self.param.setParentParameterName(parent)
elif (self.paramType == parameters.PARAMETER_SCALE or
isinstance(self.param, QgsProcessingParameterScale)):
elif (self.paramType == parameters.PARAMETER_SCALE
or isinstance(self.param, QgsProcessingParameterScale)):
self.param = QgsProcessingParameterScale(name, description,
self.defaultTextBox.text())
elif (self.paramType == parameters.PARAMETER_NUMBER or
isinstance(self.param, QgsProcessingParameterNumber)):
elif (self.paramType == parameters.PARAMETER_NUMBER
or isinstance(self.param, QgsProcessingParameterNumber)):
type = self.type_combo.currentData()
self.param = QgsProcessingParameterNumber(name, description, type,
@ -486,27 +515,27 @@ class ModelerParameterDefinitionDialog(QDialog):
QMessageBox.warning(self, self.tr('Unable to define parameter'),
self.tr('Wrong or missing parameter values'))
return
elif (self.paramType == parameters.PARAMETER_EXPRESSION or
isinstance(self.param, QgsProcessingParameterExpression)):
elif (self.paramType == parameters.PARAMETER_EXPRESSION
or isinstance(self.param, QgsProcessingParameterExpression)):
parent = self.parentCombo.currentData()
self.param = QgsProcessingParameterExpression(name, description,
str(self.defaultEdit.expression()),
parent)
elif (self.paramType == parameters.PARAMETER_EXTENT or
isinstance(self.param, QgsProcessingParameterExtent)):
elif (self.paramType == parameters.PARAMETER_EXTENT
or isinstance(self.param, QgsProcessingParameterExtent)):
self.param = QgsProcessingParameterExtent(name, description)
elif (self.paramType == parameters.PARAMETER_POINT or
isinstance(self.param, QgsProcessingParameterPoint)):
elif (self.paramType == parameters.PARAMETER_POINT
or isinstance(self.param, QgsProcessingParameterPoint)):
self.param = QgsProcessingParameterPoint(name, description,
str(self.defaultTextBox.text()))
elif (self.paramType == parameters.PARAMETER_CRS or
isinstance(self.param, QgsProcessingParameterCrs)):
elif (self.paramType == parameters.PARAMETER_CRS
or isinstance(self.param, QgsProcessingParameterCrs)):
self.param = QgsProcessingParameterCrs(name, description, self.selector.crs().authid())
elif (self.paramType == parameters.PARAMETER_ENUM or
isinstance(self.param, QgsProcessingParameterEnum)):
elif (self.paramType == parameters.PARAMETER_ENUM
or isinstance(self.param, QgsProcessingParameterEnum)):
self.param = QgsProcessingParameterEnum(name, description, self.widget.options(), self.widget.allowMultiple(), self.widget.defaultOptions())
elif (self.paramType == parameters.PARAMETER_MATRIX or
isinstance(self.param, QgsProcessingParameterMatrix)):
elif (self.paramType == parameters.PARAMETER_MATRIX
or isinstance(self.param, QgsProcessingParameterMatrix)):
self.param = QgsProcessingParameterMatrix(name, description, hasFixedNumberRows=self.widget.fixedRows(), headers=self.widget.headers(), defaultValue=self.widget.value())
# Destination parameter

View File

@ -29,7 +29,7 @@ from qgis.PyQt.QtCore import (Qt,
QByteArray)
from qgis.PyQt.QtWidgets import (QDialog, QDialogButtonBox, QLabel, QLineEdit,
QFrame, QPushButton, QSizePolicy, QVBoxLayout,
QHBoxLayout, QWidget)
QHBoxLayout, QWidget, QTabWidget, QTextEdit)
from qgis.core import (Qgis,
QgsProject,
@ -102,6 +102,9 @@ class ModelerParametersDialog(QDialog):
settings.setValue("/Processing/modelParametersDialogGeometry", self.saveGeometry())
super(ModelerParametersDialog, self).closeEvent(event)
def switchToCommentTab(self):
self.tab.setCurrentIndex(1)
def setupUi(self):
self.checkBoxes = {}
self.showAdvanced = False
@ -111,6 +114,11 @@ class ModelerParametersDialog(QDialog):
self.algorithmItem = None
self.resize(650, 450)
self.mainLayout = QVBoxLayout()
self.tab = QTabWidget()
self.mainLayout.addWidget(self.tab)
self.buttonBox = QDialogButtonBox()
self.buttonBox.setOrientation(Qt.Horizontal)
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok | QDialogButtonBox.Help)
@ -118,7 +126,6 @@ class ModelerParametersDialog(QDialog):
QSizePolicy.Expanding)
self.verticalLayout = QVBoxLayout()
self.verticalLayout.setSpacing(5)
self.verticalLayout.setMargin(20)
self.bar = QgsMessageBar()
self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
@ -229,13 +236,33 @@ class ModelerParametersDialog(QDialog):
self.scrollArea.setWidgetResizable(True)
self.verticalLayout2.addWidget(self.scrollArea)
self.verticalLayout2.addWidget(self.buttonBox)
self.setLayout(self.verticalLayout2)
self.buttonBox.accepted.connect(self.okPressed)
self.buttonBox.rejected.connect(self.cancelPressed)
self.buttonBox.helpRequested.connect(self.openHelp)
w = QWidget()
w.setLayout(self.verticalLayout2)
self.tab.addTab(w, self.tr('Properties'))
self.commentLayout = QVBoxLayout()
self.commentEdit = QTextEdit()
self.commentEdit.setAcceptRichText(False)
self.commentLayout.addWidget(self.commentEdit)
w2 = QWidget()
w2.setLayout(self.commentLayout)
self.tab.addTab(w2, self.tr('Comments'))
self.mainLayout.addWidget(self.buttonBox)
self.setLayout(self.mainLayout)
QMetaObject.connectSlotsByName(self)
def setComments(self, text):
self.commentEdit.setPlainText(text)
def comments(self):
return self.commentEdit.toPlainText()
def getAvailableDependencies(self): # spellok
if self.childId is None:
dependent = []
@ -378,9 +405,9 @@ class ModelerParametersDialog(QDialog):
[isinstance(subval, QgsProcessingModelChildParameterSource) for subval in val])):
val = [QgsProcessingModelChildParameterSource.fromStaticValue(val)]
for subval in val:
if (isinstance(subval, QgsProcessingModelChildParameterSource) and
subval.source() == QgsProcessingModelChildParameterSource.StaticValue and
not param.checkValueIsAcceptable(subval.staticValue())) \
if (isinstance(subval, QgsProcessingModelChildParameterSource)
and subval.source() == QgsProcessingModelChildParameterSource.StaticValue
and not param.checkValueIsAcceptable(subval.staticValue())) \
or (subval is None and not param.flags() & QgsProcessingParameterDefinition.FlagOptional):
self.bar.pushMessage(self.tr("Error"), self.tr("Wrong or missing value for parameter '{}'").format(
param.description()),
@ -419,6 +446,7 @@ class ModelerParametersDialog(QDialog):
#except:
# pass
alg.comment().setDescription(self.comments())
return alg
def okPressed(self):

View File

@ -25,7 +25,8 @@ from qgis.gui import QgsModelGraphicsScene
from processing.modeler.ModelerGraphicItem import (
ModelerInputGraphicItem,
ModelerOutputGraphicItem,
ModelerChildAlgorithmGraphicItem
ModelerChildAlgorithmGraphicItem,
ModelerCommentGraphicItem
)
@ -48,3 +49,6 @@ class ModelerScene(QgsModelGraphicsScene):
def createOutputGraphicItem(self, model, output):
return ModelerOutputGraphicItem(output.clone(), model)
#def createCommentGraphicItem(self, model, comment, parent):
# return ModelerCommentGraphicItem(model, comment.clone(), parent)

View File

@ -152,6 +152,7 @@ SET(QGIS_CORE_SRCS
processing/models/qgsprocessingmodelalgorithm.cpp
processing/models/qgsprocessingmodelchildalgorithm.cpp
processing/models/qgsprocessingmodelchildparametersource.cpp
processing/models/qgsprocessingmodelcomment.cpp
processing/models/qgsprocessingmodelcomponent.cpp
processing/models/qgsprocessingmodelparameter.cpp
processing/models/qgsprocessingmodeloutput.cpp
@ -1169,6 +1170,7 @@ SET(QGIS_CORE_HDRS
processing/models/qgsprocessingmodelalgorithm.h
processing/models/qgsprocessingmodelchildalgorithm.h
processing/models/qgsprocessingmodelchildparametersource.h
processing/models/qgsprocessingmodelcomment.h
processing/models/qgsprocessingmodelcomponent.h
processing/models/qgsprocessingmodeloutput.h
processing/models/qgsprocessingmodelparameter.h

View File

@ -35,6 +35,7 @@ QgsProcessingModelChildAlgorithm::QgsProcessingModelChildAlgorithm( const QgsPro
, mModelOutputs( other.mModelOutputs )
, mActive( other.mActive )
, mDependencies( other.mDependencies )
, mComment( other.mComment )
{
setAlgorithmId( other.algorithmId() );
}
@ -49,6 +50,7 @@ QgsProcessingModelChildAlgorithm &QgsProcessingModelChildAlgorithm::operator=( c
mModelOutputs = other.mModelOutputs;
mActive = other.mActive;
mDependencies = other.mDependencies;
mComment = other.mComment;
return *this;
}
@ -89,6 +91,7 @@ QVariant QgsProcessingModelChildAlgorithm::toVariant() const
map.insert( QStringLiteral( "alg_config" ), mConfiguration );
map.insert( QStringLiteral( "active" ), mActive );
map.insert( QStringLiteral( "dependencies" ), mDependencies );
map.insert( QStringLiteral( "comment" ), mComment.toVariant() );
saveCommonProperties( map );
@ -126,6 +129,7 @@ bool QgsProcessingModelChildAlgorithm::loadVariant( const QVariant &child )
setAlgorithmId( map.value( QStringLiteral( "alg_id" ) ).toString() );
mActive = map.value( QStringLiteral( "active" ) ).toBool();
mDependencies = map.value( QStringLiteral( "dependencies" ) ).toStringList();
mComment.loadVariant( map.value( QStringLiteral( "comment" ) ).toMap() );
restoreCommonProperties( map );

View File

@ -23,6 +23,7 @@
#include "qgsprocessingmodelcomponent.h"
#include "qgsprocessingmodelchildparametersource.h"
#include "qgsprocessingmodeloutput.h"
#include "qgsprocessingmodelcomment.h"
#include <memory>
class QgsProcessingModelAlgorithm;
@ -261,6 +262,10 @@ class CORE_EXPORT QgsProcessingModelChildAlgorithm : public QgsProcessingModelCo
QStringList asPythonCode( QgsProcessing::PythonOutputType outputType, const QgsStringMap &extraParameters, int currentIndent, int indentSize,
const QMap<QString, QString> &friendlyChildNames, const QMap<QString, QString> &friendlyOutputNames ) const;
SIP_SKIP const QgsProcessingModelComment *comment() const override { return &mComment; }
QgsProcessingModelComment *comment() override { return &mComment; }
void setComment( const QgsProcessingModelComment &comment ) override { mComment = comment; }
private:
QString mId;
@ -281,6 +286,8 @@ class CORE_EXPORT QgsProcessingModelChildAlgorithm : public QgsProcessingModelCo
//! List of child algorithms from the parent model on which this algorithm is dependent
QStringList mDependencies;
QgsProcessingModelComment mComment;
friend class TestQgsProcessing;
};

View File

@ -0,0 +1,47 @@
/***************************************************************************
qgsprocessingmodelcomment.cpp
--------------------------
begin : February 2020
copyright : (C) 2020 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsprocessingmodelcomment.h"
///@cond NOT_STABLE
QgsProcessingModelComment::QgsProcessingModelComment( const QString &description )
: QgsProcessingModelComponent( description )
{
setSize( QSizeF( 100, 60 ) );
}
QgsProcessingModelComment *QgsProcessingModelComment::clone() const
{
return new QgsProcessingModelComment( *this );
}
QVariant QgsProcessingModelComment::toVariant() const
{
QVariantMap map;
saveCommonProperties( map );
return map;
}
bool QgsProcessingModelComment::loadVariant( const QVariantMap &map )
{
restoreCommonProperties( map );
return true;
}
///@endcond

View File

@ -0,0 +1,59 @@
/***************************************************************************
qgsprocessingmodelcomment.h
--------------------------
begin : February 2020
copyright : (C) 2020 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSPROCESSINGMODELCOMMENT_H
#define QGSPROCESSINGMODELCOMMENT_H
#include "qgis_core.h"
#include "qgis.h"
#include "qgsprocessingmodelcomponent.h"
#include "qgsprocessingparameters.h"
///@cond NOT_STABLE
/**
* Represents a comment in a model.
* \ingroup core
* \since QGIS 3.14
*/
class CORE_EXPORT QgsProcessingModelComment : public QgsProcessingModelComponent
{
public:
/**
* Constructor for QgsProcessingModelComment with the specified \a description.
*/
QgsProcessingModelComment( const QString &description = QString() );
QgsProcessingModelComment *clone() const override SIP_FACTORY;
/**
* Saves this comment to a QVariant.
* \see loadVariant()
*/
QVariant toVariant() const;
/**
* Loads this comment from a QVariantMap.
* \see toVariant()
*/
bool loadVariant( const QVariantMap &map );
};
///@endcond
#endif // QGSPROCESSINGMODELCOMMENT_H

View File

@ -88,6 +88,11 @@ void QgsProcessingModelComponent::setLinksCollapsed( Qt::Edge edge, bool collaps
}
}
void QgsProcessingModelComponent::setComment( const QgsProcessingModelComment & )
{
}
void QgsProcessingModelComponent::saveCommonProperties( QVariantMap &map ) const
{
map.insert( QStringLiteral( "component_pos_x" ), mPosition.x() );

View File

@ -23,6 +23,8 @@
#include <QPointF>
#include <QSizeF>
class QgsProcessingModelComment;
///@cond NOT_STABLE
/**
@ -87,6 +89,24 @@ class CORE_EXPORT QgsProcessingModelComponent
*/
void setLinksCollapsed( Qt::Edge edge, bool collapsed );
/**
* Returns the comment attached to this component (may be NULLPTR)
* \see setComment()
*/
SIP_SKIP virtual const QgsProcessingModelComment *comment() const { return nullptr; }
/**
* Returns the comment attached to this component (may be NULLPTR)
* \see setComment()
*/
virtual QgsProcessingModelComment *comment() { return nullptr; }
/**
* Sets the \a comment attached to this component.
* \see comment()
*/
virtual void setComment( const QgsProcessingModelComment &comment );
/**
* Clones the component.
*

View File

@ -48,6 +48,7 @@ QVariant QgsProcessingModelOutput::toVariant() const
map.insert( QStringLiteral( "child_id" ), mChildId );
map.insert( QStringLiteral( "output_name" ), mOutputName );
map.insert( QStringLiteral( "mandatory" ), mMandatory );
map.insert( QStringLiteral( "comment" ), mComment.toVariant() );
saveCommonProperties( map );
return map;
}
@ -79,6 +80,7 @@ bool QgsProcessingModelOutput::loadVariant( const QVariantMap &map )
mChildId = map.value( QStringLiteral( "child_id" ) ).toString();
mOutputName = map.value( QStringLiteral( "output_name" ) ).toString();
mMandatory = map.value( QStringLiteral( "mandatory" ), false ).toBool();
mComment.loadVariant( map.value( QStringLiteral( "comment" ) ).toMap() );
restoreCommonProperties( map );
return true;
}

View File

@ -22,6 +22,7 @@
#include "qgis.h"
#include "qgsprocessingmodelcomponent.h"
#include "qgsprocessingparameters.h"
#include "qgsprocessingmodelcomment.h"
///@cond NOT_STABLE
@ -121,6 +122,10 @@ class CORE_EXPORT QgsProcessingModelOutput : public QgsProcessingModelComponent
*/
bool loadVariant( const QVariantMap &map );
SIP_SKIP const QgsProcessingModelComment *comment() const override { return &mComment; }
QgsProcessingModelComment *comment() override { return &mComment; }
void setComment( const QgsProcessingModelComment &comment ) override { mComment = comment; }
private:
QString mName;
@ -128,6 +133,9 @@ class CORE_EXPORT QgsProcessingModelOutput : public QgsProcessingModelComponent
QString mChildId;
QString mOutputName;
bool mMandatory = false;
QgsProcessingModelComment mComment;
};
///@endcond

View File

@ -34,6 +34,7 @@ QVariant QgsProcessingModelParameter::toVariant() const
{
QVariantMap map;
map.insert( QStringLiteral( "name" ), mParameterName );
map.insert( QStringLiteral( "comment" ), mComment.toVariant() );
saveCommonProperties( map );
return map;
}
@ -41,6 +42,7 @@ QVariant QgsProcessingModelParameter::toVariant() const
bool QgsProcessingModelParameter::loadVariant( const QVariantMap &map )
{
mParameterName = map.value( QStringLiteral( "name" ) ).toString();
mComment.loadVariant( map.value( QStringLiteral( "comment" ) ).toMap() );
restoreCommonProperties( map );
return true;
}

View File

@ -21,6 +21,7 @@
#include "qgis_core.h"
#include "qgis.h"
#include "qgsprocessingmodelcomponent.h"
#include "qgsprocessingmodelcomment.h"
///@cond NOT_STABLE
@ -68,10 +69,16 @@ class CORE_EXPORT QgsProcessingModelParameter : public QgsProcessingModelCompone
*/
bool loadVariant( const QVariantMap &map );
SIP_SKIP const QgsProcessingModelComment *comment() const override { return &mComment; }
QgsProcessingModelComment *comment() override { return &mComment; }
void setComment( const QgsProcessingModelComment &comment ) override { mComment = comment; }
private:
QString mParameterName;
QgsProcessingModelComment mComment;
};
///@endcond

View File

@ -20,6 +20,7 @@
#include "qgsrectangle.h"
#include "qgsproperty.h"
#include "qgssymbollayerutils.h"
#include "qgsprocessingparameters.h"
QgsUnitTypes::DistanceUnit QgsXmlUtils::readMapUnits( const QDomElement &element )
{
@ -213,7 +214,15 @@ QDomElement QgsXmlUtils::writeVariant( const QVariant &value, QDomDocument &doc
element.setAttribute( QStringLiteral( "value" ), geom.asWkt() );
break;
}
FALLTHROUGH
else if ( value.canConvert< QgsProcessingOutputLayerDefinition >() )
{
QDomElement valueElement = writeVariant( value.value< QgsProcessingOutputLayerDefinition >().toVariant(), doc );
element.appendChild( valueElement );
element.setAttribute( QStringLiteral( "type" ), QStringLiteral( "QgsProcessingOutputLayerDefinition" ) );
break;
}
Q_ASSERT_X( false, "QgsXmlUtils::writeVariant", QStringLiteral( "unsupported user variant type %1" ).arg( QMetaType::typeName( value.userType() ) ).toLocal8Bit() );
break;
}
default:
@ -338,6 +347,18 @@ QVariant QgsXmlUtils::readVariant( const QDomElement &element )
{
return QgsGeometry::fromWkt( element.attribute( "value" ) );
}
else if ( type == QLatin1String( "QgsProcessingOutputLayerDefinition" ) )
{
QgsProcessingOutputLayerDefinition res;
const QDomNodeList values = element.childNodes();
if ( values.isEmpty() )
return QVariant();
if ( res.loadVariant( QgsXmlUtils::readVariant( values.at( 0 ).toElement() ).toMap() ) )
return res;
return QVariant();
}
else
{
return QVariant();

View File

@ -131,14 +131,7 @@ QVariant QgsModelComponentGraphicItem::itemChange( QGraphicsItem::GraphicsItemCh
mComponent->setPosition( pos() );
// also need to update the model's stored component's position
// TODO - this is not so nice, consider moving this to model class
if ( QgsProcessingModelChildAlgorithm *child = dynamic_cast< QgsProcessingModelChildAlgorithm * >( mComponent.get() ) )
mModel->childAlgorithm( child->childId() ).setPosition( pos() );
else if ( QgsProcessingModelParameter *param = dynamic_cast< QgsProcessingModelParameter * >( mComponent.get() ) )
mModel->parameterComponent( param->parameterName() ).setPosition( pos() );
else if ( QgsProcessingModelOutput *output = dynamic_cast< QgsProcessingModelOutput * >( mComponent.get() ) )
mModel->childAlgorithm( output->childId() ).modelOutput( output->name() ).setPosition( pos() );
updateStoredComponentPosition( pos() );
break;
}
case QGraphicsItem::ItemSelectedChange:
@ -197,7 +190,9 @@ void QgsModelComponentGraphicItem::paint( QPainter *painter, const QStyleOptionG
QColor color = fillColor( state() );
QColor stroke = strokeColor( state() );
painter->setPen( QPen( stroke, 0 ) ); // 0 width "cosmetic" pen
QPen strokePen = QPen( stroke, 0 ) ; // 0 width "cosmetic" pen
strokePen.setStyle( strokeStyle( state() ) );
painter->setPen( strokePen );
painter->setBrush( QBrush( color, Qt::SolidPattern ) );
painter->drawRect( rect );
painter->setFont( font() );
@ -291,6 +286,11 @@ QString QgsModelComponentGraphicItem::truncatedTextForItem( const QString &text
return t;
}
Qt::PenStyle QgsModelComponentGraphicItem::strokeStyle( QgsModelComponentGraphicItem::State ) const
{
return Qt::SolidLine;
}
QPicture QgsModelComponentGraphicItem::iconPicture() const
{
return QPicture();
@ -558,6 +558,12 @@ QPicture QgsModelParameterGraphicItem::iconPicture() const
return mPicture;
}
void QgsModelParameterGraphicItem::updateStoredComponentPosition( const QPointF &pos )
{
if ( QgsProcessingModelParameter *param = dynamic_cast< QgsProcessingModelParameter * >( component() ) )
model()->parameterComponent( param->parameterName() ).setPosition( pos );
}
void QgsModelParameterGraphicItem::deleteComponent()
{
if ( const QgsProcessingModelParameter *param = dynamic_cast< const QgsProcessingModelParameter * >( component() ) )
@ -733,6 +739,12 @@ QString QgsModelChildAlgorithmGraphicItem::linkPointText( Qt::Edge edge, int ind
return QString();
}
void QgsModelChildAlgorithmGraphicItem::updateStoredComponentPosition( const QPointF &pos )
{
if ( QgsProcessingModelChildAlgorithm *child = dynamic_cast< QgsProcessingModelChildAlgorithm * >( component() ) )
model()->childAlgorithm( child->childId() ).setPosition( pos );
}
void QgsModelChildAlgorithmGraphicItem::deleteComponent()
{
if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast< const QgsProcessingModelChildAlgorithm * >( component() ) )
@ -830,6 +842,12 @@ QPicture QgsModelOutputGraphicItem::iconPicture() const
return mPicture;
}
void QgsModelOutputGraphicItem::updateStoredComponentPosition( const QPointF &pos )
{
if ( QgsProcessingModelOutput *output = dynamic_cast< QgsProcessingModelOutput * >( component() ) )
model()->childAlgorithm( output->childId() ).modelOutput( output->name() ).setPosition( pos );
}
void QgsModelOutputGraphicItem::deleteComponent()
{
if ( const QgsProcessingModelOutput *output = dynamic_cast< const QgsProcessingModelOutput * >( component() ) )
@ -842,4 +860,115 @@ void QgsModelOutputGraphicItem::deleteComponent()
}
QgsModelCommentGraphicItem::QgsModelCommentGraphicItem( QgsProcessingModelComment *comment, QgsModelComponentGraphicItem *parentItem, QgsProcessingModelAlgorithm *model, QGraphicsItem *parent )
: QgsModelComponentGraphicItem( comment, model, parent )
, mParentComponent( parentItem->component()->clone() )
, mParentItem( parentItem )
{
setLabel( comment->description() );
QFont f = font();
f.setPixelSize( 9 );
setFont( f );
}
void QgsModelCommentGraphicItem::contextMenuEvent( QGraphicsSceneContextMenuEvent *event )
{
QMenu *popupmenu = new QMenu( event->widget() );
QAction *removeAction = popupmenu->addAction( QObject::tr( "Remove" ) );
connect( removeAction, &QAction::triggered, this, &QgsModelCommentGraphicItem::deleteComponent );
QAction *editAction = popupmenu->addAction( QObject::tr( "Edit" ) );
connect( editAction, &QAction::triggered, this, &QgsModelCommentGraphicItem::editComponent );
popupmenu->exec( event->screenPos() );
}
QgsModelCommentGraphicItem::~QgsModelCommentGraphicItem() = default;
QColor QgsModelCommentGraphicItem::fillColor( QgsModelComponentGraphicItem::State state ) const
{
QColor c( 230, 230, 230 );
switch ( state )
{
case Selected:
c = c.darker( 110 );
break;
case Hover:
c = c.darker( 105 );
break;
case Normal:
break;
}
return c;
}
QColor QgsModelCommentGraphicItem::strokeColor( QgsModelComponentGraphicItem::State state ) const
{
switch ( state )
{
case Selected:
return QColor( 50, 50, 50 );
case Hover:
case Normal:
return QColor( 150, 150, 150 );
}
return QColor();
}
QColor QgsModelCommentGraphicItem::textColor( QgsModelComponentGraphicItem::State ) const
{
return QColor( 100, 100, 100 );
}
Qt::PenStyle QgsModelCommentGraphicItem::strokeStyle( QgsModelComponentGraphicItem::State ) const
{
return Qt::DotLine;
}
void QgsModelCommentGraphicItem::updateStoredComponentPosition( const QPointF &pos )
{
if ( QgsProcessingModelComment *comment = modelComponent() )
{
comment->setPosition( pos );
}
}
void QgsModelCommentGraphicItem::deleteComponent()
{
if ( QgsProcessingModelComment *comment = modelComponent() )
{
comment->setDescription( QString() );
emit changed();
emit requestModelRepaint();
}
}
void QgsModelCommentGraphicItem::editComponent()
{
if ( mParentItem )
{
mParentItem->editComment();
}
}
QgsProcessingModelComment *QgsModelCommentGraphicItem::modelComponent()
{
if ( const QgsProcessingModelChildAlgorithm *child = dynamic_cast< const QgsProcessingModelChildAlgorithm * >( mParentComponent.get() ) )
{
return model()->childAlgorithm( child->childId() ).comment();
}
else if ( const QgsProcessingModelParameter *param = dynamic_cast< const QgsProcessingModelParameter * >( mParentComponent.get() ) )
{
return model()->parameterComponent( param->parameterName() ).comment();
}
else if ( const QgsProcessingModelOutput *output = dynamic_cast< const QgsProcessingModelOutput * >( mParentComponent.get() ) )
{
return model()->childAlgorithm( output->childId() ).modelOutput( output->name() ).comment();
}
return nullptr;
}
///@endcond

View File

@ -21,11 +21,13 @@
#include <QGraphicsObject>
#include <QFont>
#include <QPicture>
#include <QPointer>
class QgsProcessingModelComponent;
class QgsProcessingModelParameter;
class QgsProcessingModelChildAlgorithm;
class QgsProcessingModelOutput;
class QgsProcessingModelComment;
class QgsProcessingModelAlgorithm;
class QgsModelDesignerFlatButtonGraphicItem;
class QgsModelDesignerFoldButtonGraphicItem;
@ -158,6 +160,13 @@ class GUI_EXPORT QgsModelComponentGraphicItem : public QGraphicsObject
*/
QPointF calculateAutomaticLinkPoint( const QPointF &point, Qt::Edge &edge SIP_OUT ) const;
/**
* Called when the comment attached to the item should be edited.
*
* The default implementation does nothing.
*/
virtual void editComment() {}
signals:
// TODO - rework this, should be triggered externally when the model actually changes!
@ -222,6 +231,11 @@ class GUI_EXPORT QgsModelComponentGraphicItem : public QGraphicsObject
*/
virtual QColor textColor( State state ) const = 0;
/**
* Returns the stroke style to use while rendering the outline of the item.
*/
virtual Qt::PenStyle strokeStyle( State state ) const;
/**
* Returns a QPicture version of the item's icon, if available.
*/
@ -232,6 +246,11 @@ class GUI_EXPORT QgsModelComponentGraphicItem : public QGraphicsObject
*/
virtual QPixmap iconPixmap() const;
/**
* Updates the position stored in the model for the associated comment
*/
virtual void updateStoredComponentPosition( const QPointF &pos ) = 0;
private:
void updateToolTip( const QPointF &pos );
@ -292,6 +311,7 @@ class GUI_EXPORT QgsModelParameterGraphicItem : public QgsModelComponentGraphicI
QColor strokeColor( State state ) const override;
QColor textColor( State state ) const override;
QPicture iconPicture() const override;
void updateStoredComponentPosition( const QPointF &pos ) override;
protected slots:
@ -337,6 +357,7 @@ class GUI_EXPORT QgsModelChildAlgorithmGraphicItem : public QgsModelComponentGra
int linkPointCount( Qt::Edge edge ) const override;
QString linkPointText( Qt::Edge edge, int index ) const override;
void updateStoredComponentPosition( const QPointF &pos ) override;
protected slots:
@ -382,6 +403,7 @@ class GUI_EXPORT QgsModelOutputGraphicItem : public QgsModelComponentGraphicItem
QColor strokeColor( State state ) const override;
QColor textColor( State state ) const override;
QPicture iconPicture() const override;
void updateStoredComponentPosition( const QPointF &pos ) override;
protected slots:
@ -391,6 +413,57 @@ class GUI_EXPORT QgsModelOutputGraphicItem : public QgsModelComponentGraphicItem
QPicture mPicture;
};
/**
* \ingroup gui
* \brief A graphic item representing a model comment in the model designer.
* \warning Not stable API
* \since QGIS 3.14
*/
class GUI_EXPORT QgsModelCommentGraphicItem : public QgsModelComponentGraphicItem
{
Q_OBJECT
public:
/**
* Constructor for QgsModelCommentGraphicItem for the specified \a comment, with the specified \a parent item.
*
* The \a model argument specifies the associated processing model. Ownership of \a model is not transferred, and
* it must exist for the lifetime of this object.
*
* Ownership of \a output is transferred to the item.
*/
QgsModelCommentGraphicItem( QgsProcessingModelComment *comment SIP_TRANSFER,
QgsModelComponentGraphicItem *parentItem,
QgsProcessingModelAlgorithm *model,
QGraphicsItem *parent SIP_TRANSFERTHIS );
~QgsModelCommentGraphicItem() override;
void contextMenuEvent( QGraphicsSceneContextMenuEvent *event ) override;
protected:
QColor fillColor( State state ) const override;
QColor strokeColor( State state ) const override;
QColor textColor( State state ) const override;
Qt::PenStyle strokeStyle( State state ) const override;
void updateStoredComponentPosition( const QPointF &pos ) override;
protected slots:
void deleteComponent() override;
void editComponent() override;
private:
QgsProcessingModelComment *modelComponent();
std::unique_ptr< QgsProcessingModelComponent > mParentComponent;
QPointer< QgsModelComponentGraphicItem > mParentItem;
};
///@endcond
#endif // QGSMODELCOMPONENTGRAPHICITEM_H

View File

@ -58,6 +58,11 @@ QgsModelComponentGraphicItem *QgsModelGraphicsScene::createOutputGraphicItem( Qg
return new QgsModelOutputGraphicItem( output, model, nullptr );
}
QgsModelComponentGraphicItem *QgsModelGraphicsScene::createCommentGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelComment *comment, QgsModelComponentGraphicItem *parentItem ) const
{
return new QgsModelCommentGraphicItem( comment, parentItem, model, nullptr );
}
void QgsModelGraphicsScene::createItems( QgsProcessingModelAlgorithm *model, QgsProcessingContext &context )
{
// model input parameters
@ -70,6 +75,8 @@ void QgsModelGraphicsScene::createItems( QgsProcessingModelAlgorithm *model, Qgs
mParameterItems.insert( it.value().parameterName(), item );
connect( item, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
connect( item, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
addCommentItemForComponent( model, it.value(), item );
}
// input dependency arrows
@ -98,6 +105,8 @@ void QgsModelGraphicsScene::createItems( QgsProcessingModelAlgorithm *model, Qgs
mChildAlgorithmItems.insert( it.value().childId(), item );
connect( item, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
connect( item, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
addCommentItemForComponent( model, it.value(), item );
}
// arrows linking child algorithms
@ -148,6 +157,8 @@ void QgsModelGraphicsScene::createItems( QgsProcessingModelAlgorithm *model, Qgs
connect( item, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
connect( item, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
addCommentItemForComponent( model, outputIt.value(), item );
QPointF pos = outputIt.value().position();
// find the actual index of the linked output from the child algorithm it comes from
@ -164,12 +175,7 @@ void QgsModelGraphicsScene::createItems( QgsProcessingModelAlgorithm *model, Qgs
i++;
}
#if 0
if pos is None:
pos = ( alg.position() + QPointF( alg.size().width(), 0 )
+ self.algItems[alg.childId()].linkPoint( Qt.BottomEdge, idx ) )
#endif
item->setPos( pos );
item->setPos( pos );
outputItems.insert( outputIt.key(), item );
addItem( new QgsModelArrowItem( mChildAlgorithmItems[it.value().childId()], Qt::BottomEdge, idx, item ) );
}
@ -249,5 +255,21 @@ QList<QgsModelGraphicsScene::LinkSource> QgsModelGraphicsScene::linkSourcesForPa
return res;
}
void QgsModelGraphicsScene::addCommentItemForComponent( QgsProcessingModelAlgorithm *model, const QgsProcessingModelComponent &component, QgsModelComponentGraphicItem *parentItem )
{
if ( !component.comment() || component.comment()->description().isEmpty() )
return;
QgsModelComponentGraphicItem *commentItem = createCommentGraphicItem( model, component.comment()->clone(), parentItem );
commentItem->setPos( component.comment()->position().x(), component.comment()->position().y() );
addItem( commentItem );
connect( commentItem, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
connect( commentItem, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
std::unique_ptr< QgsModelArrowItem > arrow = qgis::make_unique< QgsModelArrowItem >( parentItem, commentItem );
arrow->setPenStyle( Qt::DotLine );
addItem( arrow.release() );
}
///@endcond

View File

@ -26,6 +26,8 @@ class QgsModelComponentGraphicItem;
class QgsProcessingModelParameter;
class QgsProcessingModelChildAlgorithm;
class QgsProcessingModelOutput;
class QgsProcessingModelComponent;
class QgsProcessingModelComment;
///@cond NOT_STABLE
@ -118,6 +120,13 @@ class GUI_EXPORT QgsModelGraphicsScene : public QGraphicsScene
*/
virtual QgsModelComponentGraphicItem *createOutputGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelOutput *output ) const SIP_FACTORY;
/**
* Creates a new graphic item for a model comment.
*/
virtual QgsModelComponentGraphicItem *createCommentGraphicItem( QgsProcessingModelAlgorithm *model, QgsProcessingModelComment *comment,
QgsModelComponentGraphicItem *parentItem ) const SIP_FACTORY;
private:
struct LinkSource
@ -128,6 +137,8 @@ class GUI_EXPORT QgsModelGraphicsScene : public QGraphicsScene
};
QList< LinkSource > linkSourcesForParameterValue( QgsProcessingModelAlgorithm *model, const QVariant &value, const QString &childId, QgsProcessingContext &context ) const;
void addCommentItemForComponent( QgsProcessingModelAlgorithm *model, const QgsProcessingModelComponent &component, QgsModelComponentGraphicItem *parentItem );
Flags mFlags = nullptr;
QMap< QString, QgsModelComponentGraphicItem * > mParameterItems;