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