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