QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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 {
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 );
109
110 mModelInputCombo = new QComboBox();
111 QHBoxLayout *hLayout2 = new QHBoxLayout();
112 hLayout2->setContentsMargins( 0, 0, 0, 0 );
113 hLayout2->addWidget( new QLabel( tr( "Using model input" ) ) );
114 hLayout2->addWidget( mModelInputCombo, 1 );
115 QWidget *hWidget2 = new QWidget();
116 hWidget2->setLayout( hLayout2 );
117 mStackedWidget->addWidget( hWidget2 );
118 connect( mModelInputCombo, qOverload< int >( &QComboBox::currentIndexChanged ), this, &QgsProcessingModelerParameterWidget::changed );
119
120 mChildOutputCombo = new QComboBox();
121 QHBoxLayout *hLayout3 = new QHBoxLayout();
122 hLayout3->setContentsMargins( 0, 0, 0, 0 );
123 hLayout3->addWidget( new QLabel( tr( "Using algorithm output" ) ) );
124 hLayout3->addWidget( mChildOutputCombo, 1 );
125 QWidget *hWidget3 = new QWidget();
126 hWidget3->setLayout( hLayout3 );
127 mStackedWidget->addWidget( hWidget3 );
128 connect( mChildOutputCombo, qOverload< int >( &QComboBox::currentIndexChanged ), this, &QgsProcessingModelerParameterWidget::changed );
129
130 if ( mParameterDefinition->isDestination() )
131 {
132 mModelOutputName = new QgsFilterLineEdit();
133 mModelOutputName->setPlaceholderText( tr( "[Enter name if this is a final result]" ) );
134 QHBoxLayout *hLayout4 = new QHBoxLayout();
135 hLayout4->setContentsMargins( 0, 0, 0, 0 );
136 hLayout4->addWidget( mModelOutputName );
137 QWidget *hWidget4 = new QWidget();
138 hWidget4->setLayout( hLayout4 );
139 mStackedWidget->addWidget( hWidget4 );
141 }
142
143 hLayout->setContentsMargins( 0, 0, 0, 0 );
144 hLayout->addWidget( mStackedWidget, 1 );
145
146 setLayout( hLayout );
148}
149
151
153{
154 if ( mStaticWidgetWrapper )
155 mStaticWidgetWrapper->setWidgetContext( context );
156}
157
159{
160 if ( mStaticWidgetWrapper )
161 mStaticWidgetWrapper->registerProcessingContextGenerator( generator );
162}
163
168
170{
171 if ( mStaticWidgetWrapper )
172 return mStaticWidgetWrapper->createWrappedLabel();
173 else
174 return nullptr;
175}
176
177void QgsProcessingModelerParameterWidget::setWidgetValue( const QgsProcessingModelChildParameterSource &value )
178{
179 // we make a copy of all attributes and store locally, so that users can flick between
180 // sources without losing their current value
181 mStaticValue = value.staticValue();
182 mModelInputParameterName = value.parameterName();
183 mOutputChildId = value.outputChildId();
184 mOutputName = value.outputName();
185 mExpression = value.expression();
186
187 updateUi();
188 setSourceType( value.source() );
189 emit changed();
190}
191
192void QgsProcessingModelerParameterWidget::setWidgetValue( const QList<QgsProcessingModelChildParameterSource> &values )
193{
194 if ( values.size() == 1 )
195 setWidgetValue( values.at( 0 ) );
196 else
197 {
198 QVariantList r;
199 for ( const QgsProcessingModelChildParameterSource &v : values )
200 r << QVariant::fromValue( v );
201 mStaticValue = r;
202 updateUi();
204 emit changed();
205 }
206}
207
209{
210 if ( mModelOutputName )
211 mModelOutputName->setText( value );
213 emit changed();
214}
215
217{
218 return currentSourceType() == ModelOutput;
219}
220
222{
223 return mModelOutputName ? mModelOutputName->text().trimmed() : QString();
224}
225
227{
228 switch ( currentSourceType() )
229 {
230 case StaticValue:
231 {
232 const QVariant v = mStaticWidgetWrapper->parameterValue();
233
234 if ( v.userType() == QMetaType::Type::QVariantList )
235 {
236 const QVariantList vList = v.toList();
237 if ( std::all_of( vList.begin(), vList.end(), []( const QVariant &val ) { return val.userType() == qMetaTypeId<QgsProcessingModelChildParameterSource>(); } ) )
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() && ( mLimitedSources.empty() || mLimitedSources.contains( Qgis::ProcessingModelChildParameterSource::ModelOutput ) ) )
304 {
305 QAction *modelOutputAction = mSourceMenu->addAction( tr( "Model Output" ) );
306 modelOutputAction->setCheckable( currentSource == ModelOutput );
307 modelOutputAction->setChecked( currentSource == ModelOutput );
308 modelOutputAction->setData( QVariant::fromValue( Qgis::ProcessingModelChildParameterSource::ModelOutput ) );
309 }
310
311 if ( mHasStaticWrapper && ( mLimitedSources.empty() || mLimitedSources.contains( Qgis::ProcessingModelChildParameterSource::StaticValue ) ) )
312 {
313 QAction *fixedValueAction = mSourceMenu->addAction( tr( "Value" ) );
314 fixedValueAction->setCheckable( currentSource == StaticValue );
315 fixedValueAction->setChecked( currentSource == StaticValue );
316 fixedValueAction->setData( QVariant::fromValue( Qgis::ProcessingModelChildParameterSource::StaticValue ) );
317 }
318
319 if ( mLimitedSources.empty() || mLimitedSources.contains( Qgis::ProcessingModelChildParameterSource::Expression ) )
320 {
321 QAction *calculatedValueAction = mSourceMenu->addAction( tr( "Pre-calculated Value" ) );
322 calculatedValueAction->setCheckable( currentSource == Expression );
323 calculatedValueAction->setChecked( currentSource == Expression );
324 calculatedValueAction->setData( QVariant::fromValue( Qgis::ProcessingModelChildParameterSource::Expression ) );
325 }
326
327 if ( mLimitedSources.empty() || mLimitedSources.contains( Qgis::ProcessingModelChildParameterSource::ModelParameter ) )
328 {
329 QAction *inputValueAction = mSourceMenu->addAction( tr( "Model Input" ) );
330 inputValueAction->setCheckable( currentSource == ModelParameter );
331 inputValueAction->setChecked( currentSource == ModelParameter );
332 inputValueAction->setData( QVariant::fromValue( Qgis::ProcessingModelChildParameterSource::ModelParameter ) );
333 }
334
335 if ( mLimitedSources.empty() || mLimitedSources.contains( Qgis::ProcessingModelChildParameterSource::ChildOutput ) )
336 {
337 QAction *childOutputValueAction = mSourceMenu->addAction( tr( "Algorithm Output" ) );
338 childOutputValueAction->setCheckable( currentSource == ChildOutput );
339 childOutputValueAction->setChecked( currentSource == ChildOutput );
340 childOutputValueAction->setData( QVariant::fromValue( Qgis::ProcessingModelChildParameterSource::ChildOutput ) );
341 }
342
343 // TODO - expression text item?
344}
345
346void QgsProcessingModelerParameterWidget::sourceMenuActionTriggered( QAction *action )
347{
349 setSourceType( sourceType );
350 emit changed();
351}
352
353QgsProcessingModelerParameterWidget::SourceType QgsProcessingModelerParameterWidget::currentSourceType() const
354{
355 return static_cast<SourceType>( mStackedWidget->currentIndex() );
356}
357
359{
360 if ( !mLimitedSources.empty() && !mLimitedSources.contains( type ) )
361 {
362 // specified type is not acceptable for this parameter, so override with the first acceptable
363 // type
364 type = mLimitedSources.at( 0 );
365 }
366
367 switch ( type )
368 {
370 mStackedWidget->setCurrentIndex( static_cast<int>( StaticValue ) );
371 mSourceButton->setIcon( QgsApplication::getThemeIcon( u"mIconFieldInteger.svg"_s ) );
372 mSourceButton->setToolTip( tr( "Value" ) );
373 break;
374
376 mSourceButton->setIcon( QgsApplication::getThemeIcon( u"mIconExpression.svg"_s ) );
377 mStackedWidget->setCurrentIndex( static_cast<int>( Expression ) );
378 mSourceButton->setToolTip( tr( "Pre-calculated Value" ) );
379 break;
380
382 {
383 mSourceButton->setIcon( QgsApplication::getThemeIcon( u"processingModel.svg"_s ) );
384 mStackedWidget->setCurrentIndex( static_cast<int>( ModelParameter ) );
385 mSourceButton->setToolTip( tr( "Model Input" ) );
386 break;
387 }
388
390 {
391 mSourceButton->setIcon( QgsApplication::getThemeIcon( u"processingAlgorithm.svg"_s ) );
392 mStackedWidget->setCurrentIndex( static_cast<int>( ChildOutput ) );
393 mSourceButton->setToolTip( tr( "Algorithm Output" ) );
394 break;
395 }
396
398 {
399 mSourceButton->setIcon( QgsApplication::getThemeIcon( u"mIconModelOutput.svg"_s ) );
400 mStackedWidget->setCurrentIndex( static_cast<int>( ModelOutput ) );
401 mSourceButton->setToolTip( tr( "Model Output" ) );
402 break;
403 }
404
406 break;
407 }
408}
409
410void QgsProcessingModelerParameterWidget::updateUi()
411{
412 mStaticWidgetWrapper->setParameterValue( mStaticValue, mContext );
413
414 mExpressionWidget->setExpression( mExpression );
415
416 int currentIndex = mModelInputCombo->findData( mModelInputParameterName );
417 if ( currentIndex == -1 && mModelInputCombo->count() > 0 )
418 currentIndex = 0;
419 mModelInputCombo->setCurrentIndex( currentIndex );
420
421 const QStringList parts = QStringList() << mOutputChildId << mOutputName;
422 currentIndex = mChildOutputCombo->findData( parts );
423 if ( currentIndex == -1 && mChildOutputCombo->count() > 0 )
424 currentIndex = 0;
425 mChildOutputCombo->setCurrentIndex( currentIndex );
426}
427
428void QgsProcessingModelerParameterWidget::populateSources( const QStringList &compatibleParameterTypes, const QStringList &compatibleOutputTypes, const QList<int> &compatibleDataTypes )
429{
430 QgsProcessingModelChildParameterSources sources = mModel->availableSourcesForChild( mChildId, compatibleParameterTypes, compatibleOutputTypes, compatibleDataTypes );
431
432 for ( const QgsProcessingModelChildParameterSource &source : sources )
433 {
434 switch ( source.source() )
435 {
437 mModelInputCombo->addItem( mModel->parameterDefinition( source.parameterName() )->description(), source.parameterName() );
438 break;
439
441 {
442 if ( !mModel->childAlgorithms().contains( source.outputChildId() ) )
443 continue;
444
445 const QgsProcessingModelChildAlgorithm &alg = mModel->childAlgorithm( source.outputChildId() );
446 if ( !alg.algorithm() )
447 continue;
448 const QString outputDescription = alg.algorithm()->outputDefinition( source.outputName() )->description();
449 const QString childDescription = alg.description();
450
451 mChildOutputCombo->addItem( tr( "“%1” from algorithm “%2”" ).arg( outputDescription, childDescription ), QStringList() << source.outputChildId() << source.outputName() );
452 break;
453 }
454
459 break;
460 }
461 }
462}
463
465{
466 mExpressionWidget->setExpectedOutputFormat( text );
467}
@ Modeler
Modeler mode.
Definition qgis.h:3789
ProcessingModelChildParameterSource
Processing model child parameter sources.
Definition qgis.h:3964
@ ExpressionText
Parameter value is taken from a text with expressions, evaluated just before the algorithm runs.
Definition qgis.h:3969
@ ModelOutput
Parameter value is linked to an output parameter for the model.
Definition qgis.h:3970
@ ChildOutput
Parameter value is taken from an output generated by a child algorithm.
Definition qgis.h:3966
@ ModelParameter
Parameter value is taken from a parent model parameter.
Definition qgis.h:3965
@ StaticValue
Parameter value is a static value.
Definition qgis.h:3967
@ Expression
Parameter value is taken from an expression, evaluated just before the algorithm runs.
Definition qgis.h:3968
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