QGIS API Documentation 3.99.0-Master (7d2ca374f2d)
Loading...
Searching...
No Matches
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
20
22#include "qgsapplication.h"
26#include "qgsfilterlineedit.h"
27#include "qgsgui.h"
28#include "qgsguiutils.h"
33
34#include <QComboBox>
35#include <QHBoxLayout>
36#include <QLabel>
37#include <QMenu>
38#include <QStackedWidget>
39#include <QString>
40#include <QToolButton>
41
42#include "moc_qgsprocessingmodelerparameterwidget.cpp"
43
44using namespace Qt::StringLiterals;
45
46QgsProcessingModelerParameterWidget::QgsProcessingModelerParameterWidget( QgsProcessingModelAlgorithm *model, const QString &childId, const QgsProcessingParameterDefinition *parameter, QgsProcessingContext &context, QWidget *parent )
47 : QWidget( parent )
48 , mModel( model )
49 , mChildId( childId )
50 , mParameterDefinition( parameter )
51 , mContext( context )
52{
53 setFocusPolicy( Qt::StrongFocus );
54
55 // icon size is a bit bigger than text, but minimum size of 24 so that we get pixel-aligned rendering on low-dpi screens
56 const int iconSize = QgsGuiUtils::scaleIconSize( 24 );
57
58 QHBoxLayout *hLayout = new QHBoxLayout();
59
60 {
61 const QVariantList acceptedSourcesMetadata = mParameterDefinition->metadata().value( u"model_widget"_s ).toMap().value( u"accepted_sources"_s ).toList();
62 for ( const QVariant &acceptedSource : acceptedSourcesMetadata )
63 {
64 mLimitedSources.append( static_cast<Qgis::ProcessingModelChildParameterSource>( acceptedSource.toInt() ) );
65 }
66 }
67
68 mSourceButton = new QToolButton();
69 mSourceButton->setFocusPolicy( Qt::StrongFocus );
70
71 // button width is 1.25 * icon size, height 1.1 * icon size. But we round to ensure even pixel sizes for equal margins
72 mSourceButton->setFixedSize( 2 * static_cast<int>( 1.25 * iconSize / 2.0 ), 2 * static_cast<int>( iconSize * 1.1 / 2.0 ) );
73 mSourceButton->setIconSize( QSize( iconSize, iconSize ) );
74 mSourceButton->setPopupMode( QToolButton::InstantPopup );
75
76 mSourceMenu = new QMenu( this );
77 connect( mSourceMenu, &QMenu::aboutToShow, this, &QgsProcessingModelerParameterWidget::sourceMenuAboutToShow );
78 connect( mSourceMenu, &QMenu::triggered, this, &QgsProcessingModelerParameterWidget::sourceMenuActionTriggered );
79 mSourceButton->setMenu( mSourceMenu );
80
81 hLayout->addWidget( mSourceButton );
82
83 mStackedWidget = new QStackedWidget();
84
85 mStaticWidgetWrapper.reset( QgsGui::processingGuiRegistry()->createParameterWidgetWrapper( mParameterDefinition, Qgis::ProcessingMode::Modeler ) );
86 if ( mStaticWidgetWrapper )
87 {
89 QWidget *widget = mStaticWidgetWrapper->createWrappedWidget( context );
90 if ( widget )
91 {
92 mHasStaticWrapper = true;
93 mStackedWidget->addWidget( widget );
94 }
95 else
96 mStackedWidget->addWidget( new QWidget() );
97 }
98 else
99 {
100 mStackedWidget->addWidget( new QWidget() );
101 }
102
103 mExpressionWidget = new QgsExpressionLineEdit();
104 mExpressionWidget->registerExpressionContextGenerator( this );
105 mStackedWidget->addWidget( mExpressionWidget );
107
108 mModelInputCombo = new QComboBox();
109 QHBoxLayout *hLayout2 = new QHBoxLayout();
110 hLayout2->setContentsMargins( 0, 0, 0, 0 );
111 hLayout2->addWidget( new QLabel( tr( "Using model input" ) ) );
112 hLayout2->addWidget( mModelInputCombo, 1 );
113 QWidget *hWidget2 = new QWidget();
114 hWidget2->setLayout( hLayout2 );
115 mStackedWidget->addWidget( hWidget2 );
116 connect( mModelInputCombo, qOverload< int >( &QComboBox::currentIndexChanged ), this, &QgsProcessingModelerParameterWidget::changed );
117
118 mChildOutputCombo = new QComboBox();
119 QHBoxLayout *hLayout3 = new QHBoxLayout();
120 hLayout3->setContentsMargins( 0, 0, 0, 0 );
121 hLayout3->addWidget( new QLabel( tr( "Using algorithm output" ) ) );
122 hLayout3->addWidget( mChildOutputCombo, 1 );
123 QWidget *hWidget3 = new QWidget();
124 hWidget3->setLayout( hLayout3 );
125 mStackedWidget->addWidget( hWidget3 );
126 connect( mChildOutputCombo, qOverload< int >( &QComboBox::currentIndexChanged ), this, &QgsProcessingModelerParameterWidget::changed );
127
128 if ( mParameterDefinition->isDestination() )
129 {
130 mModelOutputName = new QgsFilterLineEdit();
131 mModelOutputName->setPlaceholderText( tr( "[Enter name if this is a final result]" ) );
132 QHBoxLayout *hLayout4 = new QHBoxLayout();
133 hLayout4->setContentsMargins( 0, 0, 0, 0 );
134 hLayout4->addWidget( mModelOutputName );
135 QWidget *hWidget4 = new QWidget();
136 hWidget4->setLayout( hLayout4 );
137 mStackedWidget->addWidget( hWidget4 );
139 }
140
141 hLayout->setContentsMargins( 0, 0, 0, 0 );
142 hLayout->addWidget( mStackedWidget, 1 );
143
144 setLayout( hLayout );
146}
147
149
151{
152 if ( mStaticWidgetWrapper )
153 mStaticWidgetWrapper->setWidgetContext( context );
154}
155
157{
158 if ( mStaticWidgetWrapper )
159 mStaticWidgetWrapper->registerProcessingContextGenerator( generator );
160}
161
166
168{
169 if ( mStaticWidgetWrapper )
170 return mStaticWidgetWrapper->createWrappedLabel();
171 else
172 return nullptr;
173}
174
175void QgsProcessingModelerParameterWidget::setWidgetValue( const QgsProcessingModelChildParameterSource &value )
176{
177 // we make a copy of all attributes and store locally, so that users can flick between
178 // sources without losing their current value
179 mStaticValue = value.staticValue();
180 mModelInputParameterName = value.parameterName();
181 mOutputChildId = value.outputChildId();
182 mOutputName = value.outputName();
183 mExpression = value.expression();
184
185 updateUi();
186 setSourceType( value.source() );
187 emit changed();
188}
189
190void QgsProcessingModelerParameterWidget::setWidgetValue( const QList<QgsProcessingModelChildParameterSource> &values )
191{
192 if ( values.size() == 1 )
193 setWidgetValue( values.at( 0 ) );
194 else
195 {
196 QVariantList r;
197 for ( const QgsProcessingModelChildParameterSource &v : values )
198 r << QVariant::fromValue( v );
199 mStaticValue = r;
200 updateUi();
202 emit changed();
203 }
204}
205
207{
208 if ( mModelOutputName )
209 mModelOutputName->setText( value );
211 emit changed();
212}
213
215{
216 return currentSourceType() == ModelOutput;
217}
218
220{
221 return mModelOutputName ? mModelOutputName->text().trimmed() : QString();
222}
223
225{
226 switch ( currentSourceType() )
227 {
228 case StaticValue:
229 {
230 const QVariant v = mStaticWidgetWrapper->parameterValue();
231
232 if ( v.userType() == QMetaType::Type::QVariantList )
233 {
234 const QVariantList vList = v.toList();
235 if ( std::all_of( vList.begin(), vList.end(), []( const QVariant &val ) {
236 return val.userType() == qMetaTypeId<QgsProcessingModelChildParameterSource>();
237 } ) )
238 {
239 return v;
240 }
241 }
242 return QVariant::fromValue( QgsProcessingModelChildParameterSource::fromStaticValue( v ) );
243 }
244
245 case Expression:
246 return QVariant::fromValue( QgsProcessingModelChildParameterSource::fromExpression( mExpressionWidget->expression() ) );
247
248 case ModelParameter:
249 return QVariant::fromValue( QgsProcessingModelChildParameterSource::fromModelParameter( mModelInputCombo->currentData().toString() ) );
250
251 case ChildOutput:
252 {
253 const QStringList parts = mChildOutputCombo->currentData().toStringList();
254 return QVariant::fromValue( QgsProcessingModelChildParameterSource::fromChildOutput( parts.value( 0, QString() ), parts.value( 1, QString() ) ) );
255 }
256
257 case ModelOutput:
258 return mModelOutputName ? ( mModelOutputName->text().trimmed().isEmpty() ? QVariant() : mModelOutputName->text() ) : QVariant();
259 }
260
261 return QVariant::fromValue( QgsProcessingModelChildParameterSource() );
262}
263
265{
266 if ( mStaticWidgetWrapper )
267 mStaticWidgetWrapper->setDialog( dialog );
268}
269
271{
272 QgsExpressionContext c = mContext.expressionContext();
273 if ( mModel )
274 {
275 const QgsProcessingAlgorithm *alg = nullptr;
276 if ( mModel->childAlgorithms().contains( mChildId ) )
277 alg = mModel->childAlgorithm( mChildId ).algorithm();
278 QgsExpressionContextScope *algorithmScope = QgsExpressionContextUtils::processingAlgorithmScope( alg, QVariantMap(), mContext );
279 c << algorithmScope;
280 QgsExpressionContextScope *modelScope = QgsExpressionContextUtils::processingModelAlgorithmScope( mModel, QVariantMap(), mContext );
281 c << modelScope;
282 QgsExpressionContextScope *childScope = mModel->createExpressionContextScopeForChildAlgorithm( mChildId, mContext, QVariantMap(), QVariantMap() );
283 c << childScope;
284
285 QStringList highlightedVariables = childScope->variableNames();
286 QStringList highlightedFunctions = childScope->functionNames();
287 highlightedVariables += algorithmScope->variableNames();
288 highlightedVariables += mModel->variables().keys();
289 highlightedFunctions += algorithmScope->functionNames();
290 c.setHighlightedVariables( highlightedVariables );
291 c.setHighlightedFunctions( highlightedFunctions );
292 }
293
294 return c;
295}
296
297void QgsProcessingModelerParameterWidget::sourceMenuAboutToShow()
298{
299 mSourceMenu->clear();
300
301 const SourceType currentSource = currentSourceType();
302
303 if ( mParameterDefinition->isDestination()
304 && ( mLimitedSources.empty() || mLimitedSources.contains( Qgis::ProcessingModelChildParameterSource::ModelOutput ) ) )
305 {
306 QAction *modelOutputAction = mSourceMenu->addAction( tr( "Model Output" ) );
307 modelOutputAction->setCheckable( currentSource == ModelOutput );
308 modelOutputAction->setChecked( currentSource == ModelOutput );
309 modelOutputAction->setData( QVariant::fromValue( Qgis::ProcessingModelChildParameterSource::ModelOutput ) );
310 }
311
312 if ( mHasStaticWrapper
313 && ( mLimitedSources.empty() || mLimitedSources.contains( Qgis::ProcessingModelChildParameterSource::StaticValue ) ) )
314 {
315 QAction *fixedValueAction = mSourceMenu->addAction( tr( "Value" ) );
316 fixedValueAction->setCheckable( currentSource == StaticValue );
317 fixedValueAction->setChecked( currentSource == StaticValue );
318 fixedValueAction->setData( QVariant::fromValue( Qgis::ProcessingModelChildParameterSource::StaticValue ) );
319 }
320
321 if ( mLimitedSources.empty() || mLimitedSources.contains( Qgis::ProcessingModelChildParameterSource::Expression ) )
322 {
323 QAction *calculatedValueAction = mSourceMenu->addAction( tr( "Pre-calculated Value" ) );
324 calculatedValueAction->setCheckable( currentSource == Expression );
325 calculatedValueAction->setChecked( currentSource == Expression );
326 calculatedValueAction->setData( QVariant::fromValue( Qgis::ProcessingModelChildParameterSource::Expression ) );
327 }
328
329 if ( mLimitedSources.empty() || mLimitedSources.contains( Qgis::ProcessingModelChildParameterSource::ModelParameter ) )
330 {
331 QAction *inputValueAction = mSourceMenu->addAction( tr( "Model Input" ) );
332 inputValueAction->setCheckable( currentSource == ModelParameter );
333 inputValueAction->setChecked( currentSource == ModelParameter );
334 inputValueAction->setData( QVariant::fromValue( Qgis::ProcessingModelChildParameterSource::ModelParameter ) );
335 }
336
337 if ( mLimitedSources.empty() || mLimitedSources.contains( Qgis::ProcessingModelChildParameterSource::ChildOutput ) )
338 {
339 QAction *childOutputValueAction = mSourceMenu->addAction( tr( "Algorithm Output" ) );
340 childOutputValueAction->setCheckable( currentSource == ChildOutput );
341 childOutputValueAction->setChecked( currentSource == ChildOutput );
342 childOutputValueAction->setData( QVariant::fromValue( Qgis::ProcessingModelChildParameterSource::ChildOutput ) );
343 }
344
345 // TODO - expression text item?
346}
347
348void QgsProcessingModelerParameterWidget::sourceMenuActionTriggered( QAction *action )
349{
351 setSourceType( sourceType );
352 emit changed();
353}
354
355QgsProcessingModelerParameterWidget::SourceType QgsProcessingModelerParameterWidget::currentSourceType() const
356{
357 return static_cast<SourceType>( mStackedWidget->currentIndex() );
358}
359
361{
362 if ( !mLimitedSources.empty() && !mLimitedSources.contains( type ) )
363 {
364 // specified type is not acceptable for this parameter, so override with the first acceptable
365 // type
366 type = mLimitedSources.at( 0 );
367 }
368
369 switch ( type )
370 {
372 mStackedWidget->setCurrentIndex( static_cast<int>( StaticValue ) );
373 mSourceButton->setIcon( QgsApplication::getThemeIcon( u"mIconFieldInteger.svg"_s ) );
374 mSourceButton->setToolTip( tr( "Value" ) );
375 break;
376
378 mSourceButton->setIcon( QgsApplication::getThemeIcon( u"mIconExpression.svg"_s ) );
379 mStackedWidget->setCurrentIndex( static_cast<int>( Expression ) );
380 mSourceButton->setToolTip( tr( "Pre-calculated Value" ) );
381 break;
382
384 {
385 mSourceButton->setIcon( QgsApplication::getThemeIcon( u"processingModel.svg"_s ) );
386 mStackedWidget->setCurrentIndex( static_cast<int>( ModelParameter ) );
387 mSourceButton->setToolTip( tr( "Model Input" ) );
388 break;
389 }
390
392 {
393 mSourceButton->setIcon( QgsApplication::getThemeIcon( u"processingAlgorithm.svg"_s ) );
394 mStackedWidget->setCurrentIndex( static_cast<int>( ChildOutput ) );
395 mSourceButton->setToolTip( tr( "Algorithm Output" ) );
396 break;
397 }
398
400 {
401 mSourceButton->setIcon( QgsApplication::getThemeIcon( u"mIconModelOutput.svg"_s ) );
402 mStackedWidget->setCurrentIndex( static_cast<int>( ModelOutput ) );
403 mSourceButton->setToolTip( tr( "Model Output" ) );
404 break;
405 }
406
408 break;
409 }
410}
411
412void QgsProcessingModelerParameterWidget::updateUi()
413{
414 mStaticWidgetWrapper->setParameterValue( mStaticValue, mContext );
415
416 mExpressionWidget->setExpression( mExpression );
417
418 int currentIndex = mModelInputCombo->findData( mModelInputParameterName );
419 if ( currentIndex == -1 && mModelInputCombo->count() > 0 )
420 currentIndex = 0;
421 mModelInputCombo->setCurrentIndex( currentIndex );
422
423 const QStringList parts = QStringList() << mOutputChildId << mOutputName;
424 currentIndex = mChildOutputCombo->findData( parts );
425 if ( currentIndex == -1 && mChildOutputCombo->count() > 0 )
426 currentIndex = 0;
427 mChildOutputCombo->setCurrentIndex( currentIndex );
428}
429
430void QgsProcessingModelerParameterWidget::populateSources( const QStringList &compatibleParameterTypes, const QStringList &compatibleOutputTypes, const QList<int> &compatibleDataTypes )
431{
432 QgsProcessingModelChildParameterSources sources = mModel->availableSourcesForChild( mChildId, compatibleParameterTypes, compatibleOutputTypes, compatibleDataTypes );
433
434 for ( const QgsProcessingModelChildParameterSource &source : sources )
435 {
436 switch ( source.source() )
437 {
439 mModelInputCombo->addItem( mModel->parameterDefinition( source.parameterName() )->description(), source.parameterName() );
440 break;
441
443 {
444 if ( !mModel->childAlgorithms().contains( source.outputChildId() ) )
445 continue;
446
447 const QgsProcessingModelChildAlgorithm &alg = mModel->childAlgorithm( source.outputChildId() );
448 if ( !alg.algorithm() )
449 continue;
450 const QString outputDescription = alg.algorithm()->outputDefinition( source.outputName() )->description();
451 const QString childDescription = alg.description();
452
453 mChildOutputCombo->addItem( tr( "“%1” from algorithm “%2”" ).arg( outputDescription, childDescription ), QStringList() << source.outputChildId() << source.outputName() );
454 break;
455 }
456
461 break;
462 }
463 }
464}
465
467{
468 mExpressionWidget->setExpectedOutputFormat( text );
469}
@ Modeler
Modeler mode.
Definition qgis.h:3760
ProcessingModelChildParameterSource
Processing model child parameter sources.
Definition qgis.h:3933
@ ExpressionText
Parameter value is taken from a text with expressions, evaluated just before the algorithm runs.
Definition qgis.h:3938
@ ModelOutput
Parameter value is linked to an output parameter for the model.
Definition qgis.h:3939
@ ChildOutput
Parameter value is taken from an output generated by a child algorithm.
Definition qgis.h:3935
@ ModelParameter
Parameter value is taken from a parent model parameter.
Definition qgis.h:3934
@ StaticValue
Parameter value is a static value.
Definition qgis.h:3936
@ Expression
Parameter value is taken from an expression, evaluated just before the algorithm runs.
Definition qgis.h:3937
void widgetValueHasChanged(QgsAbstractProcessingParameterWidgetWrapper *wrapper)
Emitted whenever the parameter value (as defined by the wrapped widget) is changed.
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...
A widget which includes a line edit for entering expressions together with a button to open the expre...
void expressionChanged(const QString &expression)
Emitted when the expression is changed.
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...
void valueChanged(const QString &value)
Same as textChanged() but with support for null values.
static QgsProcessingGuiRegistry * processingGuiRegistry()
Returns the global processing gui registry, used for registering the GUI behavior of processing algor...
Definition qgsgui.cpp:169
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.
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.
QLabel * createLabel()
Creates a label for use identifying the associated parameter.
virtual void setWidgetValue(const QgsProcessingModelChildParameterSource &value)
Sets the current value for the parameter.
void setSourceType(Qgis::ProcessingModelChildParameterSource type)
Sets the current source type 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 changed()
Emitted whenever the definition of the parameter is changed in the widget.
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.
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