QGIS API Documentation 3.43.0-Master (0cdc48caa8d)
qgsmodelviewtoollink.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmodelviewtoollink.cpp
3 ------------------------------------
4 Date : January 2024
5 Copyright : (C) 2024 Valentin Buira
6 Email : valentin dot buira at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17#include "moc_qgsmodelviewtoollink.cpp"
26#include <QScrollBar>
27
28QgsModelViewToolLink::QgsModelViewToolLink( QgsModelGraphicsView *view )
29 : QgsModelViewTool( view, tr( "Link Tool" ) )
30{
31 setCursor( Qt::PointingHandCursor );
32 mBezierRubberBand.reset( new QgsModelViewBezierRubberBand( view ) );
33
34 mBezierRubberBand->setBrush( QBrush( QColor( 0, 0, 0, 63 ) ) );
35 mBezierRubberBand->setPen( QPen( QBrush( QColor( 0, 0, 0, 100 ) ), 0, Qt::SolidLine ) );
36}
37
39{
40 mBezierRubberBand->update( event->modelPoint(), Qt::KeyboardModifiers() );
41
42 // we need to manually pass this event down to items we want it to go to -- QGraphicsScene doesn't propagate
43 QList<QGraphicsItem *> items = scene()->items( event->modelPoint() );
44
45 QgsModelDesignerSocketGraphicItem *socket = nullptr;
46 for ( QGraphicsItem *item : items )
47 {
48 if ( ( socket = dynamic_cast<QgsModelDesignerSocketGraphicItem *>( item ) )
49 && ( mFromSocket != socket && mFromSocket->edge() != socket->edge() ) )
50 {
51 // snap
52 socket->modelHoverEnterEvent( event );
53 QPointF rubberEndPos = socket->mapToScene( socket->position() );
54 mBezierRubberBand->update( rubberEndPos, Qt::KeyboardModifiers() );
55
56 break;
57 }
58 }
59
60 if ( mLastHoveredSocket && socket != mLastHoveredSocket )
61 {
62 mLastHoveredSocket->modelHoverLeaveEvent( event );
63 mLastHoveredSocket = nullptr;
64 }
65
66 if ( socket && socket != mLastHoveredSocket )
67 {
68 mLastHoveredSocket = socket;
69 }
70}
71
73{
74 if ( event->button() != Qt::LeftButton )
75 {
76 return;
77 }
78 mBezierRubberBand->finish( event->modelPoint() );
79 if ( mLastHoveredSocket )
80 {
81 mLastHoveredSocket->modelHoverLeaveEvent( nullptr );
82 mLastHoveredSocket = nullptr;
83 }
84
85 view()->setTool( mPreviousViewTool );
86
87 // we need to manually pass this event down to items we want it to go to -- QGraphicsScene doesn't propagate
88 QList<QGraphicsItem *> items = scene()->items( event->modelPoint() );
89
90 mToSocket = nullptr;
91
92 for ( QGraphicsItem *item : items )
93 {
94 if ( QgsModelDesignerSocketGraphicItem *socket = dynamic_cast<QgsModelDesignerSocketGraphicItem *>( item ) )
95 {
96 mToSocket = socket;
97 break;
98 }
99 }
100
101 // Do nothing if cursor didn't land on another socket
102 if ( !mToSocket )
103 {
104 return;
105 }
106
107 // Do nothing if from socket and to socket are both input or both output
108 if ( mFromSocket->edge() == mToSocket->edge() )
109 {
110 return;
111 }
112
113 view()->beginCommand( tr( "Edit link" ) );
114
115 QList<QgsProcessingModelChildParameterSource> sources;
116
117 QgsProcessingModelComponent *componentFrom = nullptr;
118 QgsProcessingModelChildAlgorithm *childTo = nullptr;
119
128 if ( !mToSocket->isInput() )
129 {
130 std::swap( mFromSocket, mToSocket );
131 }
132
133 componentFrom = mFromSocket->component();
134 childTo = dynamic_cast<QgsProcessingModelChildAlgorithm *>( mToSocket->component() );
135
136
137 const QgsProcessingParameterDefinition *toParam = childTo->algorithm()->parameterDefinitions().at( mToSocket->index() );
138
139 QgsProcessingModelChildParameterSource source;
140 if ( QgsProcessingModelChildAlgorithm *childFrom = dynamic_cast<QgsProcessingModelChildAlgorithm *>( componentFrom ) )
141 {
142 QString outputName = childFrom->algorithm()->outputDefinitions().at( mFromSocket->index() )->name();
143 source = QgsProcessingModelChildParameterSource::fromChildOutput( childFrom->childId(), outputName );
144 }
145 else if ( QgsProcessingModelParameter *paramFrom = dynamic_cast<QgsProcessingModelParameter *>( componentFrom ) )
146 {
147 source = QgsProcessingModelChildParameterSource::fromModelParameter( paramFrom->parameterName() );
148 }
149
150 QList<QgsProcessingModelChildParameterSource> compatibleParamSources = scene()->model()->availableSourcesForChild( childTo->childId(), toParam );
151
152 if ( !compatibleParamSources.contains( source ) )
153 {
154 //Type are incomatible
155 const QString title = tr( "Sockets cannot be connected" );
156 const QString message = tr( "Either the sockets are incompatible or there is a circular dependency" );
157 scene()->showWarning( message, title, message );
158 return;
159 }
160
161 sources << source;
162 childTo->addParameterSources( toParam->name(), sources );
163
164
165 //We need to pass the update child algorithm to the model
166 scene()->model()->setChildAlgorithm( *childTo );
167
168 view()->endCommand();
169 // Redraw
170 scene()->requestRebuildRequired();
171}
172
174{
175 return true;
176}
177
179{
180 QgsModelViewTool *tool = view()->tool();
181 // Make sure we always return to the select tool and not a temporary tool.
182 if ( dynamic_cast<QgsModelViewToolSelect *>( tool ) )
183 {
184 mPreviousViewTool = tool;
185 }
186
187 QPointF rubberStartPos = mFromSocket->mapToScene( mFromSocket->position() );
188 mBezierRubberBand->start( rubberStartPos, Qt::KeyboardModifiers() );
189
191}
192
194{
195 mBezierRubberBand->finish();
197}
198
199void QgsModelViewToolLink::setFromSocket( QgsModelDesignerSocketGraphicItem *socket )
200{
201 mFromSocket = socket;
202
203 if ( mFromSocket->isInput() )
204 {
205 QgsProcessingModelChildAlgorithm *childFrom = dynamic_cast<QgsProcessingModelChildAlgorithm *>( mFromSocket->component() );
206 const QgsProcessingParameterDefinition *param = childFrom->algorithm()->parameterDefinitions().at( mFromSocket->index() );
207
208 auto currentSources = childFrom->parameterSources().value( param->name() );
209
210 QgsProcessingModelChildParameterSource oldSource;
211 for ( const QgsProcessingModelChildParameterSource &source : std::as_const( currentSources ) )
212 {
213 switch ( source.source() )
214 {
217 {
218 oldSource = source;
219 view()->beginCommand( tr( "Edit link" ) );
220
221 //reset to default value
222 QList<QgsProcessingModelChildParameterSource> newSources;
223 newSources << QgsProcessingModelChildParameterSource::fromStaticValue( param->defaultValue() );
224
225
226 childFrom->addParameterSources( param->name(), newSources );
227 //We need to pass the update child algorithm to the model
228 scene()->model()->setChildAlgorithm( *childFrom );
229 // Redraw
230 scene()->requestRebuildRequired();
231
232 //Get socket from initial source alg / source parameter
233 QgsModelComponentGraphicItem *item = nullptr;
234 int socketIndex = -1;
236 {
237 item = scene()->childAlgorithmItem( oldSource.outputChildId() );
238 auto algSource = dynamic_cast<QgsProcessingModelChildAlgorithm *>( item->component() );
239 if ( !algSource )
240 {
241 QgsDebugError( QStringLiteral( "algSource not set, aborting!" ) );
242 return;
243 }
244 socketIndex = QgsProcessingUtils::outputDefinitionIndex( algSource->algorithm(), source.outputName() );
245 }
246 else if ( oldSource.source() == Qgis::ProcessingModelChildParameterSource::ModelParameter )
247 {
248 item = scene()->parameterItem( source.parameterName() );
249 socketIndex = 0;
250 }
251
252 if ( !item )
253 {
254 QgsDebugError( QStringLiteral( "item not set, aborting!" ) );
255 return;
256 }
257
258 mFromSocket = item->outSocketAt( socketIndex );
259 }
260 break;
261
266 continue;
267 }
268
269 // Stop on first iteration to get only one link at a time
270 break;
271 }
272 }
273};
@ ExpressionText
Parameter value is taken from a text with expressions, evaluated just before the algorithm runs.
@ ModelOutput
Parameter value is linked to an output parameter for the model.
@ ChildOutput
Parameter value is taken from an output generated by a child algorithm.
@ ModelParameter
Parameter value is taken from a parent model parameter.
@ StaticValue
Parameter value is a static value.
@ Expression
Parameter value is taken from an expression, evaluated just before the algorithm runs.
A bezier curve rubber band for use within QgsModelGraphicsView widgets.
A mouse event which is the result of a user interaction with a QgsModelGraphicsView.
QPointF modelPoint() const
Returns the event point location in model coordinates.
Model designer view tool for selecting items in the model.
Abstract base class for all model designer view tools.
QgsModelGraphicsView * view() const
Returns the view associated with the tool.
void setCursor(const QCursor &cursor)
Sets a user defined cursor for use when the tool is active.
virtual void deactivate()
Called when tool is deactivated.
virtual void activate()
Called when tool is set as the currently active model tool.
QgsModelGraphicsScene * scene() const
Returns the scene associated with the tool.
QgsProcessingParameterDefinitions parameterDefinitions() const
Returns an ordered list of parameter definitions utilized by the algorithm.
Base class for the definition of processing parameters.
QVariant defaultValue() const
Returns the default value for the parameter.
QgsProcessingAlgorithm * algorithm() const
Returns a pointer to the algorithm which owns this parameter.
QString name() const
Returns the name of the parameter.
static int outputDefinitionIndex(const QgsProcessingAlgorithm *algorithm, const QString &name)
Returns the index of the output matching name for a specified algorithm.
#define QgsDebugError(str)
Definition qgslogger.h:40