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