QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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 <QHBoxLayout>
30 #include <QToolButton>
31 #include <QStackedWidget>
32 #include <QMenu>
33 #include <QLabel>
34 #include <QComboBox>
35 
37  const QString &childId,
38  const QgsProcessingParameterDefinition *parameter, QgsProcessingContext &context,
39  QWidget *parent )
40  : QWidget( parent )
41  , mModel( model )
42  , mChildId( childId )
43  , mParameterDefinition( parameter )
44  , mContext( context )
45 {
46  setFocusPolicy( Qt::StrongFocus );
47 
48  // 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 
51  QHBoxLayout *hLayout = new QHBoxLayout();
52 
53  mSourceButton = new QToolButton();
54  mSourceButton->setFocusPolicy( Qt::StrongFocus );
55 
56  // button width is 1.25 * icon size, height 1.1 * icon size. But we round to ensure even pixel sizes for equal margins
57  mSourceButton->setFixedSize( 2 * static_cast< int >( 1.25 * iconSize / 2.0 ), 2 * static_cast< int >( iconSize * 1.1 / 2.0 ) );
58  mSourceButton->setIconSize( QSize( iconSize, iconSize ) );
59  mSourceButton->setPopupMode( QToolButton::InstantPopup );
60 
61  mSourceMenu = new QMenu( this );
62  connect( mSourceMenu, &QMenu::aboutToShow, this, &QgsProcessingModelerParameterWidget::sourceMenuAboutToShow );
63  connect( mSourceMenu, &QMenu::triggered, this, &QgsProcessingModelerParameterWidget::sourceMenuActionTriggered );
64  mSourceButton->setMenu( mSourceMenu );
65 
66  hLayout->addWidget( mSourceButton );
67 
68  mStackedWidget = new QStackedWidget();
69 
70  mStaticWidgetWrapper.reset( QgsGui::processingGuiRegistry()->createParameterWidgetWrapper( mParameterDefinition, QgsProcessingGui::Modeler ) );
71  if ( mStaticWidgetWrapper )
72  {
73  QWidget *widget = mStaticWidgetWrapper->createWrappedWidget( context );
74  if ( widget )
75  {
76  mHasStaticWrapper = true;
77  mStackedWidget->addWidget( widget );
78  }
79  else
80  mStackedWidget->addWidget( new QWidget() );
81  }
82  else
83  {
84  mStackedWidget->addWidget( new QWidget() );
85  }
86 
87  mExpressionWidget = new QgsExpressionLineEdit();
88  mExpressionWidget->registerExpressionContextGenerator( this );
89  mStackedWidget->addWidget( mExpressionWidget );
90 
91  mModelInputCombo = new QComboBox();
92  QHBoxLayout *hLayout2 = new QHBoxLayout();
93  hLayout2->setMargin( 0 );
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->setMargin( 0 );
104  hLayout3->setContentsMargins( 0, 0, 0, 0 );
105  hLayout3->addWidget( new QLabel( tr( "Using algorithm output" ) ) );
106  hLayout3->addWidget( mChildOutputCombo, 1 );
107  QWidget *hWidget3 = new QWidget();
108  hWidget3->setLayout( hLayout3 );
109  mStackedWidget->addWidget( hWidget3 );
110 
111  hLayout->setMargin( 0 );
112  hLayout->setContentsMargins( 0, 0, 0, 0 );
113  hLayout->addWidget( mStackedWidget, 1 );
114 
115  setLayout( hLayout );
116  setSourceType( QgsProcessingModelChildParameterSource::StaticValue );
117 }
118 
120 
122 {
123  if ( mStaticWidgetWrapper )
124  mStaticWidgetWrapper->setWidgetContext( context );
125 }
126 
128 {
129  if ( mStaticWidgetWrapper )
130  mStaticWidgetWrapper->registerProcessingContextGenerator( generator );
131 }
132 
134 {
135  return mParameterDefinition;
136 }
137 
139 {
140  if ( mStaticWidgetWrapper )
141  return mStaticWidgetWrapper->createWrappedLabel();
142  else
143  return nullptr;
144 }
145 
146 void QgsProcessingModelerParameterWidget::setWidgetValue( const QgsProcessingModelChildParameterSource &value )
147 {
148  // we make a copy of all attributes and store locally, so that users can flick between
149  // sources without losing their current value
150  mStaticValue = value.staticValue();
151  mModelInputParameterName = value.parameterName();
152  mOutputChildId = value.outputChildId();
153  mOutputName = value.outputName();
154  mExpression = value.expression();
155 
156  updateUi();
157  setSourceType( value.source() );
158 }
159 
160 QgsProcessingModelChildParameterSource QgsProcessingModelerParameterWidget::value() const
161 {
162  switch ( currentSourceType() )
163  {
164  case StaticValue:
165  return QgsProcessingModelChildParameterSource::fromStaticValue( mStaticWidgetWrapper->parameterValue() );
166 
167  case Expression:
168  return QgsProcessingModelChildParameterSource::fromExpression( mExpressionWidget->expression() );
169 
170  case ModelParameter:
171  return QgsProcessingModelChildParameterSource::fromModelParameter( mModelInputCombo->currentData().toString() );
172 
173  case ChildOutput:
174  {
175  const QStringList parts = mChildOutputCombo->currentData().toStringList();
176  return QgsProcessingModelChildParameterSource::fromChildOutput( parts.value( 0, QString() ), parts.value( 1, QString() ) );
177  }
178  }
179 
180  return QgsProcessingModelChildParameterSource();
181 }
182 
184 {
185  if ( mStaticWidgetWrapper )
186  mStaticWidgetWrapper->setDialog( dialog );
187 }
188 
190 {
192  if ( mModel )
193  {
194  const QgsProcessingAlgorithm *alg = nullptr;
195  if ( mModel->childAlgorithms().contains( mChildId ) )
196  alg = mModel->childAlgorithm( mChildId ).algorithm();
197  QgsExpressionContextScope *algorithmScope = QgsExpressionContextUtils::processingAlgorithmScope( alg, QVariantMap(), mContext );
198  c << algorithmScope;
199  QgsExpressionContextScope *modelScope = QgsExpressionContextUtils::processingModelAlgorithmScope( mModel, QVariantMap(), mContext );
200  c << modelScope;
201  QgsExpressionContextScope *childScope = mModel->createExpressionContextScopeForChildAlgorithm( mChildId, mContext, QVariantMap(), QVariantMap() );
202  c << childScope;
203 
204  QStringList highlightedVariables = childScope->variableNames();
205  QStringList highlightedFunctions = childScope->functionNames();
206  highlightedVariables += algorithmScope->variableNames();
207  highlightedVariables += mModel->variables().keys();
208  highlightedFunctions += algorithmScope->functionNames();
209  c.setHighlightedVariables( highlightedVariables );
210  c.setHighlightedFunctions( highlightedFunctions );
211  }
212 
213  return c;
214 }
215 
216 void QgsProcessingModelerParameterWidget::sourceMenuAboutToShow()
217 {
218  mSourceMenu->clear();
219 
220  const SourceType currentSource = currentSourceType();
221 
222  if ( mHasStaticWrapper )
223  {
224  QAction *fixedValueAction = mSourceMenu->addAction( tr( "Value" ) );
225  fixedValueAction->setCheckable( currentSource == StaticValue );
226  fixedValueAction->setChecked( currentSource == StaticValue );
227  fixedValueAction->setData( QgsProcessingModelChildParameterSource::StaticValue );
228  }
229 
230  QAction *calculatedValueAction = mSourceMenu->addAction( tr( "Pre-calculated Value" ) );
231  calculatedValueAction->setCheckable( currentSource == Expression );
232  calculatedValueAction->setChecked( currentSource == Expression );
233  calculatedValueAction->setData( QgsProcessingModelChildParameterSource::Expression );
234 
235  QAction *inputValueAction = mSourceMenu->addAction( tr( "Model Input" ) );
236  inputValueAction->setCheckable( currentSource == ModelParameter );
237  inputValueAction->setChecked( currentSource == ModelParameter );
238  inputValueAction->setData( QgsProcessingModelChildParameterSource::ModelParameter );
239 
240  QAction *childOutputValueAction = mSourceMenu->addAction( tr( "Algorithm Output" ) );
241  childOutputValueAction->setCheckable( currentSource == ChildOutput );
242  childOutputValueAction->setChecked( currentSource == ChildOutput );
243  childOutputValueAction->setData( QgsProcessingModelChildParameterSource::ChildOutput );
244 
245  // TODO - expression text item?
246 }
247 
248 void QgsProcessingModelerParameterWidget::sourceMenuActionTriggered( QAction *action )
249 {
250  QgsProcessingModelChildParameterSource::Source sourceType = static_cast< QgsProcessingModelChildParameterSource::Source >( action->data().toInt() );
251  setSourceType( sourceType );
252 }
253 
254 QgsProcessingModelerParameterWidget::SourceType QgsProcessingModelerParameterWidget::currentSourceType() const
255 {
256  return static_cast< SourceType >( mStackedWidget->currentIndex() );
257 }
258 
259 void QgsProcessingModelerParameterWidget::setSourceType( QgsProcessingModelChildParameterSource::Source type )
260 {
261  switch ( type )
262  {
263  case QgsProcessingModelChildParameterSource::StaticValue:
264  mStackedWidget->setCurrentIndex( static_cast< int >( StaticValue ) );
265  mSourceButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconFieldInteger.svg" ) ) );
266  mSourceButton->setToolTip( tr( "Value" ) );
267  break;
268 
269  case QgsProcessingModelChildParameterSource::Expression:
270  mSourceButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconExpression.svg" ) ) );
271  mStackedWidget->setCurrentIndex( static_cast< int >( Expression ) );
272  mSourceButton->setToolTip( tr( "Pre-calculated Value" ) );
273  break;
274 
275  case QgsProcessingModelChildParameterSource::ModelParameter:
276  {
277  mSourceButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "processingModel.svg" ) ) );
278  mStackedWidget->setCurrentIndex( static_cast< int >( ModelParameter ) );
279  mSourceButton->setToolTip( tr( "Model Input" ) );
280  break;
281  }
282 
283  case QgsProcessingModelChildParameterSource::ChildOutput:
284  {
285  mSourceButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "processingAlgorithm.svg" ) ) );
286  mStackedWidget->setCurrentIndex( static_cast< int >( ChildOutput ) );
287  mSourceButton->setToolTip( tr( "Algorithm Output" ) );
288  break;
289  }
290 
291  case QgsProcessingModelChildParameterSource::ExpressionText:
292  break;
293  }
294 }
295 
296 void QgsProcessingModelerParameterWidget::updateUi()
297 {
298  mStaticWidgetWrapper->setParameterValue( mStaticValue, mContext );
299 
300  mExpressionWidget->setExpression( mExpression );
301 
302  int currentIndex = mModelInputCombo->findData( mModelInputParameterName );
303  if ( currentIndex == -1 && mModelInputCombo->count() > 0 )
304  currentIndex = 0;
305  mModelInputCombo->setCurrentIndex( currentIndex );
306 
307  const QStringList parts = QStringList() << mOutputChildId << mOutputName;
308  currentIndex = mChildOutputCombo->findData( parts );
309  if ( currentIndex == -1 && mChildOutputCombo->count() > 0 )
310  currentIndex = 0;
311  mChildOutputCombo->setCurrentIndex( currentIndex );
312 }
313 
314 void QgsProcessingModelerParameterWidget::populateSources( const QStringList &compatibleParameterTypes, const QStringList &compatibleOutputTypes, const QList<int> &compatibleDataTypes )
315 {
316  const QList< QgsProcessingModelChildParameterSource > sources = mModel->availableSourcesForChild( mChildId,
317  compatibleParameterTypes, compatibleOutputTypes, compatibleDataTypes );
318 
319  for ( const QgsProcessingModelChildParameterSource &source : sources )
320  {
321  switch ( source.source() )
322  {
323  case QgsProcessingModelChildParameterSource::ModelParameter:
324  mModelInputCombo->addItem( mModel->parameterDefinition( source.parameterName() )->description(), source.parameterName() );
325  break;
326 
327  case QgsProcessingModelChildParameterSource::ChildOutput:
328  {
329  if ( !mModel->childAlgorithms().contains( source.outputChildId() ) )
330  continue;
331 
332  const QgsProcessingModelChildAlgorithm &alg = mModel->childAlgorithm( source.outputChildId() );
333  if ( !alg.algorithm() )
334  continue;
335  const QString outputDescription = alg.algorithm()->outputDefinition( source.outputName() )->description();
336  const QString childDescription = alg.description();
337 
338  mChildOutputCombo->addItem( tr( "“%1” from algorithm “%2”" ).arg( outputDescription, childDescription ), QStringList() << source.outputChildId() << source.outputName() );
339  break;
340  }
341 
342  case QgsProcessingModelChildParameterSource::StaticValue:
343  case QgsProcessingModelChildParameterSource::Expression:
344  case QgsProcessingModelChildParameterSource::ExpressionText:
345  break;
346  }
347 
348  }
349 }
350 
352 {
353  mExpressionWidget->setExpectedOutputFormat( text );
354 }
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...
static QgsProcessingGuiRegistry * processingGuiRegistry()
Returns the global processing gui registry, used for registering the GUI behavior of processing algor...
Definition: qgsgui.cpp:89
virtual void setWidgetValue(const QgsProcessingModelChildParameterSource &value)
Sets the current value for the parameter.
virtual QgsProcessingModelChildParameterSource value() const
Returns the current value of the parameter.
void setWidgetContext(const QgsProcessingParameterWidgetContext &context)
Sets the context in which the modeler parameter widget is shown, e.g., the parent model algorithm and...
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly...
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QStringList variableNames() const
Returns a list of variable names contained within the scope.
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Abstract base class for processing algorithms.
QLabel * createLabel()
Creates a label for use identifying the associated parameter.
void setHighlightedFunctions(const QStringList &names)
Sets the list of function names intended to be highlighted to the user.
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
void setExpression(const QString &expression)
Sets the current expression to show in the widget.
QString expression() const
Returns the current expression shown in the widget.
An interface for objects which can create Processing contexts.
void populateSources(const QStringList &compatibleParameterTypes, const QStringList &compatibleOutputTypes, const QList< int > &compatibleDataTypes)
Populates the widget with available sources for the parameter&#39;s value, e.g.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setDialog(QDialog *dialog)
Sets the parent dialog in which the widget is shown.
Single scope for storing variables and functions for use within a QgsExpressionContext.
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window&#39;s toolbar icons.
QgsExpressionContext & expressionContext()
Returns the expression context.
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...
const QgsProcessingParameterDefinition * parameterDefinition() const
Returns the parameter definition associated with this wrapper.
Contains settings which reflect the context in which a Processing parameter widget is shown...
The QgsExpressionLineEdit widget includes a line edit for entering expressions together with a button...
void registerExpressionContextGenerator(const QgsExpressionContextGenerator *generator)
Register an expression context generator class that will be used to retrieve an expression context fo...
QgsProcessingModelerParameterWidget(QgsProcessingModelAlgorithm *model, const QString &childId, const QgsProcessingParameterDefinition *parameter, QgsProcessingContext &context, QWidget *parent=nullptr)
Constructor for QgsProcessingModelerParameterWidget, for the specified parameter definition within th...
Base class for the definition of processing parameters.
void setExpectedOutputFormat(const QString &expected)
Set the expected format string, which is shown in the expression builder dialog for the widget...
void setHighlightedVariables(const QStringList &variableNames)
Sets the list of variable names within the context intended to be highlighted to the user...
void setExpressionHelpText(const QString &text)
Set the expected expression format text, which is shown in the expression builder dialog for the widg...
Contains information about the context in which a processing algorithm is executed.
void registerProcessingContextGenerator(QgsProcessingContextGenerator *generator)
Registers a Processing context generator class that will be used to retrieve a Processing context for...