Compare commits

...

2 Commits

13 changed files with 138 additions and 64 deletions

View File

@ -5,7 +5,7 @@ QgsModelGraphicsView.PasteModeCursor = QgsModelGraphicsView.PasteMode.PasteModeC
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\nactive item and should have its properties displayed in any designer\nwindows.\n', 'willBeDeleted': 'Emitted in the destructor when the view is about to be deleted, but is\nstill in a perfectly valid state.\n', 'macroCommandStarted': 'Emitted when a macro command containing a group of interactions is\nstarted in the view.\n', 'macroCommandEnded': 'Emitted when a macro command containing a group of interactions in the\nview 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.__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\nactive item and should have its properties displayed in any designer\nwindows.\n', 'willBeDeleted': 'Emitted in the destructor when the view is about to be deleted, but is\nstill in a perfectly valid state.\n', 'macroCommandStarted': 'Emitted when a macro command containing a group of interactions is\nstarted in the view.\n', 'macroCommandEnded': 'Emitted when a macro command containing a group of interactions in the\nview 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', 'commandAborted': 'Emitted when an undo command in the view was aborted.\n', 'deleteSelectedItems': 'Emitted when the selected items should be deleted;\n'}
QgsModelGraphicsView.__overridden_methods__ = ['dragEnterEvent', 'dropEvent', 'dragMoveEvent', 'wheelEvent', 'mousePressEvent', 'mouseReleaseEvent', 'mouseMoveEvent', 'mouseDoubleClickEvent', 'keyPressEvent', 'keyReleaseEvent']
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']

View File

@ -44,6 +44,12 @@ made to the model.
%Docstring
Ends the current undo command. This should be called after changes are
made to the model.
%End
void abortUndoCommand();
%Docstring
Aborts pending undo command, tunring last call to beginUndoCommand
obsolete
%End
QgsProcessingModelAlgorithm *model();

View File

