diff --git a/python/PyQt6/core/auto_additions/qgsprocessingutils.py b/python/PyQt6/core/auto_additions/qgsprocessingutils.py index 1b31cd1564cb..c4741e4da135 100644 --- a/python/PyQt6/core/auto_additions/qgsprocessingutils.py +++ b/python/PyQt6/core/auto_additions/qgsprocessingutils.py @@ -65,6 +65,7 @@ QgsProcessingUtils.tempFolder = staticmethod(QgsProcessingUtils.tempFolder) QgsProcessingUtils.generateTempFilename = staticmethod(QgsProcessingUtils.generateTempFilename) QgsProcessingUtils.formatHelpMapAsHtml = staticmethod(QgsProcessingUtils.formatHelpMapAsHtml) + QgsProcessingUtils.outputDefinitionIndex = staticmethod(QgsProcessingUtils.outputDefinitionIndex) QgsProcessingUtils.convertToCompatibleFormat = staticmethod(QgsProcessingUtils.convertToCompatibleFormat) QgsProcessingUtils.convertToCompatibleFormatAndLayerName = staticmethod(QgsProcessingUtils.convertToCompatibleFormatAndLayerName) QgsProcessingUtils.combineFields = staticmethod(QgsProcessingUtils.combineFields) diff --git a/python/PyQt6/core/auto_generated/processing/qgsprocessingutils.sip.in b/python/PyQt6/core/auto_generated/processing/qgsprocessingutils.sip.in index d4f215798021..4250694bb5c6 100644 --- a/python/PyQt6/core/auto_generated/processing/qgsprocessingutils.sip.in +++ b/python/PyQt6/core/auto_generated/processing/qgsprocessingutils.sip.in @@ -435,6 +435,12 @@ but not changing the ``basename``. %Docstring Returns a HTML formatted version of the help text encoded in a variant ``map`` for a specified ``algorithm``. +%End + + static int outputDefinitionIndex( const QgsProcessingAlgorithm *algorithm, const QString &name ) /HoldGIL/; +%Docstring +Returns the index of the output matching ``name`` for a specified ``algorithm``. +Matching is done in a case-insensitive manner. %End static QString convertToCompatibleFormat( const QgsVectorLayer *layer, diff --git a/python/PyQt6/gui/auto_additions/qgsmodelarrowitem.py b/python/PyQt6/gui/auto_additions/qgsmodelarrowitem.py index db0aedc420c9..acc3afca7b77 100644 --- a/python/PyQt6/gui/auto_additions/qgsmodelarrowitem.py +++ b/python/PyQt6/gui/auto_additions/qgsmodelarrowitem.py @@ -1,6 +1,7 @@ # The following has been generated automatically from src/gui/processing/models/qgsmodelarrowitem.h QgsModelArrowItem.Circle = QgsModelArrowItem.Marker.Circle QgsModelArrowItem.ArrowHead = QgsModelArrowItem.Marker.ArrowHead +QgsModelArrowItem.NoMarker = QgsModelArrowItem.Marker.NoMarker try: QgsModelArrowItem.__group__ = ['processing', 'models'] except (NameError, AttributeError): diff --git a/python/PyQt6/gui/auto_additions/qgsmodelgraphicitem.py b/python/PyQt6/gui/auto_additions/qgsmodelgraphicitem.py index d26b525d1263..b892622629bd 100644 --- a/python/PyQt6/gui/auto_additions/qgsmodelgraphicitem.py +++ b/python/PyQt6/gui/auto_additions/qgsmodelgraphicitem.py @@ -10,3 +10,7 @@ QgsModelDesignerFoldButtonGraphicItem.__group__ = ['processing', 'models'] except (NameError, AttributeError): pass +try: + QgsModelDesignerSocketGraphicItem.__group__ = ['processing', 'models'] +except (NameError, AttributeError): + pass diff --git a/python/PyQt6/gui/auto_additions/qgsmodelgraphicsview.py b/python/PyQt6/gui/auto_additions/qgsmodelgraphicsview.py index b842e6901973..9d5f4846c3c1 100644 --- a/python/PyQt6/gui/auto_additions/qgsmodelgraphicsview.py +++ b/python/PyQt6/gui/auto_additions/qgsmodelgraphicsview.py @@ -5,8 +5,8 @@ QgsModelGraphicsView.PasteModeCenter = QgsModelGraphicsView.PasteMode.PasteModeCenter QgsModelGraphicsView.PasteModeInPlace = QgsModelGraphicsView.PasteMode.PasteModeInPlace try: - QgsModelGraphicsView.__attribute_docs__ = {'algorithmDropped': 'Emitted when an algorithm is dropped onto the view.\n', 'inputDropped': 'Emitted when an input parameter is dropped onto the view.\n', 'itemFocused': 'Emitted when an ``item`` is "focused" in the view, i.e. it becomes the active\nitem and should have its properties displayed in any designer windows.\n', 'willBeDeleted': 'Emitted in the destructor when the view is about to be deleted,\nbut is still in a perfectly valid state.\n', 'macroCommandStarted': 'Emitted when a macro command containing a group of interactions is started in the view.\n', 'macroCommandEnded': 'Emitted when a macro command containing a group of interactions in the view has ended.\n', 'beginCommand': 'Emitted when an undo command is started in the view.\n', 'endCommand': 'Emitted when an undo command in the view has ended.\n', 'deleteSelectedItems': 'Emitted when the selected items should be deleted;\n'} - QgsModelGraphicsView.__signal_arguments__ = {'algorithmDropped': ['algorithmId: str', 'pos: QPointF'], 'inputDropped': ['inputId: str', 'pos: QPointF'], 'itemFocused': ['item: QgsModelComponentGraphicItem'], 'macroCommandStarted': ['text: str'], 'beginCommand': ['text: str']} + QgsModelGraphicsView.__attribute_docs__ = {'algorithmDropped': 'Emitted when an algorithm is dropped onto the view.\n', 'inputDropped': 'Emitted when an input parameter is dropped onto the view.\n', 'itemFocused': 'Emitted when an ``item`` is "focused" in the view, i.e. it becomes the active\nitem and should have its properties displayed in any designer windows.\n', 'willBeDeleted': 'Emitted in the destructor when the view is about to be deleted,\nbut is still in a perfectly valid state.\n', 'macroCommandStarted': 'Emitted when a macro command containing a group of interactions is started in the view.\n', 'macroCommandEnded': 'Emitted when a macro command containing a group of interactions in the view has ended.\n', 'commandBegun': 'Emitted when an undo command is started in the view.\n', 'commandEnded': 'Emitted when an undo command in the view has ended.\n', 'deleteSelectedItems': 'Emitted when the selected items should be deleted;\n'} + QgsModelGraphicsView.__signal_arguments__ = {'algorithmDropped': ['algorithmId: str', 'pos: QPointF'], 'inputDropped': ['inputId: str', 'pos: QPointF'], 'itemFocused': ['item: QgsModelComponentGraphicItem'], 'macroCommandStarted': ['text: str'], 'commandBegun': ['text: str']} QgsModelGraphicsView.__group__ = ['processing', 'models'] except (NameError, AttributeError): pass diff --git a/python/PyQt6/gui/auto_generated/processing/models/qgsmodelarrowitem.sip.in b/python/PyQt6/gui/auto_generated/processing/models/qgsmodelarrowitem.sip.in index 0d6f849367aa..0c1a6866e581 100644 --- a/python/PyQt6/gui/auto_generated/processing/models/qgsmodelarrowitem.sip.in +++ b/python/PyQt6/gui/auto_generated/processing/models/qgsmodelarrowitem.sip.in @@ -31,6 +31,7 @@ A link arrow item for use in the model designer. { Circle, ArrowHead, + NoMarker }; QgsModelArrowItem( QgsModelComponentGraphicItem *startItem, Qt::Edge startEdge, int startIndex, bool startIsOutgoing, Marker startMarker, QgsModelComponentGraphicItem *endItem, Qt::Edge endEdge, int endIndex, bool endIsIncoming, Marker endMarker ); diff --git a/python/PyQt6/gui/auto_generated/processing/models/qgsmodelcomponentgraphicitem.sip.in b/python/PyQt6/gui/auto_generated/processing/models/qgsmodelcomponentgraphicitem.sip.in index af4c2ae47c2d..d7d5868c0e9f 100644 --- a/python/PyQt6/gui/auto_generated/processing/models/qgsmodelcomponentgraphicitem.sip.in +++ b/python/PyQt6/gui/auto_generated/processing/models/qgsmodelcomponentgraphicitem.sip.in @@ -69,6 +69,7 @@ Returns the model component associated with this item. Returns the model associated with this item. %End + QgsModelGraphicsView *view(); %Docstring Returns the associated view. @@ -183,6 +184,14 @@ Returns the best link point to use for a link originating at a specified ``other - edge: item edge for calculated best link point %End + QgsModelDesignerSocketGraphicItem *outSocketAt( int index ) const; +%Docstring +Returns the output socket graphics items at the specified ``index``. + +May return ``None`` if no corresponding output socket exists. +%End + + virtual void editComment(); %Docstring Called when the comment attached to the item should be edited. @@ -345,6 +354,11 @@ Ownership of ``parameter`` is transferred to the item. virtual QPicture iconPicture() const; + + virtual int linkPointCount( Qt::Edge edge ) const; + + virtual QString linkPointText( Qt::Edge edge, int index ) const; + virtual void updateStoredComponentPosition( const QPointF &pos, const QSizeF &size ); diff --git a/python/PyQt6/gui/auto_generated/processing/models/qgsmodelgraphicitem.sip.in b/python/PyQt6/gui/auto_generated/processing/models/qgsmodelgraphicitem.sip.in index 65f29fb20da0..a0e9d410b317 100644 --- a/python/PyQt6/gui/auto_generated/processing/models/qgsmodelgraphicitem.sip.in +++ b/python/PyQt6/gui/auto_generated/processing/models/qgsmodelgraphicitem.sip.in @@ -47,6 +47,11 @@ for the button. The button will be rendered at the specified ``position`` and `` + QPointF getPosition(); +%Docstring +Returns the button's position. +%End + void setPosition( const QPointF &position ); %Docstring Sets the button's ``position``. @@ -114,6 +119,65 @@ If ``folded`` is ``True``, the button represents the collapsed state for the ite }; +class QgsModelDesignerSocketGraphicItem : QgsModelDesignerFlatButtonGraphicItem +{ +%Docstring(signature="appended") +A socket allowing linking component together + +.. warning:: + + Not stable API + +.. versionadded:: 3.42 +%End + +%TypeHeaderCode +#include "qgsmodelgraphicitem.h" +%End + public: + QgsModelDesignerSocketGraphicItem( QgsModelComponentGraphicItem *parent /TransferThis/, QgsProcessingModelComponent *component, int index, const QPointF &position, Qt::Edge edge, const QSizeF &size = QSizeF( 11, 11 ) ); +%Docstring +Constructor for QgsModelDesignerSocketGraphicItem, with the specified ``parent`` item. + +The ``index`` argument specifies whether the input or output index of this socket inside the component +And the ``edge`` argument specifies if it's an input socket( Qt.Edge.TopEdge ) or output ( Qt.Edge.BottomEdge ) + +The sockets will be rendered at the specified ``position`` +%End + + virtual void paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0 ); + + int index() const; +%Docstring +Returns the index of this socket in either QgsModelDesignerSocketGraphicItem.mInSockets +or QgsModelDesignerSocketGraphicItem.mOutSockets array +%End + Qt::Edge edge() const; +%Docstring +Returns on which edge this socket is: +* Qt.Edge.TopEdge for input socket +* Qt.Edge.BottomEdge for output socket +%End + bool isInput() const; +%Docstring +Returns whether the socket is an input socket or not + +Convenient function around mEdge member +%End + QgsProcessingModelComponent *component(); +%Docstring +Return the component associated to the socket */ +%End + QgsModelComponentGraphicItem *componentItem(); +%Docstring +Return the parent GraphicItem (:py:class:`QgsModelComponentGraphicItem`) associated to the socket */ +%End + signals: + + +}; + + /************************************************************************ * This file has been generated automatically from * * * diff --git a/python/PyQt6/gui/auto_generated/processing/models/qgsmodelgraphicsscene.sip.in b/python/PyQt6/gui/auto_generated/processing/models/qgsmodelgraphicsscene.sip.in index 3dd3ea31ee0c..3b276db2dd9a 100644 --- a/python/PyQt6/gui/auto_generated/processing/models/qgsmodelgraphicsscene.sip.in +++ b/python/PyQt6/gui/auto_generated/processing/models/qgsmodelgraphicsscene.sip.in @@ -103,6 +103,16 @@ Returns the topmost component item at a specified ``position``. QgsModelComponentGraphicItem *groupBoxItem( const QString &uuid ); %Docstring Returns the graphic item corresponding to the specified group box ``uuid``. +%End + + QgsModelChildAlgorithmGraphicItem *childAlgorithmItem( const QString &childId ); +%Docstring +Returns the graphic item corresponding to the specified specified child algorithm +%End + + QgsModelComponentGraphicItem *parameterItem( const QString &name ); +%Docstring +Returns the :py:class:`QgsModelComponentGraphicItem` corresponding to the specified child algorithm %End void selectAll(); diff --git a/python/PyQt6/gui/auto_generated/processing/models/qgsmodelgraphicsview.sip.in b/python/PyQt6/gui/auto_generated/processing/models/qgsmodelgraphicsview.sip.in index 01ce84e7ca58..d5e53505fa6c 100644 --- a/python/PyQt6/gui/auto_generated/processing/models/qgsmodelgraphicsview.sip.in +++ b/python/PyQt6/gui/auto_generated/processing/models/qgsmodelgraphicsview.sip.in @@ -79,6 +79,16 @@ Starts a macro command, containing a group of interactions in the view. Ends a macro command, containing a group of interactions in the view. %End + void beginCommand( const QString &text ); +%Docstring +Starts a single undo command +%End + + void endCommand(); +%Docstring +Ends a single undo command +%End + enum ClipboardOperation /BaseType=IntEnum/ { @@ -162,12 +172,12 @@ Emitted when a macro command containing a group of interactions is started in th Emitted when a macro command containing a group of interactions in the view has ended. %End - void beginCommand( const QString &text ); + void commandBegun( const QString &text ); %Docstring Emitted when an undo command is started in the view. %End - void endCommand(); + void commandEnded(); %Docstring Emitted when an undo command in the view has ended. %End diff --git a/python/PyQt6/gui/auto_generated/processing/qgsprocessingmodelerparameterwidget.sip.in b/python/PyQt6/gui/auto_generated/processing/qgsprocessingmodelerparameterwidget.sip.in index 1566fe2b5236..117e08614104 100644 --- a/python/PyQt6/gui/auto_generated/processing/qgsprocessingmodelerparameterwidget.sip.in +++ b/python/PyQt6/gui/auto_generated/processing/qgsprocessingmodelerparameterwidget.sip.in @@ -81,6 +81,17 @@ The ``compatibleDataTypes`` list corresponds to the compatible data types from :py:class:`QgsProcessing`.SourceType. %End + QList availableSourcesForChild(); +%Docstring +HACK : Ugly workaround to expose the compatible sources for a parameter definition ( :py:class:`QgsProcessingParameterDefinition`) +Idealy we should no rely on any UI-related function to get this + +see also :py:class:`QgsProcessingModelAlgorithm`.availableSourcesForChild + +The available sources are created on widget creation in :py:func:`populateSources` +%End + + void setExpressionHelpText( const QString &text ); %Docstring Set the expected expression format ``text``, which is shown in the expression builder dialog for the widget diff --git a/python/core/auto_additions/qgsprocessingutils.py b/python/core/auto_additions/qgsprocessingutils.py index 1b31cd1564cb..c4741e4da135 100644 --- a/python/core/auto_additions/qgsprocessingutils.py +++ b/python/core/auto_additions/qgsprocessingutils.py @@ -65,6 +65,7 @@ QgsProcessingUtils.tempFolder = staticmethod(QgsProcessingUtils.tempFolder) QgsProcessingUtils.generateTempFilename = staticmethod(QgsProcessingUtils.generateTempFilename) QgsProcessingUtils.formatHelpMapAsHtml = staticmethod(QgsProcessingUtils.formatHelpMapAsHtml) + QgsProcessingUtils.outputDefinitionIndex = staticmethod(QgsProcessingUtils.outputDefinitionIndex) QgsProcessingUtils.convertToCompatibleFormat = staticmethod(QgsProcessingUtils.convertToCompatibleFormat) QgsProcessingUtils.convertToCompatibleFormatAndLayerName = staticmethod(QgsProcessingUtils.convertToCompatibleFormatAndLayerName) QgsProcessingUtils.combineFields = staticmethod(QgsProcessingUtils.combineFields) diff --git a/python/core/auto_generated/processing/qgsprocessingutils.sip.in b/python/core/auto_generated/processing/qgsprocessingutils.sip.in index 82649ee9268e..6f7c25c739a8 100644 --- a/python/core/auto_generated/processing/qgsprocessingutils.sip.in +++ b/python/core/auto_generated/processing/qgsprocessingutils.sip.in @@ -435,6 +435,12 @@ but not changing the ``basename``. %Docstring Returns a HTML formatted version of the help text encoded in a variant ``map`` for a specified ``algorithm``. +%End + + static int outputDefinitionIndex( const QgsProcessingAlgorithm *algorithm, const QString &name ) /HoldGIL/; +%Docstring +Returns the index of the output matching ``name`` for a specified ``algorithm``. +Matching is done in a case-insensitive manner. %End static QString convertToCompatibleFormat( const QgsVectorLayer *layer, diff --git a/python/gui/auto_additions/qgsmodelgraphicitem.py b/python/gui/auto_additions/qgsmodelgraphicitem.py index d26b525d1263..b892622629bd 100644 --- a/python/gui/auto_additions/qgsmodelgraphicitem.py +++ b/python/gui/auto_additions/qgsmodelgraphicitem.py @@ -10,3 +10,7 @@ QgsModelDesignerFoldButtonGraphicItem.__group__ = ['processing', 'models'] except (NameError, AttributeError): pass +try: + QgsModelDesignerSocketGraphicItem.__group__ = ['processing', 'models'] +except (NameError, AttributeError): + pass diff --git a/python/gui/auto_additions/qgsmodelgraphicsview.py b/python/gui/auto_additions/qgsmodelgraphicsview.py index 2feb247bf445..696d7b9a98a6 100644 --- a/python/gui/auto_additions/qgsmodelgraphicsview.py +++ b/python/gui/auto_additions/qgsmodelgraphicsview.py @@ -1,7 +1,7 @@ # The following has been generated automatically from src/gui/processing/models/qgsmodelgraphicsview.h try: - QgsModelGraphicsView.__attribute_docs__ = {'algorithmDropped': 'Emitted when an algorithm is dropped onto the view.\n', 'inputDropped': 'Emitted when an input parameter is dropped onto the view.\n', 'itemFocused': 'Emitted when an ``item`` is "focused" in the view, i.e. it becomes the active\nitem and should have its properties displayed in any designer windows.\n', 'willBeDeleted': 'Emitted in the destructor when the view is about to be deleted,\nbut is still in a perfectly valid state.\n', 'macroCommandStarted': 'Emitted when a macro command containing a group of interactions is started in the view.\n', 'macroCommandEnded': 'Emitted when a macro command containing a group of interactions in the view has ended.\n', 'beginCommand': 'Emitted when an undo command is started in the view.\n', 'endCommand': 'Emitted when an undo command in the view has ended.\n', 'deleteSelectedItems': 'Emitted when the selected items should be deleted;\n'} - QgsModelGraphicsView.__signal_arguments__ = {'algorithmDropped': ['algorithmId: str', 'pos: QPointF'], 'inputDropped': ['inputId: str', 'pos: QPointF'], 'itemFocused': ['item: QgsModelComponentGraphicItem'], 'macroCommandStarted': ['text: str'], 'beginCommand': ['text: str']} + QgsModelGraphicsView.__attribute_docs__ = {'algorithmDropped': 'Emitted when an algorithm is dropped onto the view.\n', 'inputDropped': 'Emitted when an input parameter is dropped onto the view.\n', 'itemFocused': 'Emitted when an ``item`` is "focused" in the view, i.e. it becomes the active\nitem and should have its properties displayed in any designer windows.\n', 'willBeDeleted': 'Emitted in the destructor when the view is about to be deleted,\nbut is still in a perfectly valid state.\n', 'macroCommandStarted': 'Emitted when a macro command containing a group of interactions is started in the view.\n', 'macroCommandEnded': 'Emitted when a macro command containing a group of interactions in the view has ended.\n', 'commandBegun': 'Emitted when an undo command is started in the view.\n', 'commandEnded': 'Emitted when an undo command in the view has ended.\n', 'deleteSelectedItems': 'Emitted when the selected items should be deleted;\n'} + QgsModelGraphicsView.__signal_arguments__ = {'algorithmDropped': ['algorithmId: str', 'pos: QPointF'], 'inputDropped': ['inputId: str', 'pos: QPointF'], 'itemFocused': ['item: QgsModelComponentGraphicItem'], 'macroCommandStarted': ['text: str'], 'commandBegun': ['text: str']} QgsModelGraphicsView.__group__ = ['processing', 'models'] except (NameError, AttributeError): pass diff --git a/python/gui/auto_generated/processing/models/qgsmodelarrowitem.sip.in b/python/gui/auto_generated/processing/models/qgsmodelarrowitem.sip.in index dadcd4801890..2601cc387f57 100644 --- a/python/gui/auto_generated/processing/models/qgsmodelarrowitem.sip.in +++ b/python/gui/auto_generated/processing/models/qgsmodelarrowitem.sip.in @@ -31,6 +31,7 @@ A link arrow item for use in the model designer. { Circle, ArrowHead, + NoMarker }; QgsModelArrowItem( QgsModelComponentGraphicItem *startItem, Qt::Edge startEdge, int startIndex, bool startIsOutgoing, Marker startMarker, QgsModelComponentGraphicItem *endItem, Qt::Edge endEdge, int endIndex, bool endIsIncoming, Marker endMarker ); diff --git a/python/gui/auto_generated/processing/models/qgsmodelcomponentgraphicitem.sip.in b/python/gui/auto_generated/processing/models/qgsmodelcomponentgraphicitem.sip.in index f0f580f6c9c9..69cd6dcc66bc 100644 --- a/python/gui/auto_generated/processing/models/qgsmodelcomponentgraphicitem.sip.in +++ b/python/gui/auto_generated/processing/models/qgsmodelcomponentgraphicitem.sip.in @@ -69,6 +69,7 @@ Returns the model component associated with this item. Returns the model associated with this item. %End + QgsModelGraphicsView *view(); %Docstring Returns the associated view. @@ -183,6 +184,14 @@ Returns the best link point to use for a link originating at a specified ``other - edge: item edge for calculated best link point %End + QgsModelDesignerSocketGraphicItem *outSocketAt( int index ) const; +%Docstring +Returns the output socket graphics items at the specified ``index``. + +May return ``None`` if no corresponding output socket exists. +%End + + virtual void editComment(); %Docstring Called when the comment attached to the item should be edited. @@ -345,6 +354,11 @@ Ownership of ``parameter`` is transferred to the item. virtual QPicture iconPicture() const; + + virtual int linkPointCount( Qt::Edge edge ) const; + + virtual QString linkPointText( Qt::Edge edge, int index ) const; + virtual void updateStoredComponentPosition( const QPointF &pos, const QSizeF &size ); diff --git a/python/gui/auto_generated/processing/models/qgsmodelgraphicitem.sip.in b/python/gui/auto_generated/processing/models/qgsmodelgraphicitem.sip.in index 65f29fb20da0..a0e9d410b317 100644 --- a/python/gui/auto_generated/processing/models/qgsmodelgraphicitem.sip.in +++ b/python/gui/auto_generated/processing/models/qgsmodelgraphicitem.sip.in @@ -47,6 +47,11 @@ for the button. The button will be rendered at the specified ``position`` and `` + QPointF getPosition(); +%Docstring +Returns the button's position. +%End + void setPosition( const QPointF &position ); %Docstring Sets the button's ``position``. @@ -114,6 +119,65 @@ If ``folded`` is ``True``, the button represents the collapsed state for the ite }; +class QgsModelDesignerSocketGraphicItem : QgsModelDesignerFlatButtonGraphicItem +{ +%Docstring(signature="appended") +A socket allowing linking component together + +.. warning:: + + Not stable API + +.. versionadded:: 3.42 +%End + +%TypeHeaderCode +#include "qgsmodelgraphicitem.h" +%End + public: + QgsModelDesignerSocketGraphicItem( QgsModelComponentGraphicItem *parent /TransferThis/, QgsProcessingModelComponent *component, int index, const QPointF &position, Qt::Edge edge, const QSizeF &size = QSizeF( 11, 11 ) ); +%Docstring +Constructor for QgsModelDesignerSocketGraphicItem, with the specified ``parent`` item. + +The ``index`` argument specifies whether the input or output index of this socket inside the component +And the ``edge`` argument specifies if it's an input socket( Qt.Edge.TopEdge ) or output ( Qt.Edge.BottomEdge ) + +The sockets will be rendered at the specified ``position`` +%End + + virtual void paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0 ); + + int index() const; +%Docstring +Returns the index of this socket in either QgsModelDesignerSocketGraphicItem.mInSockets +or QgsModelDesignerSocketGraphicItem.mOutSockets array +%End + Qt::Edge edge() const; +%Docstring +Returns on which edge this socket is: +* Qt.Edge.TopEdge for input socket +* Qt.Edge.BottomEdge for output socket +%End + bool isInput() const; +%Docstring +Returns whether the socket is an input socket or not + +Convenient function around mEdge member +%End + QgsProcessingModelComponent *component(); +%Docstring +Return the component associated to the socket */ +%End + QgsModelComponentGraphicItem *componentItem(); +%Docstring +Return the parent GraphicItem (:py:class:`QgsModelComponentGraphicItem`) associated to the socket */ +%End + signals: + + +}; + + /************************************************************************ * This file has been generated automatically from * * * diff --git a/python/gui/auto_generated/processing/models/qgsmodelgraphicsscene.sip.in b/python/gui/auto_generated/processing/models/qgsmodelgraphicsscene.sip.in index 2acc2c4ae196..d135bb19ff55 100644 --- a/python/gui/auto_generated/processing/models/qgsmodelgraphicsscene.sip.in +++ b/python/gui/auto_generated/processing/models/qgsmodelgraphicsscene.sip.in @@ -103,6 +103,16 @@ Returns the topmost component item at a specified ``position``. QgsModelComponentGraphicItem *groupBoxItem( const QString &uuid ); %Docstring Returns the graphic item corresponding to the specified group box ``uuid``. +%End + + QgsModelChildAlgorithmGraphicItem *childAlgorithmItem( const QString &childId ); +%Docstring +Returns the graphic item corresponding to the specified specified child algorithm +%End + + QgsModelComponentGraphicItem *parameterItem( const QString &name ); +%Docstring +Returns the :py:class:`QgsModelComponentGraphicItem` corresponding to the specified child algorithm %End void selectAll(); diff --git a/python/gui/auto_generated/processing/models/qgsmodelgraphicsview.sip.in b/python/gui/auto_generated/processing/models/qgsmodelgraphicsview.sip.in index c17a913eb7aa..f9ff3b09ed2a 100644 --- a/python/gui/auto_generated/processing/models/qgsmodelgraphicsview.sip.in +++ b/python/gui/auto_generated/processing/models/qgsmodelgraphicsview.sip.in @@ -79,6 +79,16 @@ Starts a macro command, containing a group of interactions in the view. Ends a macro command, containing a group of interactions in the view. %End + void beginCommand( const QString &text ); +%Docstring +Starts a single undo command +%End + + void endCommand(); +%Docstring +Ends a single undo command +%End + enum ClipboardOperation { @@ -162,12 +172,12 @@ Emitted when a macro command containing a group of interactions is started in th Emitted when a macro command containing a group of interactions in the view has ended. %End - void beginCommand( const QString &text ); + void commandBegun( const QString &text ); %Docstring Emitted when an undo command is started in the view. %End - void endCommand(); + void commandEnded(); %Docstring Emitted when an undo command in the view has ended. %End diff --git a/python/gui/auto_generated/processing/qgsprocessingmodelerparameterwidget.sip.in b/python/gui/auto_generated/processing/qgsprocessingmodelerparameterwidget.sip.in index 1566fe2b5236..117e08614104 100644 --- a/python/gui/auto_generated/processing/qgsprocessingmodelerparameterwidget.sip.in +++ b/python/gui/auto_generated/processing/qgsprocessingmodelerparameterwidget.sip.in @@ -81,6 +81,17 @@ The ``compatibleDataTypes`` list corresponds to the compatible data types from :py:class:`QgsProcessing`.SourceType. %End + QList availableSourcesForChild(); +%Docstring +HACK : Ugly workaround to expose the compatible sources for a parameter definition ( :py:class:`QgsProcessingParameterDefinition`) +Idealy we should no rely on any UI-related function to get this + +see also :py:class:`QgsProcessingModelAlgorithm`.availableSourcesForChild + +The available sources are created on widget creation in :py:func:`populateSources` +%End + + void setExpressionHelpText( const QString &text ); %Docstring Set the expected expression format ``text``, which is shown in the expression builder dialog for the widget diff --git a/src/core/processing/qgsprocessingutils.cpp b/src/core/processing/qgsprocessingutils.cpp index 091f13e6a0aa..b19c11c01f98 100644 --- a/src/core/processing/qgsprocessingutils.cpp +++ b/src/core/processing/qgsprocessingutils.cpp @@ -1361,6 +1361,18 @@ QString QgsProcessingUtils::formatHelpMapAsHtml( const QVariantMap &map, const Q return s; } +int QgsProcessingUtils::outputDefinitionIndex( const QgsProcessingAlgorithm *algorithm, const QString &name ) +{ + int index = 0; + for ( const QgsProcessingOutputDefinition *def : algorithm->outputDefinitions() ) + { + if ( def->name().compare( name, Qt::CaseInsensitive ) == 0 ) + return index; + index++; + } + return -1; +} + QString convertToCompatibleFormatInternal( const QgsVectorLayer *vl, bool selectedFeaturesOnly, const QString &baseName, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingContext &context, QgsProcessingFeedback *feedback, QString *layerName, long long featureLimit, const QString &filterExpression, bool renameFid ) { diff --git a/src/core/processing/qgsprocessingutils.h b/src/core/processing/qgsprocessingutils.h index fd82eded11c5..1fb206608d7b 100644 --- a/src/core/processing/qgsprocessingutils.h +++ b/src/core/processing/qgsprocessingutils.h @@ -427,6 +427,12 @@ class CORE_EXPORT QgsProcessingUtils */ static QString formatHelpMapAsHtml( const QVariantMap &map, const QgsProcessingAlgorithm *algorithm ); + /** + * Returns the index of the output matching \a name for a specified \a algorithm. + * Matching is done in a case-insensitive manner. + */ + static int outputDefinitionIndex( const QgsProcessingAlgorithm *algorithm, const QString &name ) SIP_HOLDGIL; + /** * Converts a source vector \a layer to a file path of a vector layer of compatible format. * diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 6e3b283a034d..dcf6fe897efb 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -446,6 +446,7 @@ set(QGIS_GUI_SRCS processing/models/qgsmodelviewmousehandles.cpp processing/models/qgsmodelviewrubberband.cpp processing/models/qgsmodelviewtool.cpp + processing/models/qgsmodelviewtoollink.cpp processing/models/qgsmodelviewtoolpan.cpp processing/models/qgsmodelviewtoolselect.cpp processing/models/qgsmodelviewtooltemporarykeypan.cpp @@ -1432,6 +1433,7 @@ set(QGIS_GUI_HDRS processing/models/qgsmodelviewrubberband.h processing/models/qgsmodelviewtool.h processing/models/qgsmodelviewtoolpan.h + processing/models/qgsmodelviewtoollink.h processing/models/qgsmodelviewtoolselect.h processing/models/qgsmodelviewtooltemporarykeypan.h processing/models/qgsmodelviewtooltemporarykeyzoom.h diff --git a/src/gui/processing/models/qgsmodelarrowitem.cpp b/src/gui/processing/models/qgsmodelarrowitem.cpp index 341ee429f53f..47d0356bdbcd 100644 --- a/src/gui/processing/models/qgsmodelarrowitem.cpp +++ b/src/gui/processing/models/qgsmodelarrowitem.cpp @@ -97,6 +97,8 @@ void QgsModelArrowItem::paint( QPainter *painter, const QStyleOptionGraphicsItem case Marker::ArrowHead: drawArrowHead( painter, mStartPoint, path().pointAtPercent( 0.0 ) - path().pointAtPercent( 0.05 ) ); break; + case Marker::NoMarker: + break; } switch ( mEndMarker ) @@ -107,6 +109,8 @@ void QgsModelArrowItem::paint( QPainter *painter, const QStyleOptionGraphicsItem case Marker::ArrowHead: drawArrowHead( painter, mEndPoint, path().pointAtPercent( 1.0 ) - path().pointAtPercent( 0.95 ) ); break; + case Marker::NoMarker: + break; } painter->setBrush( Qt::NoBrush ); diff --git a/src/gui/processing/models/qgsmodelarrowitem.h b/src/gui/processing/models/qgsmodelarrowitem.h index 55209f74d55f..44d1c1fadc41 100644 --- a/src/gui/processing/models/qgsmodelarrowitem.h +++ b/src/gui/processing/models/qgsmodelarrowitem.h @@ -40,6 +40,7 @@ class GUI_EXPORT QgsModelArrowItem : public QObject, public QGraphicsPathItem { Circle, ArrowHead, + NoMarker }; /** diff --git a/src/gui/processing/models/qgsmodelcomponentgraphicitem.cpp b/src/gui/processing/models/qgsmodelcomponentgraphicitem.cpp index 85dd244f7139..ae627fab0741 100644 --- a/src/gui/processing/models/qgsmodelcomponentgraphicitem.cpp +++ b/src/gui/processing/models/qgsmodelcomponentgraphicitem.cpp @@ -94,6 +94,11 @@ QgsProcessingModelAlgorithm *QgsModelComponentGraphicItem::model() return mModel; } +const QgsProcessingModelAlgorithm *QgsModelComponentGraphicItem::model() const +{ + return mModel; +} + QgsModelGraphicsView *QgsModelComponentGraphicItem::view() { if ( scene()->views().isEmpty() ) @@ -269,11 +274,21 @@ QVariant QgsModelComponentGraphicItem::itemChange( QGraphicsItem::GraphicsItemCh { mExpandTopButton = new QgsModelDesignerFoldButtonGraphicItem( this, mComponent->linksCollapsed( Qt::TopEdge ), QPointF( 0, 0 ) ); connect( mExpandTopButton, &QgsModelDesignerFoldButtonGraphicItem::folded, this, [=]( bool folded ) { fold( Qt::TopEdge, folded ); } ); + + for ( int idx = 0; idx < linkPointCount( Qt::TopEdge ); ++idx ) + { + mInSockets.append( new QgsModelDesignerSocketGraphicItem( this, mComponent.get(), idx, QPointF( 0, 0 ), Qt::TopEdge ) ); + } } if ( linkPointCount( Qt::BottomEdge ) ) { mExpandBottomButton = new QgsModelDesignerFoldButtonGraphicItem( this, mComponent->linksCollapsed( Qt::BottomEdge ), QPointF( 0, 0 ) ); connect( mExpandBottomButton, &QgsModelDesignerFoldButtonGraphicItem::folded, this, [=]( bool folded ) { fold( Qt::BottomEdge, folded ); } ); + + for ( int idx = 0; idx < linkPointCount( Qt::BottomEdge ); ++idx ) + { + mOutSockets.append( new QgsModelDesignerSocketGraphicItem( this, mComponent.get(), idx, QPointF( 0, 0 ), Qt::BottomEdge ) ); + } } mInitialized = true; updateButtonPositions(); @@ -376,7 +391,7 @@ void QgsModelComponentGraphicItem::paint( QPainter *painter, const QStyleOptionG painter->setPen( QPen( QApplication::palette().color( QPalette::Text ) ) ); - if ( linkPointCount( Qt::TopEdge ) || linkPointCount( Qt::BottomEdge ) ) + if ( linkPointCount( Qt::TopEdge ) ) { h = -( fm.height() * 1.2 ); h = h - componentSize.height() / 2.0 + 5; @@ -395,7 +410,9 @@ void QgsModelComponentGraphicItem::paint( QPainter *painter, const QStyleOptionG i += 1; } } - + } + if ( linkPointCount( Qt::BottomEdge ) ) + { h = fm.height() * 1.1; h = h + componentSize.height() / 2.0; pt = QPointF( -componentSize.width() / 2 + 25, h ); @@ -494,6 +511,23 @@ void QgsModelComponentGraphicItem::updateButtonPositions() const QPointF pt = linkPoint( Qt::BottomEdge, -1, false ); mExpandBottomButton->setPosition( QPointF( 0, pt.y() ) ); } + + + bool collapsed = mComponent->linksCollapsed( Qt::TopEdge ); + for ( QgsModelDesignerSocketGraphicItem *socket : std::as_const( mInSockets ) ) + { + const QPointF pt = linkPoint( Qt::TopEdge, socket->index(), true ); + socket->setPosition( pt ); + socket->setVisible( !collapsed ); + } + + collapsed = mComponent->linksCollapsed( Qt::BottomEdge ); + for ( QgsModelDesignerSocketGraphicItem *socket : std::as_const( mOutSockets ) ) + { + const QPointF pt = linkPoint( Qt::BottomEdge, socket->index(), false ); + socket->setPosition( pt ); + socket->setVisible( !collapsed ); + } } QSizeF QgsModelComponentGraphicItem::itemSize() const @@ -535,6 +569,9 @@ void QgsModelComponentGraphicItem::fold( Qt::Edge edge, bool folded ) else if ( QgsProcessingModelOutput *output = dynamic_cast( mComponent.get() ) ) mModel->childAlgorithm( output->childId() ).modelOutput( output->name() ).setLinksCollapsed( edge, folded ); + + updateButtonPositions(); + prepareGeometryChange(); emit updateArrowPaths(); emit changed(); @@ -702,6 +739,15 @@ QPointF QgsModelComponentGraphicItem::calculateAutomaticLinkPoint( const QPointF } } +QgsModelDesignerSocketGraphicItem *QgsModelComponentGraphicItem::outSocketAt( int index ) const +{ + if ( index < 0 || index >= mOutSockets.size() ) + { + return nullptr; + } + return mOutSockets.at( index ); +} + QgsModelParameterGraphicItem::QgsModelParameterGraphicItem( QgsProcessingModelParameter *parameter, QgsProcessingModelAlgorithm *model, QGraphicsItem *parent ) : QgsModelComponentGraphicItem( parameter, model, parent ) { @@ -770,6 +816,39 @@ QPicture QgsModelParameterGraphicItem::iconPicture() const return mPicture; } +int QgsModelParameterGraphicItem::linkPointCount( Qt::Edge edge ) const +{ + switch ( edge ) + { + case Qt::BottomEdge: + return 1; + case Qt::TopEdge: + case Qt::LeftEdge: + case Qt::RightEdge: + break; + } + + return 0; +} + +QString QgsModelParameterGraphicItem::linkPointText( Qt::Edge, int index ) const +{ + if ( index < 0 ) + { + return QString(); + } + + + if ( const QgsProcessingModelParameter *parameter = dynamic_cast< const QgsProcessingModelParameter * >( component() ) ) + { + QString text = this->model()->parameterDefinition( parameter->parameterName() )->type(); + return truncatedTextForItem( text ); + } + + + return QString(); +} + void QgsModelParameterGraphicItem::updateStoredComponentPosition( const QPointF &pos, const QSizeF &size ) { if ( QgsProcessingModelParameter *param = dynamic_cast( component() ) ) diff --git a/src/gui/processing/models/qgsmodelcomponentgraphicitem.h b/src/gui/processing/models/qgsmodelcomponentgraphicitem.h index 088539bf5cd2..b11f59125f4d 100644 --- a/src/gui/processing/models/qgsmodelcomponentgraphicitem.h +++ b/src/gui/processing/models/qgsmodelcomponentgraphicitem.h @@ -32,6 +32,7 @@ class QgsProcessingModelComment; class QgsProcessingModelAlgorithm; class QgsModelDesignerFlatButtonGraphicItem; class QgsModelDesignerFoldButtonGraphicItem; +class QgsModelDesignerSocketGraphicItem; class QgsModelGraphicsView; class QgsModelViewMouseEvent; class QgsProcessingModelGroupBox; @@ -97,6 +98,11 @@ class GUI_EXPORT QgsModelComponentGraphicItem : public QGraphicsObject */ QgsProcessingModelAlgorithm *model(); + /** + * Returns the model associated with this item. + */ + const QgsProcessingModelAlgorithm *model() const SIP_SKIP; + /** * Returns the associated view. */ @@ -229,6 +235,14 @@ class GUI_EXPORT QgsModelComponentGraphicItem : public QGraphicsObject */ QPointF calculateAutomaticLinkPoint( const QPointF &point, Qt::Edge &edge SIP_OUT ) const; + /** + * Returns the output socket graphics items at the specified \a index. + * + * May return NULLPTR if no corresponding output socket exists. + */ + QgsModelDesignerSocketGraphicItem *outSocketAt( int index ) const; + + /** * Called when the comment attached to the item should be edited. * @@ -367,6 +381,10 @@ class GUI_EXPORT QgsModelComponentGraphicItem : public QGraphicsObject QgsModelDesignerFlatButtonGraphicItem *mEditButton = nullptr; QgsModelDesignerFlatButtonGraphicItem *mDeleteButton = nullptr; + QList< QgsModelDesignerSocketGraphicItem * > mInSockets; + QList< QgsModelDesignerSocketGraphicItem * > mOutSockets; + + static constexpr double MIN_COMPONENT_WIDTH = 70; static constexpr double MIN_COMPONENT_HEIGHT = 30; @@ -413,6 +431,9 @@ class GUI_EXPORT QgsModelParameterGraphicItem : public QgsModelComponentGraphicI QColor strokeColor( State state ) const override; QColor textColor( State state ) const override; QPicture iconPicture() const override; + + int linkPointCount( Qt::Edge edge ) const override; + QString linkPointText( Qt::Edge edge, int index ) const override; void updateStoredComponentPosition( const QPointF &pos, const QSizeF &size ) override; protected slots: diff --git a/src/gui/processing/models/qgsmodelgraphicitem.cpp b/src/gui/processing/models/qgsmodelgraphicitem.cpp index 69280957ab76..8f79b9ad2de3 100644 --- a/src/gui/processing/models/qgsmodelgraphicitem.cpp +++ b/src/gui/processing/models/qgsmodelgraphicitem.cpp @@ -14,12 +14,14 @@ ***************************************************************************/ #include "qgsmodelgraphicitem.h" +#include "qgsmodelcomponentgraphicitem.h" #include "moc_qgsmodelgraphicitem.cpp" #include "qgsapplication.h" #include "qgsmodelgraphicsscene.h" #include "qgsmodelgraphicsview.h" #include "qgsmodelviewtool.h" #include "qgsmodelviewmouseevent.h" +#include #include #include @@ -166,4 +168,33 @@ void QgsModelDesignerFoldButtonGraphicItem::modelPressEvent( QgsModelViewMouseEv QgsModelDesignerFlatButtonGraphicItem::modelPressEvent( event ); } + +QgsModelDesignerSocketGraphicItem::QgsModelDesignerSocketGraphicItem( QgsModelComponentGraphicItem *parent, QgsProcessingModelComponent *component, int index, const QPointF &position, Qt::Edge edge, const QSizeF &size ) + : QgsModelDesignerFlatButtonGraphicItem( parent, QPicture(), position, size ) + , mComponentItem( parent ) + , mComponent( component ) + , mIndex( index ) + , mEdge( edge ) +{ +} + +void QgsModelDesignerSocketGraphicItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *, QWidget * ) +{ + painter->setPen( QPen() ); + painter->setBrush( QBrush( QColor( 0, 0, 0, mHoverState ? 200 : 33 ), Qt::SolidPattern ) ); + + painter->setRenderHint( QPainter::Antialiasing ); + + constexpr float DISPLAY_SIZE = 3.2; + painter->drawEllipse( getPosition(), DISPLAY_SIZE, DISPLAY_SIZE ); + /* Uncomment to display bounding box */ +#if 0 + painter->save(); + painter->setPen( QPen() ); + painter->setBrush( QBrush() ); + painter->drawRect( boundingRect() ); + painter->restore(); +#endif +} + ///@endcond diff --git a/src/gui/processing/models/qgsmodelgraphicitem.h b/src/gui/processing/models/qgsmodelgraphicitem.h index 1d826d70665e..c561261ebadf 100644 --- a/src/gui/processing/models/qgsmodelgraphicitem.h +++ b/src/gui/processing/models/qgsmodelgraphicitem.h @@ -18,6 +18,7 @@ #include "qgis.h" #include "qgis_gui.h" +#include "qgsmodelcomponentgraphicitem.h" #include #include @@ -69,6 +70,12 @@ class GUI_EXPORT QgsModelDesignerFlatButtonGraphicItem : public QGraphicsObject virtual void modelPressEvent( QgsModelViewMouseEvent *event ); #endif + /** + * Returns the button's position. + */ + QPointF getPosition() { return mPosition; }; + + /** * Sets the button's \a position. */ @@ -91,12 +98,12 @@ class GUI_EXPORT QgsModelDesignerFlatButtonGraphicItem : public QGraphicsObject * Sets the \a picture to render for the button graphics. */ void setPicture( const QPicture &picture ); + bool mHoverState = false; private: QPicture mPicture; QPointF mPosition; QSizeF mSize; - bool mHoverState = false; }; @@ -140,6 +147,64 @@ class GUI_EXPORT QgsModelDesignerFoldButtonGraphicItem : public QgsModelDesigner bool mFolded = false; }; + +/** + * \ingroup gui + * \brief A socket allowing linking component together + * \warning Not stable API + * \since QGIS 3.42 + */ +class GUI_EXPORT QgsModelDesignerSocketGraphicItem : public QgsModelDesignerFlatButtonGraphicItem +{ + Q_OBJECT + public: + /** + * Constructor for QgsModelDesignerSocketGraphicItem, with the specified \a parent item. + * + * The \a index argument specifies whether the input or output index of this socket inside the component + * And the \a edge argument specifies if it's an input socket( Qt::Edge::TopEdge ) or output ( Qt::Edge::BottomEdge ) + * + * The sockets will be rendered at the specified \a position + */ + QgsModelDesignerSocketGraphicItem( QgsModelComponentGraphicItem *parent SIP_TRANSFERTHIS, QgsProcessingModelComponent *component, int index, const QPointF &position, Qt::Edge edge, const QSizeF &size = QSizeF( 11, 11 ) ); + + void paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr ) override; + /** + * Returns the index of this socket in either QgsModelDesignerSocketGraphicItem::mInSockets + * or QgsModelDesignerSocketGraphicItem::mOutSockets array + */ + int index() const { return mIndex; }; + + /** + * Returns on which edge this socket is: + * * Qt::Edge::TopEdge for input socket + * * Qt::Edge::BottomEdge for output socket + */ + Qt::Edge edge() const { return mEdge; }; + + /** + * Returns whether the socket is an input socket or not + * + * Convenient function around mEdge member + */ + bool isInput() const { return mEdge == Qt::TopEdge; }; + + /** Return the component associated to the socket */ + QgsProcessingModelComponent *component() { return mComponent; }; + + /** Return the parent GraphicItem (QgsModelComponentGraphicItem) associated to the socket */ + QgsModelComponentGraphicItem *componentItem() { return mComponentItem; }; + + signals: + + + private: + QgsModelComponentGraphicItem *mComponentItem = nullptr; + QgsProcessingModelComponent *mComponent = nullptr; + int mIndex = -1; + Qt::Edge mEdge = Qt::Edge::TopEdge; +}; + ///@endcond #endif // QGSMODELGRAPHICITEM_H diff --git a/src/gui/processing/models/qgsmodelgraphicsscene.cpp b/src/gui/processing/models/qgsmodelgraphicsscene.cpp index a2c458b9ce05..84d12d7bc88c 100644 --- a/src/gui/processing/models/qgsmodelgraphicsscene.cpp +++ b/src/gui/processing/models/qgsmodelgraphicsscene.cpp @@ -185,9 +185,13 @@ void QgsModelGraphicsScene::createItems( QgsProcessingModelAlgorithm *model, Qgs continue; QgsModelArrowItem *arrow = nullptr; if ( link.linkIndex == -1 ) - arrow = new QgsModelArrowItem( link.item, QgsModelArrowItem::Marker::Circle, mChildAlgorithmItems.value( it.value().childId() ), parameter->isDestination() ? Qt::BottomEdge : Qt::TopEdge, parameter->isDestination() ? bottomIdx : topIdx, QgsModelArrowItem::Marker::Circle ); + { + arrow = new QgsModelArrowItem( link.item, QgsModelArrowItem::Marker::NoMarker, mChildAlgorithmItems.value( it.value().childId() ), parameter->isDestination() ? Qt::BottomEdge : Qt::TopEdge, parameter->isDestination() ? bottomIdx : topIdx, QgsModelArrowItem::Marker::Circle ); + } else - arrow = new QgsModelArrowItem( link.item, link.edge, link.linkIndex, true, QgsModelArrowItem::Marker::Circle, mChildAlgorithmItems.value( it.value().childId() ), parameter->isDestination() ? Qt::BottomEdge : Qt::TopEdge, parameter->isDestination() ? bottomIdx : topIdx, true, QgsModelArrowItem::Marker::Circle ); + { + arrow = new QgsModelArrowItem( link.item, link.edge, link.linkIndex, true, QgsModelArrowItem::Marker::NoMarker, mChildAlgorithmItems.value( it.value().childId() ), parameter->isDestination() ? Qt::BottomEdge : Qt::TopEdge, parameter->isDestination() ? bottomIdx : topIdx, true, QgsModelArrowItem::Marker::NoMarker ); + } addItem( arrow ); } } @@ -318,6 +322,16 @@ QgsModelComponentGraphicItem *QgsModelGraphicsScene::groupBoxItem( const QString return mGroupBoxItems.value( uuid ); } +QgsModelChildAlgorithmGraphicItem *QgsModelGraphicsScene::childAlgorithmItem( const QString &childId ) +{ + return mChildAlgorithmItems.value( childId ); +} + +QgsModelComponentGraphicItem *QgsModelGraphicsScene::parameterItem( const QString &name ) +{ + return mParameterItems.value( name ); +} + void QgsModelGraphicsScene::selectAll() { //select all items in scene @@ -399,6 +413,9 @@ QList QgsModelGraphicsScene::linkSourcesForPa { LinkSource l; l.item = mParameterItems.value( source.parameterName() ); + l.edge = Qt::BottomEdge; + l.linkIndex = 0; + res.append( l ); break; } diff --git a/src/gui/processing/models/qgsmodelgraphicsscene.h b/src/gui/processing/models/qgsmodelgraphicsscene.h index b4a99f7f200e..cd123117568f 100644 --- a/src/gui/processing/models/qgsmodelgraphicsscene.h +++ b/src/gui/processing/models/qgsmodelgraphicsscene.h @@ -118,6 +118,16 @@ class GUI_EXPORT QgsModelGraphicsScene : public QGraphicsScene */ QgsModelComponentGraphicItem *groupBoxItem( const QString &uuid ); + /** + * Returns the graphic item corresponding to the specified specified child algorithm + */ + QgsModelChildAlgorithmGraphicItem *childAlgorithmItem( const QString &childId ); + + /** + * Returns the QgsModelComponentGraphicItem corresponding to the specified child algorithm + */ + QgsModelComponentGraphicItem *parameterItem( const QString &name ); + /** * Selects all the components in the scene. */ diff --git a/src/gui/processing/models/qgsmodelgraphicsview.cpp b/src/gui/processing/models/qgsmodelgraphicsview.cpp index f9dd4f7c1dc3..5c9e56e92999 100644 --- a/src/gui/processing/models/qgsmodelgraphicsview.cpp +++ b/src/gui/processing/models/qgsmodelgraphicsview.cpp @@ -442,6 +442,17 @@ void QgsModelGraphicsView::endMacroCommand() emit macroCommandEnded(); } +void QgsModelGraphicsView::beginCommand( const QString &text ) +{ + emit commandBegun( text ); +} + +void QgsModelGraphicsView::endCommand() +{ + emit commandEnded(); +} + + void QgsModelGraphicsView::snapSelected() { QgsModelGraphicsScene *s = modelScene(); @@ -481,7 +492,7 @@ void QgsModelGraphicsView::copyItems( const QList paramComponents; @@ -589,7 +600,7 @@ void QgsModelGraphicsView::copyItems( const QListscene()->removeItem( mRubberBandItem ); + delete mRubberBandItem; + } +} + +void QgsModelViewBezierRubberBand::start( QPointF position, Qt::KeyboardModifiers ) +{ + // cppcheck-suppress publicAllocationError + mRubberBandItem = new QGraphicsPathItem(); + mRubberBandItem->setBrush( Qt::NoBrush ); + mRubberBandItem->setPen( pen() ); + mRubberBandStartPos = position; + mRubberBandItem->setZValue( QgsModelGraphicsScene::RubberBand ); + view()->scene()->addItem( mRubberBandItem ); + view()->scene()->update(); +} + +void QgsModelViewBezierRubberBand::update( QPointF position, Qt::KeyboardModifiers ) +{ + if ( !mRubberBandItem ) + { + return; + } + + + // Change the offset + QList controlPoints; + + int offsetX = ( position.x() - mRubberBandStartPos.x() > 0 ) ? 50 : -50; + + controlPoints.append( mRubberBandStartPos ); + controlPoints.append( mRubberBandStartPos + QPointF( offsetX, 0 ) ); + controlPoints.append( position - QPointF( offsetX, 0 ) ); + controlPoints.append( position ); + + QPainterPath path; + + path.moveTo( controlPoints.at( 0 ) ); + path.cubicTo( controlPoints.at( 1 ), controlPoints.at( 2 ), controlPoints.at( 3 ) ); + + mRubberBandItem->setPath( path ); +} + +QRectF QgsModelViewBezierRubberBand::finish( QPointF position, Qt::KeyboardModifiers ) +{ + if ( mRubberBandItem ) + { + view()->scene()->removeItem( mRubberBandItem ); + delete mRubberBandItem; + mRubberBandItem = nullptr; + } + return updateRect( mRubberBandStartPos, position, false, false ); +} diff --git a/src/gui/processing/models/qgsmodelviewrubberband.h b/src/gui/processing/models/qgsmodelviewrubberband.h index 17c74b4f9ff1..089ab21b2009 100644 --- a/src/gui/processing/models/qgsmodelviewrubberband.h +++ b/src/gui/processing/models/qgsmodelviewrubberband.h @@ -27,6 +27,7 @@ class QgsModelGraphicsView; class QGraphicsRectItem; class QGraphicsEllipseItem; +class QGraphicsPathItem; class QGraphicsPolygonItem; /** @@ -161,4 +162,35 @@ class GUI_EXPORT QgsModelViewRectangularRubberBand : public QgsModelViewRubberBa QPointF mRubberBandStartPos; }; +/** + * \ingroup gui + * \brief QgsModelViewBezierRubberBand is a bezier curve rubber band for use within QgsModelGraphicsView widgets. + * \since QGIS 3.44 + */ +class GUI_EXPORT QgsModelViewBezierRubberBand : public QgsModelViewRubberBand +{ + Q_OBJECT + + public: + /** + * Constructor for QgsModelViewRectangularRubberBand. + */ + QgsModelViewBezierRubberBand( QgsModelGraphicsView *view = nullptr ); + QgsModelViewBezierRubberBand *create( QgsModelGraphicsView *view ) const override SIP_FACTORY; + + ~QgsModelViewBezierRubberBand() override; + + void start( QPointF position, Qt::KeyboardModifiers modifiers ) override; + void update( QPointF position, Qt::KeyboardModifiers modifiers ) override; + QRectF finish( QPointF position = QPointF(), Qt::KeyboardModifiers modifiers = Qt::KeyboardModifiers() ) override; + + private: + //! Rubber band item + QGraphicsPathItem *mRubberBandItem = nullptr; + + //! Start of rubber band creation + QPointF mRubberBandStartPos; +}; + + #endif // QGSMODELVIEWRUBBERBAND_H diff --git a/src/gui/processing/models/qgsmodelviewtoollink.cpp b/src/gui/processing/models/qgsmodelviewtoollink.cpp new file mode 100644 index 000000000000..0b7074f3f505 --- /dev/null +++ b/src/gui/processing/models/qgsmodelviewtoollink.cpp @@ -0,0 +1,285 @@ +/*************************************************************************** + qgsmodelviewtoollink.cpp + ------------------------------------ + Date : January 2024 + Copyright : (C) 2024 Valentin Buira + Email : valentin dot buira 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 "qgsmodelviewtoollink.h" +#include "moc_qgsmodelviewtoollink.cpp" +#include "qgsprocessingmodelerparameterwidget.h" +#include "qgsprocessingmodelalgorithm.h" +#include "qgsgui.h" +#include "qgsprocessingguiregistry.h" +#include "qgsprocessingmodelchildalgorithm.h" +#include "qgsmodelgraphicsscene.h" +#include "qgsmodelviewmouseevent.h" +#include "qgsmodelgraphicsview.h" +#include + +QgsModelViewToolLink::QgsModelViewToolLink( QgsModelGraphicsView *view ) + : QgsModelViewTool( view, tr( "Link Tool" ) ) +{ + setCursor( Qt::PointingHandCursor ); + mBezierRubberBand.reset( new QgsModelViewBezierRubberBand( view ) ); + + mBezierRubberBand->setBrush( QBrush( QColor( 0, 0, 0, 63 ) ) ); + mBezierRubberBand->setPen( QPen( QBrush( QColor( 0, 0, 0, 100 ) ), 0, Qt::SolidLine ) ); + + connect( this, &QgsModelViewToolLink::requestRebuildRequired, scene(), &QgsModelGraphicsScene::rebuildRequired ); +} + +void QgsModelViewToolLink::modelMoveEvent( QgsModelViewMouseEvent *event ) +{ + mBezierRubberBand->update( event->modelPoint(), Qt::KeyboardModifiers() ); + + // we need to manually pass this event down to items we want it to go to -- QGraphicsScene doesn't propagate + QList items = scene()->items( event->modelPoint() ); + + QgsModelDesignerSocketGraphicItem *socket = nullptr; + for ( QGraphicsItem *item : items ) + { + if ( ( socket = dynamic_cast( item ) ) + && ( mFromSocket != socket && mFromSocket->edge() != socket->edge() ) ) + { + // snap + socket->modelHoverEnterEvent( event ); + QPointF rubberEndPos = socket->mapToScene( socket->getPosition() ); + mBezierRubberBand->update( rubberEndPos, Qt::KeyboardModifiers() ); + + break; + } + } + + if ( !socket && mLastHoveredSocket && socket != mLastHoveredSocket ) + { + mLastHoveredSocket->modelHoverLeaveEvent( event ); + mLastHoveredSocket = nullptr; + } + else + { + mLastHoveredSocket = socket; + } +} + +void QgsModelViewToolLink::modelReleaseEvent( QgsModelViewMouseEvent *event ) +{ + if ( event->button() != Qt::LeftButton ) + { + return; + } + view()->setTool( mPreviousViewTool ); + mBezierRubberBand->finish( event->modelPoint() ); + + // we need to manually pass this event down to items we want it to go to -- QGraphicsScene doesn't propagate + QList items = scene()->items( event->modelPoint() ); + + mToSocket = nullptr; + + for ( QGraphicsItem *item : items ) + { + if ( QgsModelDesignerSocketGraphicItem *socket = dynamic_cast( item ) ) + { + mToSocket = socket; + break; + } + } + + // Do nothing if cursor didn't land on another socket + if ( !mToSocket ) + { + return; + } + + // Do nothing if from socket and to socket are both input or both output + if ( mFromSocket->edge() == mToSocket->edge() ) + { + return; + } + + view()->beginCommand( tr( "Edit link" ) ); + + QList sources; + + QgsProcessingModelComponent *componentFrom = nullptr; + QgsProcessingModelChildAlgorithm *childTo = nullptr; + + /** + * Reorder input and output socket + * whether the user dragged : + * - From an input socket to an output socket + * - From an output socket to an input socket + * + * In the code, we always come back to the first case + */ + if ( !mToSocket->isInput() ) + { + std::swap( mFromSocket, mToSocket ); + } + + componentFrom = mFromSocket->component(); + childTo = dynamic_cast( mToSocket->component() ); + + + const QgsProcessingParameterDefinition *toParam = childTo->algorithm()->parameterDefinitions().at( mToSocket->index() ); + + QgsProcessingModelChildParameterSource source; + if ( QgsProcessingModelChildAlgorithm *childFrom = dynamic_cast( componentFrom ) ) + { + QString outputName = childFrom->algorithm()->outputDefinitions().at( mFromSocket->index() )->name(); + source = QgsProcessingModelChildParameterSource::fromChildOutput( childFrom->childId(), outputName ); + } + else if ( QgsProcessingModelParameter *paramFrom = dynamic_cast( componentFrom ) ) + { + source = QgsProcessingModelChildParameterSource::fromModelParameter( paramFrom->parameterName() ); + } + + QgsProcessingContext context; + QgsProcessingModelerParameterWidget *widget = QgsGui::processingGuiRegistry()->createModelerParameterWidget( view()->modelScene()->model(), childTo->childId(), toParam, context ); + + + QList compatibleParamSources = widget->availableSourcesForChild(); + delete widget; + + if ( !compatibleParamSources.contains( source ) ) + { + //Type are incomatible + const QString title = tr( "Sockets cannot be connected" ); + const QString message = tr( "Either the sockets are incompatible or there is a circular dependency" ); + scene()->showWarning( message, title, message ); + return; + } + + sources << source; + childTo->addParameterSources( toParam->name(), sources ); + + + //We need to pass the update child algorithm to the model + scene()->model()->setChildAlgorithm( *childTo ); + + view()->endCommand(); + // Redraw + emit requestRebuildRequired(); +} + +bool QgsModelViewToolLink::allowItemInteraction() +{ + return true; +} + +void QgsModelViewToolLink::activate() +{ + mPreviousViewTool = view()->tool(); + + QPointF rubberStartPos = mFromSocket->mapToScene( mFromSocket->getPosition() ); + mBezierRubberBand->start( rubberStartPos, Qt::KeyboardModifiers() ); + + QgsModelViewTool::activate(); +} + +void QgsModelViewToolLink::deactivate() +{ + mBezierRubberBand->finish(); + QgsModelViewTool::deactivate(); +} + +void QgsModelViewToolLink::setFromSocket( QgsModelDesignerSocketGraphicItem *socket ) +{ + mFromSocket = socket; + + if ( mFromSocket->isInput() ) + { + QgsProcessingModelChildAlgorithm *childFrom = dynamic_cast( mFromSocket->component() ); + const QgsProcessingParameterDefinition *param = childFrom->algorithm()->parameterDefinitions().at( mFromSocket->index() ); + + auto currentSources = childFrom->parameterSources().value( param->name() ); + + // we need to manually pass this event down to items we want it to go to -- QGraphicsScene doesn't propagate + QList items = scene()->items(); + QgsProcessingModelChildParameterSource oldSource; + for ( const QgsProcessingModelChildParameterSource &source : std::as_const( currentSources ) ) + { + switch ( source.source() ) + { + case Qgis::ProcessingModelChildParameterSource::ModelParameter: + case Qgis::ProcessingModelChildParameterSource::ChildOutput: + { + oldSource = source; + QgsProcessingModelChildAlgorithm *_alg; + // This is not so nice to have the UI tangled gotta think of a better abstraction later + // Loop trought all items to get the output socket + for ( QGraphicsItem *item : items ) + { + if ( QgsModelDesignerSocketGraphicItem *outputSocket = dynamic_cast( item ) ) + { + if ( ( _alg = dynamic_cast( outputSocket->component() ) ) ) + { + if ( source.outputChildId() != _alg->childId() || outputSocket->isInput() ) + { + continue; + } + int outputIndexForSourceName = QgsProcessingUtils::outputDefinitionIndex( _alg->algorithm(), source.outputName() ); + if ( outputSocket->index() == outputIndexForSourceName ) + { + mFromSocket = outputSocket; + view()->beginCommand( tr( "Edit link" ) ); + } + } + else if ( QgsProcessingModelParameter *_param = dynamic_cast( outputSocket->component() ) ) + { + if ( source.parameterName() == _param->parameterName() ) + { + mFromSocket = outputSocket; + view()->beginCommand( tr( "Edit link" ) ); + } + } + } + } + + //reset to default value + QList newSources; + newSources << QgsProcessingModelChildParameterSource::fromStaticValue( param->defaultValue() ); + + + childFrom->addParameterSources( param->name(), newSources ); + //We need to pass the update child algorithm to the model + scene()->model()->setChildAlgorithm( *childFrom ); + // Redraw + emit requestRebuildRequired(); + + //Get Socket from Source alg / source parameter + QgsModelComponentGraphicItem *item = nullptr; + int socket_index = -1; + if ( oldSource.source() == Qgis::ProcessingModelChildParameterSource::ChildOutput ) + { + item = scene()->childAlgorithmItem( oldSource.outputChildId() ); + socket_index = QgsProcessingUtils::outputDefinitionIndex( _alg->algorithm(), source.outputName() ); + } + else if ( oldSource.source() == Qgis::ProcessingModelChildParameterSource::ModelParameter ) + { + item = scene()->parameterItem( source.parameterName() ); + socket_index = 0; + } + + mFromSocket = item->outSocketAt( socket_index ); + } + + + break; + default: + continue; + } + + // Stop on first iteration to get only one link at a time + break; + } + } +}; diff --git a/src/gui/processing/models/qgsmodelviewtoollink.h b/src/gui/processing/models/qgsmodelviewtoollink.h new file mode 100644 index 000000000000..9e664d23ad5d --- /dev/null +++ b/src/gui/processing/models/qgsmodelviewtoollink.h @@ -0,0 +1,76 @@ +/*************************************************************************** + qgsmodelviewtoollink.h + ---------------------------------- + Date : January 2024 + Copyright : (C) 2024 Valentin Buira + Email : valentin dot buira 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 QGSMODELVIEWTOOLLINK_H +#define QGSMODELVIEWTOOLLINK_H + +#include "qgis_sip.h" +#include "qgis_gui.h" +#include "qgsmodelviewtool.h" +#include "qgsmodelviewrubberband.h" +#include "qgsmodelgraphicitem.h" +#include + +#define SIP_NO_FILE + +/** + * \ingroup gui + * \brief Model designer view tool for linking socket together + * This tool is not exposed in the UI and is only set when the select tool click on a socket + * \since QGIS 3.44 + */ +class GUI_EXPORT QgsModelViewToolLink : public QgsModelViewTool +{ + Q_OBJECT + + public: + /** + * Constructor for QgsModelViewToolLink. + */ + QgsModelViewToolLink( QgsModelGraphicsView *view SIP_TRANSFERTHIS ); + + void modelMoveEvent( QgsModelViewMouseEvent *event ) override; + void modelReleaseEvent( QgsModelViewMouseEvent *event ) override; + bool allowItemInteraction() override; + void activate() override; + void deactivate() override; + + /** + * Set the from socket + * + * In the case the user started dragging from an already linked input socket + * We need to figure out, which is the output socket used as the source at the other side of the link. + * + * This is used when the user disconnect a a link or relink to another input socket + */ + void setFromSocket( QgsModelDesignerSocketGraphicItem *socket ); + + signals: + /** + * Emitted when a change was made to the model that requires a full rebuild of the scene. + */ + void requestRebuildRequired(); + + private: + std::unique_ptr mBezierRubberBand; + QgsModelDesignerSocketGraphicItem *mFromSocket = nullptr; + QgsModelDesignerSocketGraphicItem *mToSocket = nullptr; + + QgsModelDesignerSocketGraphicItem *mLastHoveredSocket = nullptr; + + /* Used to return to select tool */ + QPointer mPreviousViewTool; +}; +#endif // QGSMODELVIEWTOOLLINK_H diff --git a/src/gui/processing/models/qgsmodelviewtoolselect.cpp b/src/gui/processing/models/qgsmodelviewtoolselect.cpp index 56dc6e5fecf3..2358b4bbbe97 100644 --- a/src/gui/processing/models/qgsmodelviewtoolselect.cpp +++ b/src/gui/processing/models/qgsmodelviewtoolselect.cpp @@ -31,6 +31,8 @@ QgsModelViewToolSelect::QgsModelViewToolSelect( QgsModelGraphicsView *view ) mRubberBand.reset( new QgsModelViewRectangularRubberBand( view ) ); mRubberBand->setBrush( QBrush( QColor( 224, 178, 76, 63 ) ) ); mRubberBand->setPen( QPen( QBrush( QColor( 254, 58, 29, 100 ) ), 0, Qt::DotLine ) ); + + mLinkTool.reset( new QgsModelViewToolLink( view ) ); } QgsModelViewToolSelect::~QgsModelViewToolSelect() @@ -125,7 +127,13 @@ void QgsModelViewToolSelect::modelPressEvent( QgsModelViewMouseEvent *event ) QList items = scene()->items( event->modelPoint() ); for ( QGraphicsItem *item : items ) { - if ( QgsModelDesignerFlatButtonGraphicItem *button = dynamic_cast( item ) ) + if ( QgsModelDesignerSocketGraphicItem *socket = dynamic_cast( item ) ) + { + // Start link tool" + mLinkTool->setFromSocket( socket ); + view()->setTool( mLinkTool.get() ); + } + else if ( QgsModelDesignerFlatButtonGraphicItem *button = dynamic_cast( item ) ) { // arghhh - if the event happens outside the mouse handles bounding rect, then it's ALREADY passed on! if ( mMouseHandles->sceneBoundingRect().contains( event->modelPoint() ) ) diff --git a/src/gui/processing/models/qgsmodelviewtoolselect.h b/src/gui/processing/models/qgsmodelviewtoolselect.h index a28844fc2e5b..b3d9956305e4 100644 --- a/src/gui/processing/models/qgsmodelviewtoolselect.h +++ b/src/gui/processing/models/qgsmodelviewtoolselect.h @@ -20,6 +20,7 @@ #include "qgis_sip.h" #include "qgis_gui.h" #include "qgsmodelviewtool.h" +#include "qgsmodelviewtoollink.h" #include "qgsmodelviewrubberband.h" #include @@ -72,6 +73,8 @@ class GUI_EXPORT QgsModelViewToolSelect : public QgsModelViewTool //! Rubber band item std::unique_ptr mRubberBand; + std::unique_ptr mLinkTool = nullptr; + //! Start position for mouse press QPoint mMousePressStartPos; diff --git a/src/gui/processing/qgsprocessingmodelerparameterwidget.cpp b/src/gui/processing/qgsprocessingmodelerparameterwidget.cpp index 4f26652a1e71..d25fe7034587 100644 --- a/src/gui/processing/qgsprocessingmodelerparameterwidget.cpp +++ b/src/gui/processing/qgsprocessingmodelerparameterwidget.cpp @@ -412,9 +412,9 @@ void QgsProcessingModelerParameterWidget::updateUi() void QgsProcessingModelerParameterWidget::populateSources( const QStringList &compatibleParameterTypes, const QStringList &compatibleOutputTypes, const QList &compatibleDataTypes ) { - const QList sources = mModel->availableSourcesForChild( mChildId, compatibleParameterTypes, compatibleOutputTypes, compatibleDataTypes ); + mSources = mModel->availableSourcesForChild( mChildId, compatibleParameterTypes, compatibleOutputTypes, compatibleDataTypes ); - for ( const QgsProcessingModelChildParameterSource &source : sources ) + for ( const QgsProcessingModelChildParameterSource &source : mSources ) { switch ( source.source() ) { @@ -446,6 +446,11 @@ void QgsProcessingModelerParameterWidget::populateSources( const QStringList &co } } +QList QgsProcessingModelerParameterWidget::availableSourcesForChild() +{ + return mSources; +}; + void QgsProcessingModelerParameterWidget::setExpressionHelpText( const QString &text ) { mExpressionWidget->setExpectedOutputFormat( text ); diff --git a/src/gui/processing/qgsprocessingmodelerparameterwidget.h b/src/gui/processing/qgsprocessingmodelerparameterwidget.h index 2a0d66448e76..55054a525958 100644 --- a/src/gui/processing/qgsprocessingmodelerparameterwidget.h +++ b/src/gui/processing/qgsprocessingmodelerparameterwidget.h @@ -109,6 +109,17 @@ class GUI_EXPORT QgsProcessingModelerParameterWidget : public QWidget, public Qg */ void populateSources( const QStringList &compatibleParameterTypes, const QStringList &compatibleOutputTypes, const QList &compatibleDataTypes ); + /** + * HACK : Ugly workaround to expose the compatible sources for a parameter definition ( QgsProcessingParameterDefinition) + * Idealy we should no rely on any UI-related function to get this + * + * see also QgsProcessingModelAlgorithm::availableSourcesForChild + * + * The available sources are created on widget creation in \see populateSources + */ + QList availableSourcesForChild(); + + /** * Set the expected expression format \a text, which is shown in the expression builder dialog for the widget * when in the "pre-calculated" expression mode. This is purely a text format and no expression validation is made @@ -235,8 +246,10 @@ class GUI_EXPORT QgsProcessingModelerParameterWidget : public QWidget, public Qg QComboBox *mChildOutputCombo = nullptr; QgsFilterLineEdit *mModelOutputName = nullptr; - QList mLimitedSources; + QList mSources; + + QList mLimitedSources; friend class TestProcessingGui; }; diff --git a/src/ui/layout/qgslayoutdesignerbase.ui b/src/ui/layout/qgslayoutdesignerbase.ui index c2e095a1bad4..6eb4081bb8a1 100644 --- a/src/ui/layout/qgslayoutdesignerbase.ui +++ b/src/ui/layout/qgslayoutdesignerbase.ui @@ -452,10 +452,10 @@ :/images/themes/default/mActionSelect.svg:/images/themes/default/mActionSelect.svg - Select/Move Item + Select/Move/Link Item - Select/Move item + Select/Move/Link item V