QGIS API Documentation 3.43.0-Master (e01d6d7c4c0)
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#include "moc_qgsprocessingmodelerparameterwidget.cpp"
26#include "qgsgui.h"
27#include "qgsguiutils.h"
29#include "qgsapplication.h"
32#include "qgsfilterlineedit.h"
33#include <QHBoxLayout>
34#include <QToolButton>
35#include <QStackedWidget>
36#include <QMenu>
37#include <QLabel>
38#include <QComboBox>
39
40QgsProcessingModelerParameterWidget::QgsProcessingModelerParameterWidget( QgsProcessingModelAlgorithm *model, const QString &childId, const QgsProcessingParameterDefinition *parameter, QgsProcessingContext &context, 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 {
55 const QVariantList acceptedSourcesMetadata = mParameterDefinition->metadata().value( QStringLiteral( "model_widget" ) ).toMap().value( QStringLiteral( "accepted_sources" ) ).toList();
56 for ( const QVariant &acceptedSource : acceptedSourcesMetadata )
57 {
58 mLimitedSources.append( static_cast<Qgis::ProcessingModelChildParameterSource>( acceptedSource.toInt() ) );
59 }
60 }
61
62 mSourceButton = new QToolButton();
63 mSourceButton->setFocusPolicy( Qt::StrongFocus );
64
65 // button width is 1.25 * icon size, height 1.1 * icon size. But we round to ensure even pixel sizes for equal margins
66 mSourceButton->setFixedSize( 2 * static_cast<int>( 1.25 * iconSize / 2.0 ), 2 * static_cast<int>( iconSize * 1.1 / 2.0 ) );
67 mSourceButton->setIconSize( QSize( iconSize, iconSize ) );
68 mSourceButton->setPopupMode( QToolButton::InstantPopup );
69
70 mSourceMenu = new QMenu( this );
71 connect( mSourceMenu, &QMenu::aboutToShow, this, &QgsProcessingModelerParameterWidget::sourceMenuAboutToShow );
72 connect( mSourceMenu, &QMenu::triggered, this, &QgsProcessingModelerParameterWidget::sourceMenuActionTriggered );
73 mSourceButton->setMenu( mSourceMenu );
74
75 hLayout->addWidget( mSourceButton );
76
77 mStackedWidget = new QStackedWidget();
78
79 mStaticWidgetWrapper.reset( QgsGui::processingGuiRegistry()->createParameterWidgetWrapper( mParameterDefinition, Qgis::ProcessingMode::Modeler ) );
80 if ( mStaticWidgetWrapper )
81 {
82 QWidget *widget = mStaticWidgetWrapper->createWrappedWidget( context );
83 if ( widget )
84 {
85 mHasStaticWrapper = true;
86 mStackedWidget->addWidget( widget );
87 }
88 else
89 mStackedWidget->addWidget( new QWidget() );
90 }
91 else
92 {
93 mStackedWidget->addWidget( new QWidget() );
94 }
95
96 mExpressionWidget = new QgsExpressionLineEdit();
97 mExpressionWidget->registerExpressionContextGenerator( this );
98 mStackedWidget->addWidget( mExpressionWidget );
99
100 mModelInputCombo = new QComboBox();
101 QHBoxLayout *hLayout2 = new QHBoxLayout();
102 hLayout2->setContentsMargins( 0, 0, 0, 0 );
103 hLayout2->addWidget( new QLabel( tr( "Using model input" ) ) );
104 hLayout2->addWidget( mModelInputCombo, 1 );
105 QWidget *hWidget2 = new QWidget();
106 hWidget2->setLayout( hLayout2 );
107 mStackedWidget->addWidget( hWidget2 );
108
109 mChildOutputCombo = new QComboBox();
110 QHBoxLayout *hLayout3 = new QHBoxLayout();
111 hLayout3->setContentsMargins( 0, 0, 0, 0 );
112 hLayout3->addWidget( new QLabel( tr( "Using algorithm output" ) ) );
113 hLayout3->addWidget( mChildOutputCombo, 1 );
114 QWidget *hWidget3 = new QWidget();
115 hWidget3->setLayout( hLayout3 );
116 mStackedWidget->addWidget( hWidget3 );
117
118 if ( mParameterDefinition->isDestination() )
119 {
120 mModelOutputName = new QgsFilterLineEdit();
121 mModelOutputName->setPlaceholderText( tr( "[Enter name if this is a final result]" ) );
122 QHBoxLayout *hLayout4 = new QHBoxLayout();
123 hLayout4->setContentsMargins( 0, 0, 0, 0 );
124 hLayout4->addWidget( mModelOutputName );
125 QWidget *hWidget4 = new QWidget();
126 hWidget4->setLayout( hLayout4 );
127 mStackedWidget->addWidget( hWidget4 );
128 }
129
130 hLayout->setContentsMargins( 0, 0, 0, 0 );
131 hLayout->addWidget( mStackedWidget, 1 );
132
133 setLayout( hLayout );
135}
136
138
140{
141 if ( mStaticWidgetWrapper )
142 mStaticWidgetWrapper->setWidgetContext( context );
143}
144
146{
147 if ( mStaticWidgetWrapper )
148 mStaticWidgetWrapper->registerProcessingContextGenerator( generator );
149}
150
155
157{
158 if ( mStaticWidgetWrapper )
159 return mStaticWidgetWrapper->createWrappedLabel();
160 else
161 return nullptr;
162}
163
164void QgsProcessingModelerParameterWidget::setWidgetValue( const QgsProcessingModelChildParameterSource &value )
165{
166 // we make a copy of all attributes and store locally, so that users can flick between
167 // sources without losing their current value
168 mStaticValue = value.staticValue();
169 mModelInputParameterName = value.parameterName();
170 mOutputChildId = value.outputChildId();
171 mOutputName = value.outputName();
172 mExpression = value.expression();
173
174 updateUi();
175 setSourceType( value.source() );
176}
177
178void QgsProcessingModelerParameterWidget::setWidgetValue( const QList<QgsProcessingModelChildParameterSource> &values )
179{
180 if ( values.size() == 1 )
181 setWidgetValue( values.at( 0 ) );
182 else
183 {
184 QVariantList r;
185 for ( const QgsProcessingModelChildParameterSource &v : values )
186 r << QVariant::fromValue( v );
187 mStaticValue = r;
188 updateUi();
190 }
191}
192
194{
195 if ( mModelOutputName )
196 mModelOutputName->setText( value );
198}
199
201{
202 return currentSourceType() == ModelOutput;
203}
204
206{
207 return mModelOutputName ? mModelOutputName->text().trimmed() : QString();
208}
209
211{
212 switch ( currentSourceType() )
213 {
214 case StaticValue:
215 {
216 const QVariant v = mStaticWidgetWrapper->parameterValue();
217
218 if ( v.userType() == QMetaType::Type::QVariantList )
219 {
220 const QVariantList vList = v.toList();
221 if ( std::all_of( vList.begin(), vList.end(), []( const QVariant &val ) {
222 return val.userType() == qMetaTypeId<QgsProcessingModelChildParameterSource>();
223 } ) )
224 {
225 return v;
226 }
227 }
228 return QVariant::fromValue( QgsProcessingModelChildParameterSource::fromStaticValue( v ) );
229 }
230
231 case Expression:
232 return QVariant::fromValue( QgsProcessingModelChildParameterSource::fromExpression( mExpressionWidget->expression() ) );
233
234 case ModelParameter:
235 return QVariant::fromValue( QgsProcessingModelChildParameterSource::fromModelParameter( mModelInputCombo->currentData().toString() ) );
236
237 case ChildOutput:
238 {
239 const QStringList parts = mChildOutputCombo->currentData().toStringList();
240 return QVariant::fromValue( QgsProcessingModelChildParameterSource::fromChildOutput( parts.value( 0, QString() ), parts.value( 1, QString() ) ) );
241 }
242
243 case ModelOutput:
244 return mModelOutputName ? ( mModelOutputName->text().trimmed().isEmpty() ? QVariant() : mModelOutputName->text() ) : QVariant();
245 }
246
247 return QVariant::fromValue( QgsProcessingModelChildParameterSource() );
248}
249
251{
252 if ( mStaticWidgetWrapper )
253 mStaticWidgetWrapper->setDialog( dialog );
254}
255
257{
259 if ( mModel )
260 {
261 const QgsProcessingAlgorithm *alg = nullptr;
262 if ( mModel->childAlgorithms().contains( mChildId ) )
263 alg = mModel->childAlgorithm( mChildId ).algorithm();
264 QgsExpressionContextScope *algorithmScope = QgsExpressionContextUtils::processingAlgorithmScope( alg, QVariantMap(), mContext );
265 c << algorithmScope;
266 QgsExpressionContextScope *modelScope = QgsExpressionContextUtils::processingModelAlgorithmScope( mModel, QVariantMap(), mContext );
267 c << modelScope;
268 QgsExpressionContextScope *childScope = mModel->createExpressionContextScopeForChildAlgorithm( mChildId, mContext, QVariantMap(), QVariantMap() );
269 c << childScope;
270
271 QStringList highlightedVariables = childScope->variableNames();
272 QStringList highlightedFunctions = childScope->functionNames();
273 highlightedVariables += algorithmScope->variableNames();
274 highlightedVariables += mModel->variables().keys();
275 highlightedFunctions += algorithmScope->functionNames();
276 c.setHighlightedVariables( highlightedVariables );
277 c.setHighlightedFunctions( highlightedFunctions );
278 }
279
280 return c;
281}
282
283void QgsProcessingModelerParameterWidget::sourceMenuAboutToShow()
284{
285 mSourceMenu->clear();
286
287 const SourceType currentSource = currentSourceType();
288
289 if ( mParameterDefinition->isDestination()
290 && ( mLimitedSources.empty() || mLimitedSources.contains( Qgis::ProcessingModelChildParameterSource::ModelOutput ) ) )
291 {
292 QAction *modelOutputAction = mSourceMenu->addAction( tr( "Model Output" ) );
293 modelOutputAction->setCheckable( currentSource == ModelOutput );
294 modelOutputAction->setChecked( currentSource == ModelOutput );
295 modelOutputAction->setData( QVariant::fromValue( Qgis::ProcessingModelChildParameterSource::ModelOutput ) );
296 }
297
298 if ( mHasStaticWrapper
299 && ( mLimitedSources.empty() || mLimitedSources.contains( Qgis::ProcessingModelChildParameterSource::StaticValue ) ) )
300 {
301 QAction *fixedValueAction = mSourceMenu->addAction( tr( "Value" ) );
302 fixedValueAction->setCheckable( currentSource == StaticValue );
303 fixedValueAction->setChecked( currentSource == StaticValue );
304 fixedValueAction->setData( QVariant::fromValue( Qgis::ProcessingModelChildParameterSource::StaticValue ) );
305 }
306
307 if ( mLimitedSources.empty() || mLimitedSources.contains( Qgis::ProcessingModelChildParameterSource::Expression ) )
308 {
309 QAction *calculatedValueAction = mSourceMenu->addAction( tr( "Pre-calculated Value" ) );
310 calculatedValueAction->setCheckable( currentSource == Expression );
311 calculatedValueAction->setChecked( currentSource == Expression );
312 calculatedValueAction->setData( QVariant::fromValue( Qgis::ProcessingModelChildParameterSource::Expression ) );
313 }
314
315 if ( mLimitedSources.empty() || mLimitedSources.contains( Qgis::ProcessingModelChildParameterSource::ModelParameter ) )
316 {
317 QAction *inputValueAction = mSourceMenu->addAction( tr( "Model Input" ) );
318 inputValueAction->setCheckable( currentSource == ModelParameter );
319 inputValueAction->setChecked( currentSource == ModelParameter );
320 inputValueAction->setData( QVariant::fromValue( Qgis::ProcessingModelChildParameterSource::ModelParameter ) );
321 }
322
323 if ( mLimitedSources.empty() || mLimitedSources.contains( Qgis::ProcessingModelChildParameterSource::ChildOutput ) )
324 {
325 QAction *childOutputValueAction = mSourceMenu->addAction( tr( "Algorithm Output" ) );
326 childOutputValueAction->setCheckable( currentSource == ChildOutput );
327 childOutputValueAction->setChecked( currentSource == ChildOutput );
328 childOutputValueAction->setData( QVariant::fromValue( Qgis::ProcessingModelChildParameterSource::ChildOutput ) );
329 }
330
331 // TODO - expression text item?
332}
333
334void QgsProcessingModelerParameterWidget::sourceMenuActionTriggered( QAction *action )
335{
337 setSourceType( sourceType );
338}
339
340QgsProcessingModelerParameterWidget::SourceType QgsProcessingModelerParameterWidget::currentSourceType() const
341{
342 return static_cast<SourceType>( mStackedWidget->currentIndex() );
343}
344
346{
347 if ( !mLimitedSources.empty() && !mLimitedSources.contains( type ) )
348 {
349 // specified type is not acceptable for this parameter, so override with the first acceptable
350 // type
351 type = mLimitedSources.at( 0 );
352 }
353
354 switch ( type )
355 {
357 mStackedWidget->setCurrentIndex( static_cast<int>( StaticValue ) );
358 mSourceButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconFieldInteger.svg" ) ) );
359 mSourceButton->setToolTip( tr( "Value" ) );
360 break;
361
363 mSourceButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconExpression.svg" ) ) );
364 mStackedWidget->setCurrentIndex( static_cast<int>( Expression ) );
365 mSourceButton->setToolTip( tr( "Pre-calculated Value" ) );
366 break;
367
369 {
370 mSourceButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "processingModel.svg" ) ) );
371 mStackedWidget->setCurrentIndex( static_cast<int>( ModelParameter ) );
372 mSourceButton->setToolTip( tr( "Model Input" ) );
373 break;
374 }
375
377 {
378 mSourceButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "processingAlgorithm.svg" ) ) );
379 mStackedWidget->setCurrentIndex( static_cast<int>( ChildOutput ) );
380 mSourceButton->setToolTip( tr( "Algorithm Output" ) );
381 break;
382 }
383
385 {
386 mSourceButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconModelOutput.svg" ) ) );
387 mStackedWidget->setCurrentIndex( static_cast<int>( ModelOutput ) );
388 mSourceButton->setToolTip( tr( "Model Output" ) );
389 break;
390 }
391
393 break;
394 }
395}
396
397void QgsProcessingModelerParameterWidget::updateUi()
398{
399 mStaticWidgetWrapper->setParameterValue( mStaticValue, mContext );
400
401 mExpressionWidget->setExpression( mExpression );
402
403 int currentIndex = mModelInputCombo->findData( mModelInputParameterName );
404 if ( currentIndex == -1 && mModelInputCombo->count() > 0 )
405 currentIndex = 0;
406 mModelInputCombo->setCurrentIndex( currentIndex );
407
408 const QStringList parts = QStringList() << mOutputChildId << mOutputName;
409 currentIndex = mChildOutputCombo->findData( parts );
410 if ( currentIndex == -1 && mChildOutputCombo->count() > 0 )
411 currentIndex = 0;
412 mChildOutputCombo->setCurrentIndex( currentIndex );
413}
414
415void QgsProcessingModelerParameterWidget::populateSources( const QStringList &compatibleParameterTypes, const QStringList &compatibleOutputTypes, const QList<int> &compatibleDataTypes )
416{
417 QgsProcessingModelChildParameterSources sources = mModel->availableSourcesForChild( mChildId, compatibleParameterTypes, compatibleOutputTypes, compatibleDataTypes );
418
419 for ( const QgsProcessingModelChildParameterSource &source : sources )
420 {
421 switch ( source.source() )
422 {
424 mModelInputCombo->addItem( mModel->parameterDefinition( source.parameterName() )->description(), source.parameterName() );
425 break;
426
428 {
429 if ( !mModel->childAlgorithms().contains( source.outputChildId() ) )
430 continue;
431
432 const QgsProcessingModelChildAlgorithm &alg = mModel->childAlgorithm( source.outputChildId() );
433 if ( !alg.algorithm() )
434 continue;
435 const QString outputDescription = alg.algorithm()->outputDefinition( source.outputName() )->description();
436 const QString childDescription = alg.description();
437
438 mChildOutputCombo->addItem( tr( "“%1” from algorithm “%2”" ).arg( outputDescription, childDescription ), QStringList() << source.outputChildId() << source.outputName() );
439 break;
440 }
441
446 break;
447 }
448 }
449}
450
452{
453 mExpressionWidget->setExpectedOutputFormat( text );
454}
@ Modeler
Modeler mode.
ProcessingModelChildParameterSource
Processing model child parameter sources.
Definition qgis.h:3713
@ 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.
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...
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:155
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.
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 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.
QVariantMap metadata() const
Returns the parameter's freeform metadata.
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