@ -90,6 +90,11 @@ Starts a single undo command
Ends a single undo command
%End
void abortCommand();
%Docstring
Aborts pending undo command
%End
enum ClipboardOperation /BaseType=IntEnum/
{
@ -184,6 +189,11 @@ Emitted when an undo command is started in the view.
void commandEnded();
%Docstring
Emitted when an undo command in the view has ended.
%End
void commandAborted();
%Docstring
Emitted when an undo command in the view was aborted.
%End
void deleteSelectedItems();

View File

@ -1,6 +1,6 @@
# 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\nactive item and should have its properties displayed in any designer\nwindows.\n', 'willBeDeleted': 'Emitted in the destructor when the view is about to be deleted, but is\nstill in a perfectly valid state.\n', 'macroCommandStarted': 'Emitted when a macro command containing a group of interactions is\nstarted in the view.\n', 'macroCommandEnded': 'Emitted when a macro command containing a group of interactions in the\nview 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.__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\nactive item and should have its properties displayed in any designer\nwindows.\n', 'willBeDeleted': 'Emitted in the destructor when the view is about to be deleted, but is\nstill in a perfectly valid state.\n', 'macroCommandStarted': 'Emitted when a macro command containing a group of interactions is\nstarted in the view.\n', 'macroCommandEnded': 'Emitted when a macro command containing a group of interactions in the\nview 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', 'commandAborted': 'Emitted when an undo command in the view was aborted.\n', 'deleteSelectedItems': 'Emitted when the selected items should be deleted;\n'}
QgsModelGraphicsView.__overridden_methods__ = ['dragEnterEvent', 'dropEvent', 'dragMoveEvent', 'wheelEvent', 'mousePressEvent', 'mouseReleaseEvent', 'mouseMoveEvent', 'mouseDoubleClickEvent', 'keyPressEvent', 'keyReleaseEvent']
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']

View File

@ -44,6 +44,12 @@ made to the model.
%Docstring
Ends the current undo command. This should be called after changes are
made to the model.
%End
void abortUndoCommand();
%Docstring
Aborts pending undo command, tunring last call to beginUndoCommand
obsolete
%End
QgsProcessingModelAlgorithm *model();

View File

@ -90,6 +90,11 @@ Starts a single undo command
Ends a single undo command
%End
void abortCommand();
%Docstring
Aborts pending undo command
%End
enum ClipboardOperation
{
@ -184,6 +189,11 @@ Emitted when an undo command is started in the view.
void commandEnded();
%Docstring
Emitted when an undo command in the view has ended.
%End
void commandAborted();
%Docstring
Emitted when an undo command in the view was aborted.
%End
void deleteSelectedItems();

View File

@ -345,6 +345,9 @@ QgsModelDesignerDialog::QgsModelDesignerDialog( QWidget *parent, Qt::WindowFlags
connect( mView, &QgsModelGraphicsView::commandEnded, this, [this] {
endUndoCommand();
} );
connect( mView, &QgsModelGraphicsView::commandAborted, this, [this] {
abortUndoCommand();
} );
connect( mView, &QgsModelGraphicsView::deleteSelectedItems, this, [this] {
deleteSelected();
} );
@ -429,6 +432,12 @@ void QgsModelDesignerDialog::endUndoCommand()
setDirty( true );
}
void QgsModelDesignerDialog::abortUndoCommand()
{
if ( mActiveCommand )
mActiveCommand->setObsolete( true );
}
QgsProcessingModelAlgorithm *QgsModelDesignerDialog::model()
{
return mModel.get();

View File

@ -72,6 +72,11 @@ class GUI_EXPORT QgsModelDesignerDialog : public QMainWindow, public Ui::QgsMode
*/
void endUndoCommand();
/**
* Aborts pending undo command, tunring last call to beginUndoCommand obsolete
*/
void abortUndoCommand();
/**
* Returns the model shown in the dialog.
*/

View File

@ -473,6 +473,11 @@ void QgsModelGraphicsView::endCommand()
emit commandEnded();
}
void QgsModelGraphicsView::abortCommand()
{
emit commandAborted();
}
void QgsModelGraphicsView::snapSelected()
{
QgsModelGraphicsScene *s = modelScene();

View File

@ -119,6 +119,11 @@ class GUI_EXPORT QgsModelGraphicsView : public QGraphicsView
*/
void endCommand();
/**
* Aborts pending undo command
*/
void abortCommand();
//! Clipboard operations
enum ClipboardOperation
@ -212,6 +217,11 @@ class GUI_EXPORT QgsModelGraphicsView : public QGraphicsView
*/
void commandEnded();
/**
* Emitted when an undo command in the view was aborted.
*/
void commandAborted();
/**
* Emitted when the selected items should be deleted;
*/

View File

@ -23,7 +23,9 @@
#include "qgsmodelviewmouseevent.h"
#include "qgsmodelviewtoolselect.h"
#include "qgsmodelgraphicsview.h"
#include <QScrollBar>
#include "qgsmodelviewrubberband.h"
#include "qgsmodelgraphicitem.h"
QgsModelViewToolLink::QgsModelViewToolLink( QgsModelGraphicsView *view )
: QgsModelViewTool( view, tr( "Link Tool" ) )
@ -40,22 +42,21 @@ 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<QGraphicsItem *> items = scene()->items( event->modelPoint() );
const QList<QGraphicsItem *> items = scene()->items( event->modelPoint() );
QgsModelDesignerSocketGraphicItem *socket = nullptr;
for ( QGraphicsItem *item : items )
{
if ( ( socket = dynamic_cast<QgsModelDesignerSocketGraphicItem *>( item ) )
&& ( mFromSocket != socket && mFromSocket->edge() != socket->edge() ) )
{
socket = dynamic_cast<QgsModelDesignerSocketGraphicItem *>( item );
if ( !socket || mFromSocket == socket || mFromSocket->edge() == socket->edge() || mFromSocket->component() == socket->component() )
continue;
// snap
socket->modelHoverEnterEvent( event );
QPointF rubberEndPos = socket->mapToScene( socket->position() );
const QPointF rubberEndPos = socket->mapToScene( socket->position() );
mBezierRubberBand->update( rubberEndPos, Qt::KeyboardModifiers() );
break;
}
}
if ( mLastHoveredSocket && socket != mLastHoveredSocket )
{
@ -85,7 +86,7 @@ void QgsModelViewToolLink::modelReleaseEvent( QgsModelViewMouseEvent *event )
view()->setTool( mPreviousViewTool );
// we need to manually pass this event down to items we want it to go to -- QGraphicsScene doesn't propagate
QList<QGraphicsItem *> items = scene()->items( event->modelPoint() );
const QList<QGraphicsItem *> items = scene()->items( event->modelPoint() );
mToSocket = nullptr;
@ -93,6 +94,10 @@ void QgsModelViewToolLink::modelReleaseEvent( QgsModelViewMouseEvent *event )
{
if ( QgsModelDesignerSocketGraphicItem *socket = dynamic_cast<QgsModelDesignerSocketGraphicItem *>( item ) )
{
// Skip if sockets are both input or both output or both from the same algorithm
if ( mFromSocket->edge() == socket->edge() || mFromSocket->component() == socket->component() )
continue;
mToSocket = socket;
break;
}
@ -101,21 +106,12 @@ void QgsModelViewToolLink::modelReleaseEvent( QgsModelViewMouseEvent *event )
// Do nothing if cursor didn't land on another socket
if ( !mToSocket )
{
// but it might have been an unlink, so we properly end the command
view()->endCommand();
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<QgsProcessingModelChildParameterSource> sources;
QgsProcessingModelComponent *componentFrom = nullptr;
QgsProcessingModelChildAlgorithm *childTo = nullptr;
// and we abort any pending unlink command to not litter the undo buffer
view()->abortCommand();
/**
* Reorder input and output socket
@ -130,26 +126,32 @@ void QgsModelViewToolLink::modelReleaseEvent( QgsModelViewMouseEvent *event )
std::swap( mFromSocket, mToSocket );
}
componentFrom = mFromSocket->component();
childTo = dynamic_cast<QgsProcessingModelChildAlgorithm *>( mToSocket->component() );
const QgsProcessingParameterDefinition *toParam = childTo->algorithm()->parameterDefinitions().at( mToSocket->index() );
QgsProcessingModelChildParameterSource source;
if ( QgsProcessingModelChildAlgorithm *childFrom = dynamic_cast<QgsProcessingModelChildAlgorithm *>( componentFrom ) )
QgsProcessingModelComponent *outputComponent = mFromSocket->component();
QgsProcessingModelChildAlgorithm *inputChildAlgorithm = dynamic_cast<QgsProcessingModelChildAlgorithm *>( mToSocket->component() );
if ( !inputChildAlgorithm )
{
QString outputName = childFrom->algorithm()->outputDefinitions().at( mFromSocket->index() )->name();
source = QgsProcessingModelChildParameterSource::fromChildOutput( childFrom->childId(), outputName );
}
else if ( QgsProcessingModelParameter *paramFrom = dynamic_cast<QgsProcessingModelParameter *>( componentFrom ) )
{
source = QgsProcessingModelChildParameterSource::fromModelParameter( paramFrom->parameterName() );
// Should not happen, but checking is cheap!
QgsDebugError( QStringLiteral( "Input is not a QgsProcessingModelChildAlgorithm" ) );
return;
}
QList<QgsProcessingModelChildParameterSource> compatibleParamSources = scene()->model()->availableSourcesForChild( childTo->childId(), toParam );
QgsProcessingModelChildParameterSource newInputParamSource;
QString outParamDescription;
if ( const QgsProcessingModelChildAlgorithm *outputChildAlgorithm = dynamic_cast<QgsProcessingModelChildAlgorithm *>( outputComponent ) )
{
const QString outParamName = outputChildAlgorithm->algorithm()->outputDefinitions().at( mFromSocket->index() )->name();
newInputParamSource = QgsProcessingModelChildParameterSource::fromChildOutput( outputChildAlgorithm->childId(), outParamName );
outParamDescription = outputChildAlgorithm->algorithm()->outputDefinitions().at( mFromSocket->index() )->description();
}
else if ( const QgsProcessingModelParameter *paramFrom = dynamic_cast<QgsProcessingModelParameter *>( outputComponent ) )
{
newInputParamSource = QgsProcessingModelChildParameterSource::fromModelParameter( paramFrom->parameterName() );
outParamDescription = paramFrom->description();
}
if ( !compatibleParamSources.contains( source ) )
const QgsProcessingParameterDefinition *inputParam = inputChildAlgorithm->algorithm()->parameterDefinitions().at( mToSocket->index() );
const QList<QgsProcessingModelChildParameterSource> compatibleInputParamSources = scene()->model()->availableSourcesForChild( inputChildAlgorithm->childId(), inputParam );
if ( !compatibleInputParamSources.contains( newInputParamSource ) )
{
//Type are incomatible
const QString title = tr( "Sockets cannot be connected" );
@ -158,12 +160,12 @@ void QgsModelViewToolLink::modelReleaseEvent( QgsModelViewMouseEvent *event )
return;
}
sources << source;
childTo->addParameterSources( toParam->name(), sources );
view()->beginCommand( tr( "Link %1: %2 to %3: %4" ).arg( outputComponent->description(), outParamDescription, inputChildAlgorithm->description(), inputParam->description() ) );
inputChildAlgorithm->addParameterSources( inputParam->name(), { newInputParamSource } );
//We need to pass the update child algorithm to the model
scene()->model()->setChildAlgorithm( *childTo );
scene()->model()->setChildAlgorithm( *inputChildAlgorithm );
view()->endCommand();
// Redraw
@ -184,7 +186,7 @@ void QgsModelViewToolLink::activate()
mPreviousViewTool = tool;
}
QPointF rubberStartPos = mFromSocket->mapToScene( mFromSocket->position() );
const QPointF rubberStartPos = mFromSocket->mapToScene( mFromSocket->position() );
mBezierRubberBand->start( rubberStartPos, Qt::KeyboardModifiers() );
QgsModelViewTool::activate();
@ -200,28 +202,37 @@ void QgsModelViewToolLink::setFromSocket( QgsModelDesignerSocketGraphicItem *soc
{
mFromSocket = socket;
// If it's an input socket and it's already connected, we want 'From' to be the output at the other end of the connection
if ( mFromSocket->isInput() )
{
QgsProcessingModelChildAlgorithm *childFrom = dynamic_cast<QgsProcessingModelChildAlgorithm *>( mFromSocket->component() );
const QgsProcessingParameterDefinition *param = childFrom->algorithm()->parameterDefinitions().at( mFromSocket->index() );
const QList<QgsProcessingModelChildParameterSource> currentSources = childFrom->parameterSources().value( param->name() );
auto currentSources = childFrom->parameterSources().value( param->name() );
QgsProcessingModelChildParameterSource oldSource;
for ( const QgsProcessingModelChildParameterSource &source : std::as_const( currentSources ) )
for ( const QgsProcessingModelChildParameterSource &source : currentSources )
{
// Was not connected, nothing to do
if ( source.outputChildId().isEmpty() )
break;
switch ( source.source() )
{
case Qgis::ProcessingModelChildParameterSource::ModelParameter:
case Qgis::ProcessingModelChildParameterSource::ChildOutput:
{
oldSource = source;
view()->beginCommand( tr( "Edit link" ) );
view()->beginCommand( tr( "Unlink %1: %2", "Unlink Algorithm: Input" ).arg( childFrom->description(), param->description() ) );
//reset to default value
// reset to default value. Layers/feature sources default to an empty model parameter
QList<QgsProcessingModelChildParameterSource> newSources;
if ( param->type() == QgsProcessingParameterFeatureSource::typeName() || param->type() == QgsProcessingParameterMapLayer::typeName() || param->type() == QgsProcessingParameterMeshLayer::typeName() || param->type() == QgsProcessingParameterPointCloudLayer::typeName() || param->type() == QgsProcessingParameterRasterLayer::typeName() || param->type() == QgsProcessingParameterVectorLayer::typeName() )
{
newSources << QgsProcessingModelChildParameterSource::fromModelParameter( QString() );
}
else
{
// Other parameters default to static value
newSources << QgsProcessingModelChildParameterSource::fromStaticValue( param->defaultValue() );
}
childFrom->addParameterSources( param->name(), newSources );
//We need to pass the update child algorithm to the model
@ -232,9 +243,9 @@ void QgsModelViewToolLink::setFromSocket( QgsModelDesignerSocketGraphicItem *soc
//Get socket from initial source alg / source parameter
QgsModelComponentGraphicItem *item = nullptr;
int socketIndex = -1;
if ( oldSource.source() == Qgis::ProcessingModelChildParameterSource::ChildOutput )
if ( source.source() == Qgis::ProcessingModelChildParameterSource::ChildOutput )
{
item = scene()->childAlgorithmItem( oldSource.outputChildId() );
item = scene()->childAlgorithmItem( source.outputChildId() );
auto algSource = dynamic_cast<QgsProcessingModelChildAlgorithm *>( item->component() );
if ( !algSource )
{
@ -243,7 +254,7 @@ void QgsModelViewToolLink::setFromSocket( QgsModelDesignerSocketGraphicItem *soc
}
socketIndex = QgsProcessingUtils::outputDefinitionIndex( algSource->algorithm(), source.outputName() );
}
else if ( oldSource.source() == Qgis::ProcessingModelChildParameterSource::ModelParameter )
else if ( source.source() == Qgis::ProcessingModelChildParameterSource::ModelParameter )
{
item = scene()->parameterItem( source.parameterName() );
socketIndex = 0;

View File

@ -19,12 +19,14 @@
#include "qgis_sip.h"
#include "qgis_gui.h"
#include "qgsmodelviewtool.h"
#include "qgsmodelviewrubberband.h"
#include "qgsmodelgraphicitem.h"
#include <memory>
#define SIP_NO_FILE
class QgsModelViewBezierRubberBand;
class QgsModelDesignerSocketGraphicItem;
class QgsProcessingModelComponent;
/**
* \ingroup gui
* \brief Model designer view tool for linking socket together

View File

@ -124,7 +124,7 @@ void QgsModelViewToolSelect::modelPressEvent( QgsModelViewMouseEvent *event )
{
// we need to manually pass this event down to items we want it to go to -- QGraphicsScene doesn't propagate events
// to multiple items
QList<QGraphicsItem *> items = scene()->items( event->modelPoint() );
const QList<QGraphicsItem *> items = scene()->items( event->modelPoint() );
for ( QGraphicsItem *item : items )
{
if ( QgsModelDesignerSocketGraphicItem *socket = dynamic_cast<QgsModelDesignerSocketGraphicItem *>( item ) )