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