QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
qgsprocessingmodelerparameterwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsprocessingmodelerparameterwidget.cpp
3 ---------------------------------------
4 begin : August 2018
5 copyright : (C) 2018 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18
25#include "qgsgui.h"
26#include "qgsguiutils.h"
28#include "qgsapplication.h"
29#include "qgsfilterlineedit.h"
30#include <QHBoxLayout>
31#include <QToolButton>
32#include <QStackedWidget>
33#include <QMenu>
34#include <QLabel>
35#include <QComboBox>
36
38 const QString &childId,
40 QWidget *parent )
41 : QWidget( parent )
42 , mModel( model )
43 , mChildId( childId )
44 , mParameterDefinition( parameter )
45 , mContext( context )
46{
47 setFocusPolicy( Qt::StrongFocus );
48
49 // icon size is a bit bigger than text, but minimum size of 24 so that we get pixel-aligned rendering on low-dpi screens
50 const int iconSize = QgsGuiUtils::scaleIconSize( 24 );
51
52 QHBoxLayout *hLayout = new QHBoxLayout();
53
54 mSourceButton = new QToolButton();
55 mSourceButton->setFocusPolicy( Qt::StrongFocus );
56
57 // button width is 1.25 * icon size, height 1.1 * icon size. But we round to ensure even pixel sizes for equal margins
58 mSourceButton->setFixedSize( 2 * static_cast< int >( 1.25 * iconSize / 2.0 ), 2 * static_cast< int >( iconSize * 1.1 / 2.0 ) );
59 mSourceButton->setIconSize( QSize( iconSize, iconSize ) );
60 mSourceButton->setPopupMode( QToolButton::InstantPopup );
61
62 mSourceMenu = new QMenu( this );
63 connect( mSourceMenu, &QMenu::aboutToShow, this, &QgsProcessingModelerParameterWidget::sourceMenuAboutToShow );
64 connect( mSourceMenu, &QMenu::triggered, this, &QgsProcessingModelerParameterWidget::sourceMenuActionTriggered );
65 mSourceButton->setMenu( mSourceMenu );
66
67 hLayout->addWidget( mSourceButton );
68
69 mStackedWidget = new QStackedWidget();
70
71 mStaticWidgetWrapper.reset( QgsGui::processingGuiRegistry()->createParameterWidgetWrapper( mParameterDefinition, QgsProcessingGui::Modeler ) );
72 if ( mStaticWidgetWrapper )
73 {
74 QWidget *widget = mStaticWidgetWrapper->createWrappedWidget( context );
75 if ( widget )
76 {
77 mHasStaticWrapper = true;
78 mStackedWidget->addWidget( widget );
79 }
80 else
81 mStackedWidget->addWidget( new QWidget() );
82 }
83 else
84 {
85 mStackedWidget->addWidget( new QWidget() );
86 }
87
88 mExpressionWidget = new QgsExpressionLineEdit();
89 mExpressionWidget->registerExpressionContextGenerator( this );
90 mStackedWidget->addWidget( mExpressionWidget );
91
92 mModelInputCombo = new QComboBox();
93 QHBoxLayout *hLayout2 = new QHBoxLayout();
94 hLayout2->setContentsMargins( 0, 0, 0, 0 );
95 hLayout2->addWidget( new QLabel( tr( "Using model input" ) ) );
96 hLayout2->addWidget( mModelInputCombo, 1 );
97 QWidget *hWidget2 = new QWidget();
98 hWidget2->setLayout( hLayout2 );
99 mStackedWidget->addWidget( hWidget2 );
100
101 mChildOutputCombo = new QComboBox();
102 QHBoxLayout *hLayout3 = new QHBoxLayout();
103 hLayout3->setContentsMargins( 0, 0, 0, 0 );
104 hLayout3->addWidget( new QLabel( tr( "Using algorithm output" ) ) );
105 hLayout3->addWidget( mChildOutputCombo, 1 );
106 QWidget *hWidget3 = new QWidget();
107 hWidget3->setLayout( hLayout3 );
108 mStackedWidget->addWidget( hWidget3 );
109
110 if ( mParameterDefinition->isDestination() )
111 {
112 mModelOutputName = new QgsFilterLineEdit();
113 mModelOutputName->setPlaceholderText( tr( "[Enter name if this is a final result]" ) );
114 QHBoxLayout *hLayout4 = new QHBoxLayout();
115 hLayout4->setContentsMargins( 0, 0, 0, 0 );
116 hLayout4->addWidget( mModelOutputName );
117 QWidget *hWidget4 = new QWidget();
118 hWidget4->setLayout( hLayout4 );
119 mStackedWidget->addWidget( hWidget4 );
120 }
121
122 hLayout->setContentsMargins( 0, 0, 0, 0 );
123 hLayout->addWidget( mStackedWidget, 1 );
124
125 setLayout( hLayout );
126 setSourceType( mParameterDefinition->isDestination() ? QgsProcessingModelChildParameterSource::ModelOutput : QgsProcessingModelChildParameterSource::StaticValue );
127}
128
130
132{
133 if ( mStaticWidgetWrapper )
134 mStaticWidgetWrapper->setWidgetContext( context );
135}
136
138{
139 if ( mStaticWidgetWrapper )
140 mStaticWidgetWrapper->registerProcessingContextGenerator( generator );
141}
142
144{
145 return mParameterDefinition;
146}
147
149{
150 if ( mStaticWidgetWrapper )
151 return mStaticWidgetWrapper->createWrappedLabel();
152 else
153 return nullptr;
154}
155
156void QgsProcessingModelerParameterWidget::setWidgetValue( const QgsProcessingModelChildParameterSource &value )
157{
158 // we make a copy of all attributes and store locally, so that users can flick between
159 // sources without losing their current value
160 mStaticValue = value.staticValue();
161 mModelInputParameterName = value.parameterName();
162 mOutputChildId = value.outputChildId();
163 mOutputName = value.outputName();
164 mExpression = value.expression();
165
166 updateUi();
167 setSourceType( value.source() );
168}
169
170void QgsProcessingModelerParameterWidget::setWidgetValue( const QList<QgsProcessingModelChildParameterSource> &values )
171{
172 if ( values.size() == 1 )
173 setWidgetValue( values.at( 0 ) );
174 else
175 {
176 QVariantList r;
177 for ( const QgsProcessingModelChildParameterSource &v : values )
178 r << QVariant::fromValue( v );
179 mStaticValue = r;
180 updateUi();
181 setSourceType( QgsProcessingModelChildParameterSource::StaticValue );
182 }
183}
184
186{
187 if ( mModelOutputName )
188 mModelOutputName->setText( value );
189 setSourceType( QgsProcessingModelChildParameterSource::ModelOutput );
190}
191
193{
194 return currentSourceType() == ModelOutput;
195}
196
198{
199 return mModelOutputName ? mModelOutputName->text().trimmed() : QString();
200}
201
203{
204 switch ( currentSourceType() )
205 {
206 case StaticValue:
207 {
208 const QVariant v = mStaticWidgetWrapper->parameterValue();
209
210 if ( v.type() == QVariant::List )
211 {
212 const QVariantList vList = v.toList();
213 if ( std::all_of( vList.begin(), vList.end(), []( const QVariant & val )
214 {
215 return val.userType() == QMetaType::type( "QgsProcessingModelChildParameterSource" );
216 } ) )
217 {
218 return v;
219 }
220 }
221 return QVariant::fromValue( QgsProcessingModelChildParameterSource::fromStaticValue( v ) );
222 }
223
224 case Expression:
225 return QVariant::fromValue( QgsProcessingModelChildParameterSource::fromExpression( mExpressionWidget->expression() ) );
226
227 case ModelParameter:
228 return QVariant::fromValue( QgsProcessingModelChildParameterSource::fromModelParameter( mModelInputCombo->currentData().toString() ) );
229
230 case ChildOutput:
231 {
232 const QStringList parts = mChildOutputCombo->currentData().toStringList();
233 return QVariant::fromValue( QgsProcessingModelChildParameterSource::fromChildOutput( parts.value( 0, QString() ), parts.value( 1, QString() ) ) );
234 }
235
236 case ModelOutput:
237 return mModelOutputName ? ( mModelOutputName->text().trimmed().isEmpty() ? QVariant() : mModelOutputName->text() ) : QVariant();
238 }
239
240 return QVariant::fromValue( QgsProcessingModelChildParameterSource() );
241}
242
244{
245 if ( mStaticWidgetWrapper )
246 mStaticWidgetWrapper->setDialog( dialog );
247}
248
250{
252 if ( mModel )
253 {
254 const QgsProcessingAlgorithm *alg = nullptr;
255 if ( mModel->childAlgorithms().contains( mChildId ) )
256 alg = mModel->childAlgorithm( mChildId ).algorithm();
257 QgsExpressionContextScope *algorithmScope = QgsExpressionContextUtils::processingAlgorithmScope( alg, QVariantMap(), mContext );
258 c << algorithmScope;
259 QgsExpressionContextScope *modelScope = QgsExpressionContextUtils::processingModelAlgorithmScope( mModel, QVariantMap(), mContext );
260 c << modelScope;
261 QgsExpressionContextScope *childScope = mModel->createExpressionContextScopeForChildAlgorithm( mChildId, mContext, QVariantMap(), QVariantMap() );
262 c << childScope;
263
264 QStringList highlightedVariables = childScope->variableNames();
265 QStringList highlightedFunctions = childScope->functionNames();
266 highlightedVariables += algorithmScope->variableNames();
267 highlightedVariables += mModel->variables().keys();
268 highlightedFunctions += algorithmScope->functionNames();
269 c.setHighlightedVariables( highlightedVariables );
270 c.setHighlightedFunctions( highlightedFunctions );
271 }
272
273 return c;
274}
275
276void QgsProcessingModelerParameterWidget::sourceMenuAboutToShow()
277{
278 mSourceMenu->clear();
279
280 const SourceType currentSource = currentSourceType();
281
282 if ( mParameterDefinition->isDestination() )
283 {
284 QAction *modelOutputAction = mSourceMenu->addAction( tr( "Model Output" ) );
285 modelOutputAction->setCheckable( currentSource == ModelOutput );
286 modelOutputAction->setChecked( currentSource == ModelOutput );
287 modelOutputAction->setData( QgsProcessingModelChildParameterSource::ModelOutput );
288 }
289
290 if ( mHasStaticWrapper )
291 {
292 QAction *fixedValueAction = mSourceMenu->addAction( tr( "Value" ) );
293 fixedValueAction->setCheckable( currentSource == StaticValue );
294 fixedValueAction->setChecked( currentSource == StaticValue );
295 fixedValueAction->setData( QgsProcessingModelChildParameterSource::StaticValue );
296 }
297
298 QAction *calculatedValueAction = mSourceMenu->addAction( tr( "Pre-calculated Value" ) );
299 calculatedValueAction->setCheckable( currentSource == Expression );
300 calculatedValueAction->setChecked( currentSource == Expression );
301 calculatedValueAction->setData( QgsProcessingModelChildParameterSource::Expression );
302
303 QAction *inputValueAction = mSourceMenu->addAction( tr( "Model Input" ) );
304 inputValueAction->setCheckable( currentSource == ModelParameter );
305 inputValueAction->setChecked( currentSource == ModelParameter );
306 inputValueAction->setData( QgsProcessingModelChildParameterSource::ModelParameter );
307
308 QAction *childOutputValueAction = mSourceMenu->addAction( tr( "Algorithm Output" ) );
309 childOutputValueAction->setCheckable( currentSource == ChildOutput );
310 childOutputValueAction->setChecked( currentSource == ChildOutput );
311 childOutputValueAction->setData( QgsProcessingModelChildParameterSource::ChildOutput );
312
313 // TODO - expression text item?
314}
315
316void QgsProcessingModelerParameterWidget::sourceMenuActionTriggered( QAction *action )
317{
318 const QgsProcessingModelChildParameterSource::Source sourceType = static_cast< QgsProcessingModelChildParameterSource::Source >( action->data().toInt() );
319 setSourceType( sourceType );
320}
321
322QgsProcessingModelerParameterWidget::SourceType QgsProcessingModelerParameterWidget::currentSourceType() const
323{
324 return static_cast< SourceType >( mStackedWidget->currentIndex() );
325}
326
327void QgsProcessingModelerParameterWidget::setSourceType( QgsProcessingModelChildParameterSource::Source type )
328{
329 switch ( type )
330 {
331 case QgsProcessingModelChildParameterSource::StaticValue:
332 mStackedWidget->setCurrentIndex( static_cast< int >( StaticValue ) );
333 mSourceButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconFieldInteger.svg" ) ) );
334 mSourceButton->setToolTip( tr( "Value" ) );
335 break;
336
337 case QgsProcessingModelChildParameterSource::Expression:
338 mSourceButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconExpression.svg" ) ) );
339 mStackedWidget->setCurrentIndex( static_cast< int >( Expression ) );
340 mSourceButton->setToolTip( tr( "Pre-calculated Value" ) );
341 break;
342
343 case QgsProcessingModelChildParameterSource::ModelParameter:
344 {
345 mSourceButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "processingModel.svg" ) ) );
346 mStackedWidget->setCurrentIndex( static_cast< int >( ModelParameter ) );
347 mSourceButton->setToolTip( tr( "Model Input" ) );
348 break;
349 }
350
351 case QgsProcessingModelChildParameterSource::ChildOutput:
352 {
353 mSourceButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "processingAlgorithm.svg" ) ) );
354 mStackedWidget->setCurrentIndex( static_cast< int >( ChildOutput ) );
355 mSourceButton->setToolTip( tr( "Algorithm Output" ) );
356 break;
357 }
358
359 case QgsProcessingModelChildParameterSource::ModelOutput:
360 {
361 mSourceButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconModelOutput.svg" ) ) );
362 mStackedWidget->setCurrentIndex( static_cast< int >( ModelOutput ) );
363 mSourceButton->setToolTip( tr( "Model Output" ) );
364 break;
365 }
366
367 case QgsProcessingModelChildParameterSource::ExpressionText:
368 break;
369 }
370}
371
372void QgsProcessingModelerParameterWidget::updateUi()
373{
374 mStaticWidgetWrapper->setParameterValue( mStaticValue, mContext );
375
376 mExpressionWidget->setExpression( mExpression );
377
378 int currentIndex = mModelInputCombo->findData( mModelInputParameterName );
379 if ( currentIndex == -1 && mModelInputCombo->count() > 0 )
380 currentIndex = 0;
381 mModelInputCombo->setCurrentIndex( currentIndex );
382
383 const QStringList parts = QStringList() << mOutputChildId << mOutputName;
384 currentIndex = mChildOutputCombo->findData( parts );
385 if ( currentIndex == -1 && mChildOutputCombo->count() > 0 )
386 currentIndex = 0;
387 mChildOutputCombo->setCurrentIndex( currentIndex );
388}
389
390void QgsProcessingModelerParameterWidget::populateSources( const QStringList &compatibleParameterTypes, const QStringList &compatibleOutputTypes, const QList<int> &compatibleDataTypes )
391{
392 const QList< QgsProcessingModelChildParameterSource > sources = mModel->availableSourcesForChild( mChildId,
393 compatibleParameterTypes, compatibleOutputTypes, compatibleDataTypes );
394
395 for ( const QgsProcessingModelChildParameterSource &source : sources )
396 {
397 switch ( source.source() )
398 {
399 case QgsProcessingModelChildParameterSource::ModelParameter:
400 mModelInputCombo->addItem( mModel->parameterDefinition( source.parameterName() )->description(), source.parameterName() );
401 break;
402
403 case QgsProcessingModelChildParameterSource::ChildOutput:
404 {
405 if ( !mModel->childAlgorithms().contains( source.outputChildId() ) )
406 continue;
407
408 const QgsProcessingModelChildAlgorithm &alg = mModel->childAlgorithm( source.outputChildId() );
409 if ( !alg.algorithm() )
410 continue;
411 const QString outputDescription = alg.algorithm()->outputDefinition( source.outputName() )->description();
412 const QString childDescription = alg.description();
413
414 mChildOutputCombo->addItem( tr( "“%1” from algorithm “%2”" ).arg( outputDescription, childDescription ), QStringList() << source.outputChildId() << source.outputName() );
415 break;
416 }
417
418 case QgsProcessingModelChildParameterSource::StaticValue:
419 case QgsProcessingModelChildParameterSource::Expression:
420 case QgsProcessingModelChildParameterSource::ExpressionText:
421 case QgsProcessingModelChildParameterSource::ModelOutput:
422 break;
423 }
424
425 }
426}
427
429{
430 mExpressionWidget->setExpectedOutputFormat( text );
431}
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Single scope for storing variables and functions for use within a QgsExpressionContext.
QStringList functionNames() const
Retrieves a list of names of functions contained in the scope.
QStringList variableNames() const
Returns a list of variable names contained within the scope.
static QgsExpressionContextScope * processingModelAlgorithmScope(const QgsProcessingModelAlgorithm *model, const QVariantMap &parameters, QgsProcessingContext &context)
Creates a new scope which contains variables and functions relating to a processing model algorithm,...
static QgsExpressionContextScope * processingAlgorithmScope(const QgsProcessingAlgorithm *algorithm, const QVariantMap &parameters, QgsProcessingContext &context)
Creates a new scope which contains variables and functions relating to a processing algorithm,...
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
The QgsExpressionLineEdit widget includes a line edit for entering expressions together with a button...
QString expression() const
Returns the current expression shown in the widget.
void registerExpressionContextGenerator(const QgsExpressionContextGenerator *generator)
Register an expression context generator class that will be used to retrieve an expression context fo...
void setExpectedOutputFormat(const QString &expected)
Set the expected format string, which is shown in the expression builder dialog for the widget.
void setExpression(const QString &expression)
Sets the current expression to show in the widget.
QLineEdit subclass with built in support for clearing the widget's value and handling custom null val...
static QgsProcessingGuiRegistry * processingGuiRegistry()
Returns the global processing gui registry, used for registering the GUI behavior of processing algor...
Definition: qgsgui.cpp:138
Abstract base class for processing algorithms.
An interface for objects which can create Processing contexts.
Contains information about the context in which a processing algorithm is executed.
QgsExpressionContext & expressionContext()
Returns the expression context.
@ Modeler
Modeler dialog.
QgsProcessingModelerParameterWidget(QgsProcessingModelAlgorithm *model, const QString &childId, const QgsProcessingParameterDefinition *parameter, QgsProcessingContext &context, QWidget *parent=nullptr)
Constructor for QgsProcessingModelerParameterWidget, for the specified parameter definition within th...
bool isModelOutput() const
Returns true if the widget is set to the model output mode.
QString modelOutputName() const
Returns the model output name, if isModelOutput() is true.
void setSourceType(QgsProcessingModelChildParameterSource::Source type)
Sets the current source type for the parameter.
QLabel * createLabel()
Creates a label for use identifying the associated parameter.
virtual void setWidgetValue(const QgsProcessingModelChildParameterSource &value)
Sets the current value for the parameter.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
virtual QVariant value() const
Returns the current value of the parameter.
void setDialog(QDialog *dialog)
Sets the parent dialog in which the widget is shown.
void setExpressionHelpText(const QString &text)
Set the expected expression format text, which is shown in the expression builder dialog for the widg...
void setWidgetContext(const QgsProcessingParameterWidgetContext &context)
Sets the context in which the modeler parameter widget is shown, e.g., the parent model algorithm and...
void registerProcessingContextGenerator(QgsProcessingContextGenerator *generator)
Registers a Processing context generator class that will be used to retrieve a Processing context for...
void populateSources(const QStringList &compatibleParameterTypes, const QStringList &compatibleOutputTypes, const QList< int > &compatibleDataTypes)
Populates the widget with available sources for the parameter's value, e.g.
void setToModelOutput(const QString &value)
Sets the widget to a model output, for destination parameters only.
const QgsProcessingParameterDefinition * parameterDefinition() const
Returns the parameter definition associated with this wrapper.
Base class for the definition of processing parameters.
virtual bool isDestination() const
Returns true if this parameter represents a file or layer destination, e.g.
Contains settings which reflect the context in which a Processing parameter widget is shown,...
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c