QGIS API Documentation  3.14.0-Pi (9f7028fd23)
qgsprocessingaggregatewidgetwrapper.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsprocessingaggregatewidgetwrapper.cpp
3  ---------------------
4  Date : June 2020
5  Copyright : (C) 2020 by Nyall Dawson
6  Email : nyall dot dawson at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
17 
18 #include <QBoxLayout>
19 #include <QLineEdit>
20 #include <QMessageBox>
21 #include <QPushButton>
22 #include <QStandardItemModel>
23 #include <QToolButton>
24 
25 #include "qgspanelwidget.h"
26 
27 #include "qgsprocessingcontext.h"
28 #include "qgsprocessingmodelalgorithm.h"
29 
33 
35 
36 
37 //
38 // QgsProcessingAggregatePanelWidget
39 //
40 
41 
42 QgsProcessingAggregatePanelWidget::QgsProcessingAggregatePanelWidget( QWidget *parent )
43  : QgsPanelWidget( parent )
44 {
45  setupUi( this );
46 
47  mModel = mFieldsView->model();
48 
49  mLayerCombo->setAllowEmptyLayer( true );
50  mLayerCombo->setFilters( QgsMapLayerProxyModel::VectorLayer );
51 
52  connect( mResetButton, &QPushButton::clicked, this, &QgsProcessingAggregatePanelWidget::loadFieldsFromLayer );
53  connect( mAddButton, &QPushButton::clicked, this, &QgsProcessingAggregatePanelWidget::addField );
54  connect( mDeleteButton, &QPushButton::clicked, mFieldsView, &QgsAggregateMappingWidget::removeSelectedFields );
55  connect( mUpButton, &QPushButton::clicked, mFieldsView, &QgsAggregateMappingWidget::moveSelectedFieldsUp );
56  connect( mDownButton, &QPushButton::clicked, mFieldsView, &QgsAggregateMappingWidget::moveSelectedFieldsDown );
57  connect( mLoadLayerFieldsButton, &QPushButton::clicked, this, &QgsProcessingAggregatePanelWidget::loadLayerFields );
58 
59  connect( mFieldsView, &QgsAggregateMappingWidget::changed, this, [ = ]
60  {
61  if ( !mBlockChangedSignal )
62  {
63  emit changed();
64  }
65  } );
66 }
67 
68 void QgsProcessingAggregatePanelWidget::setLayer( QgsVectorLayer *layer )
69 {
70  if ( layer == mLayer )
71  return;
72 
73  mLayer = layer;
74  if ( mModel->rowCount() == 0 )
75  {
76  loadFieldsFromLayer();
77  return;
78  }
79 
80  QMessageBox dlg( this );
81  dlg.setText( tr( "Do you want to reset the field mapping?" ) );
82  dlg.setStandardButtons(
83  QMessageBox::StandardButtons( QMessageBox::Yes |
84  QMessageBox::No ) );
85  dlg.setDefaultButton( QMessageBox::No );
86  if ( dlg.exec() == QMessageBox::Yes )
87  {
88  loadFieldsFromLayer();
89  }
90 }
91 
92 QgsVectorLayer *QgsProcessingAggregatePanelWidget::layer()
93 {
94  return mLayer;
95 }
96 
97 QVariant QgsProcessingAggregatePanelWidget::value() const
98 {
99  const QList<QgsAggregateMappingModel::Aggregate> mapping = mFieldsView->mapping();
100 
101  QVariantList results;
102  results.reserve( mapping.size() );
103  for ( const QgsAggregateMappingModel::Aggregate &aggregate : mapping )
104  {
105  QVariantMap def;
106  def.insert( QStringLiteral( "name" ), aggregate.field.name() );
107  def.insert( QStringLiteral( "type" ), static_cast< int >( aggregate.field.type() ) );
108  def.insert( QStringLiteral( "length" ), aggregate.field.length() );
109  def.insert( QStringLiteral( "precision" ), aggregate.field.precision() );
110  def.insert( QStringLiteral( "input" ), aggregate.source );
111  def.insert( QStringLiteral( "aggregate" ), aggregate.aggregate );
112  def.insert( QStringLiteral( "delimiter" ), aggregate.delimiter );
113  results.append( def );
114  }
115  return results;
116 }
117 
118 void QgsProcessingAggregatePanelWidget::setValue( const QVariant &value )
119 {
120  if ( value.type() != QVariant::List )
121  return;
122 
123  QList< QgsAggregateMappingModel::Aggregate > aggregates;
124 
125  const QVariantList fields = value.toList();
126  aggregates.reserve( fields.size() );
127  for ( const QVariant &field : fields )
128  {
129  const QVariantMap map = field.toMap();
130  QgsField f( map.value( QStringLiteral( "name" ) ).toString(),
131  static_cast< QVariant::Type >( map.value( QStringLiteral( "type" ), QVariant::Invalid ).toInt() ),
132  QVariant::typeToName( static_cast< QVariant::Type >( map.value( QStringLiteral( "type" ), QVariant::Invalid ).toInt() ) ),
133  map.value( QStringLiteral( "length" ), 0 ).toInt(),
134  map.value( QStringLiteral( "precision" ), 0 ).toInt() );
135 
137  aggregate.field = f;
138 
139  aggregate.source = map.value( QStringLiteral( "input" ) ).toString();
140  aggregate.aggregate = map.value( QStringLiteral( "aggregate" ) ).toString();
141  aggregate.delimiter = map.value( QStringLiteral( "delimiter" ) ).toString();
142 
143  aggregates.append( aggregate );
144  }
145 
146  mBlockChangedSignal = true;
147 
148  if ( aggregates.size() > 0 )
149  mFieldsView->setMapping( aggregates );
150 
151  mBlockChangedSignal = false;
152 
153  emit changed();
154 }
155 
156 void QgsProcessingAggregatePanelWidget::registerExpressionContextGenerator( const QgsExpressionContextGenerator *generator )
157 {
158  mFieldsView->registerExpressionContextGenerator( generator );
159 }
160 
161 void QgsProcessingAggregatePanelWidget::loadFieldsFromLayer()
162 {
163  if ( mLayer )
164  {
165  mFieldsView->setSourceFields( mLayer->fields() );
166  }
167 }
168 
169 void QgsProcessingAggregatePanelWidget::addField()
170 {
171  const int rowCount = mModel->rowCount();
172  mModel->appendField( QgsField( QStringLiteral( "new_field" ) ) );
173  QModelIndex index = mModel->index( rowCount, 0 );
174  mFieldsView->selectionModel()->select(
175  index,
176  QItemSelectionModel::SelectionFlags(
177  QItemSelectionModel::Clear |
178  QItemSelectionModel::Select |
179  QItemSelectionModel::Current |
180  QItemSelectionModel::Rows ) );
181  mFieldsView->scrollTo( index );
182 }
183 
184 void QgsProcessingAggregatePanelWidget::loadLayerFields()
185 {
186  if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( mLayerCombo->currentLayer() ) )
187  {
188  mFieldsView->setSourceFields( vl->fields() );
189  }
190 }
191 
192 //
193 // QgsProcessingAggregateParameterDefinitionWidget
194 //
195 
196 QgsProcessingAggregateParameterDefinitionWidget::QgsProcessingAggregateParameterDefinitionWidget( QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent )
197  : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
198 {
199  QVBoxLayout *vlayout = new QVBoxLayout();
200  vlayout->setMargin( 0 );
201  vlayout->setContentsMargins( 0, 0, 0, 0 );
202 
203  vlayout->addWidget( new QLabel( tr( "Parent layer" ) ) );
204 
205  mParentLayerComboBox = new QComboBox();
206  mParentLayerComboBox->addItem( tr( "None" ), QVariant() );
207 
208  QString initialParent;
209  if ( const QgsProcessingParameterAggregate *aggregateParam = dynamic_cast<const QgsProcessingParameterAggregate *>( definition ) )
210  initialParent = aggregateParam->parentLayerParameterName();
211 
212  if ( widgetContext.model() )
213  {
214  // populate combo box with other model input choices
215  const QMap<QString, QgsProcessingModelParameter> components = widgetContext.model()->parameterComponents();
216  for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
217  {
218  if ( const QgsProcessingParameterFeatureSource *definition = dynamic_cast< const QgsProcessingParameterFeatureSource * >( widgetContext.model()->parameterDefinition( it.value().parameterName() ) ) )
219  {
220  mParentLayerComboBox-> addItem( definition->description(), definition->name() );
221  if ( !initialParent.isEmpty() && initialParent == definition->name() )
222  {
223  mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
224  }
225  }
226  else if ( const QgsProcessingParameterVectorLayer *definition = dynamic_cast< const QgsProcessingParameterVectorLayer * >( widgetContext.model()->parameterDefinition( it.value().parameterName() ) ) )
227  {
228  mParentLayerComboBox-> addItem( definition->description(), definition->name() );
229  if ( !initialParent.isEmpty() && initialParent == definition->name() )
230  {
231  mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
232  }
233  }
234  }
235  }
236 
237  if ( mParentLayerComboBox->count() == 1 && !initialParent.isEmpty() )
238  {
239  // if no parent candidates found, we just add the existing one as a placeholder
240  mParentLayerComboBox->addItem( initialParent, initialParent );
241  mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
242  }
243 
244  vlayout->addWidget( mParentLayerComboBox );
245  setLayout( vlayout );
246 }
247 
248 QgsProcessingParameterDefinition *QgsProcessingAggregateParameterDefinitionWidget::createParameter( const QString &name, const QString &description, QgsProcessingParameterDefinition::Flags flags ) const
249 {
250  auto param = qgis::make_unique< QgsProcessingParameterAggregate >( name, description, mParentLayerComboBox->currentData().toString() );
251  param->setFlags( flags );
252  return param.release();
253 }
254 
255 //
256 // QgsProcessingAggregateWidgetWrapper
257 //
258 
259 QgsProcessingAggregateWidgetWrapper::QgsProcessingAggregateWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type, QWidget *parent )
260  : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
261 {
262 }
263 
264 QString QgsProcessingAggregateWidgetWrapper::parameterType() const
265 {
267 }
268 
269 QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingAggregateWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type )
270 {
271  return new QgsProcessingAggregateWidgetWrapper( parameter, type );
272 }
273 
274 QWidget *QgsProcessingAggregateWidgetWrapper::createWidget()
275 {
276  mPanel = new QgsProcessingAggregatePanelWidget( nullptr );
277  mPanel->setToolTip( parameterDefinition()->toolTip() );
278  mPanel->registerExpressionContextGenerator( this );
279 
280  connect( mPanel, &QgsProcessingAggregatePanelWidget::changed, this, [ = ]
281  {
282  emit widgetValueHasChanged( this );
283  } );
284 
285  return mPanel;
286 }
287 
288 QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingAggregateWidgetWrapper::createParameterDefinitionWidget( QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm )
289 {
290  return new QgsProcessingAggregateParameterDefinitionWidget( context, widgetContext, definition, algorithm );
291 }
292 
293 void QgsProcessingAggregateWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
294 {
296  switch ( type() )
297  {
300  {
301  for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
302  {
303  if ( wrapper->parameterDefinition()->name() == static_cast< const QgsProcessingParameterAggregate * >( parameterDefinition() )->parentLayerParameterName() )
304  {
305  setParentLayerWrapperValue( wrapper );
307  {
308  setParentLayerWrapperValue( wrapper );
309  } );
310  break;
311  }
312  }
313  break;
314  }
315 
317  break;
318  }
319 }
320 
321 int QgsProcessingAggregateWidgetWrapper::stretch() const
322 {
323  return 1;
324 }
325 
326 void QgsProcessingAggregateWidgetWrapper::setParentLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
327 {
328  // evaluate value to layer
329  QgsProcessingContext *context = nullptr;
330  std::unique_ptr< QgsProcessingContext > tmpContext;
331  if ( mProcessingContextGenerator )
332  context = mProcessingContextGenerator->processingContext();
333 
334  if ( !context )
335  {
336  tmpContext = qgis::make_unique< QgsProcessingContext >();
337  context = tmpContext.get();
338  }
339 
340  QgsVectorLayer *layer = QgsProcessingParameters::parameterAsVectorLayer( parentWrapper->parameterDefinition(), parentWrapper->parameterValue(), *context );
341  if ( !layer )
342  {
343  if ( mPanel )
344  mPanel->setLayer( nullptr );
345  return;
346  }
347 
348  // need to grab ownership of layer if required - otherwise layer may be deleted when context
349  // goes out of scope
350  std::unique_ptr< QgsMapLayer > ownedLayer( context->takeResultLayer( layer->id() ) );
351  if ( ownedLayer && ownedLayer->type() == QgsMapLayerType::VectorLayer )
352  {
353  mParentLayer.reset( qobject_cast< QgsVectorLayer * >( ownedLayer.release() ) );
354  layer = mParentLayer.get();
355  }
356  else
357  {
358  // don't need ownership of this layer - it wasn't owned by context (so e.g. is owned by the project)
359  }
360 
361  if ( mPanel )
362  mPanel->setLayer( layer );
363 }
364 
365 void QgsProcessingAggregateWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext & )
366 {
367  if ( mPanel )
368  mPanel->setValue( value );
369 }
370 
371 QVariant QgsProcessingAggregateWidgetWrapper::widgetValue() const
372 {
373  return mPanel ? mPanel->value() : QVariant();
374 }
375 
376 QStringList QgsProcessingAggregateWidgetWrapper::compatibleParameterTypes() const
377 {
378  return QStringList()
380 }
381 
382 QStringList QgsProcessingAggregateWidgetWrapper::compatibleOutputTypes() const
383 {
384  return QStringList();
385 }
386 
387 QString QgsProcessingAggregateWidgetWrapper::modelerExpressionFormatString() const
388 {
389  return tr( "an array of map items, each containing a 'name', 'type', 'aggregate' and 'input' value (and optional 'length' and 'precision' values)." );
390 }
391 
392 const QgsVectorLayer *QgsProcessingAggregateWidgetWrapper::linkedVectorLayer() const
393 {
394  if ( mPanel && mPanel->layer() )
395  return mPanel->layer();
396 
398 }
399 
401 
402 
QgsProcessingParameterWidgetContext
Definition: qgsprocessingwidgetwrapper.h:99
qgsexpressioncontextutils.h
QgsAggregateMappingWidget::moveSelectedFieldsDown
bool moveSelectedFieldsDown()
Moves down currently selected field.
Definition: qgsprocessingaggregatewidgets.cpp:445
QgsMapLayerType::VectorLayer
@ VectorLayer
QgsProcessingParameterDefinition::description
QString description() const
Returns the description for the parameter.
Definition: qgsprocessingparameters.h:470
algorithm
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 allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
QgsProcessingContext::takeResultLayer
QgsMapLayer * takeResultLayer(const QString &id)
Takes the result map layer with matching id from the context and transfers ownership of it back to th...
Definition: qgsprocessingcontext.cpp:108
QgsAggregateMappingWidget::moveSelectedFieldsUp
bool moveSelectedFieldsUp()
Moves up currently selected field.
Definition: qgsprocessingaggregatewidgets.cpp:429
QgsAggregateMappingWidget::removeSelectedFields
bool removeSelectedFields()
Removes the currently selected field from the model.
Definition: qgsprocessingaggregatewidgets.cpp:412
QgsProcessingParameterDefinition
Definition: qgsprocessingparameters.h:330
QgsProcessingParameterFeatureSource
Definition: qgsprocessingparameters.h:2612
QgsAbstractProcessingParameterWidgetWrapper
Definition: qgsprocessingwidgetwrapper.h:266
QgsProcessingGui::Standard
@ Standard
Standard algorithm dialog.
Definition: qgsprocessinggui.h:40
QgsMapLayerProxyModel::VectorLayer
@ VectorLayer
Definition: qgsmaplayerproxymodel.h:50
QgsProcessingParameterAggregate
Definition: qgsprocessingparameteraggregate.h:31
qgsprocessingparameteraggregate.h
QgsPanelWidget
Base class for any widget that can be shown as a inline panel.
Definition: qgspanelwidget.h:29
QgsProcessingParameters::parameterAsVectorLayer
static QgsVectorLayer * parameterAsVectorLayer(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Evaluates the parameter with matching definition to a vector layer.
Definition: qgsprocessingparameters.cpp:945
QgsProcessingContext
Definition: qgsprocessingcontext.h:43
QgsProcessingParameterDefinition::name
QString name() const
Returns the name of the parameter.
Definition: qgsprocessingparameters.h:456
QgsProcessingGui::Batch
@ Batch
Batch processing dialog.
Definition: qgsprocessinggui.h:41
QgsProcessingParameterAggregate::typeName
static QString typeName()
Returns the type name for the parameter class.
Definition: qgsprocessingparameteraggregate.h:60
QgsMapLayer::id
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
Definition: qgsmaplayer.cpp:148
QgsProcessingGui::Modeler
@ Modeler
Modeler dialog.
Definition: qgsprocessinggui.h:42
QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged
void widgetValueHasChanged(QgsAbstractProcessingParameterWidgetWrapper *wrapper)
Emitted whenever the parameter value (as defined by the wrapped widget) is changed.
QgsAbstractProcessingParameterWidgetWrapper::linkedVectorLayer
virtual const QgsVectorLayer * linkedVectorLayer() const
Returns the optional vector layer associated with this widget wrapper, or nullptr if no vector layer ...
Definition: qgsprocessingwidgetwrapper.cpp:246
QgsProcessingParameterVectorLayer
Definition: qgsprocessingparameters.h:2382
QgsAggregateMappingModel::Aggregate::aggregate
QString aggregate
Aggregate name.
Definition: qgsprocessingaggregatewidgets.h:72
QgsProcessingAbstractParameterDefinitionWidget
Abstract base class for widgets which allow users to specify the properties of a Processing parameter...
Definition: qgsprocessingparameterdefinitionwidget.h:43
QgsAggregateMappingWidget::changed
void changed()
Emitted when the aggregates defined in the widget are changed.
QgsProcessingGui::WidgetType
WidgetType
Types of dialogs which Processing widgets can be created for.
Definition: qgsprocessinggui.h:38
QgsProcessingAlgorithm
Definition: qgsprocessingalgorithm.h:51
QgsVectorLayer
Definition: qgsvectorlayer.h:385
QgsAbstractProcessingParameterWidgetWrapper::parameterValue
QVariant parameterValue() const
Returns the current value of the parameter.
Definition: qgsprocessingwidgetwrapper.cpp:202
qgsprocessingcontext.h
QgsProcessingParameterWidgetContext::model
QgsProcessingModelAlgorithm * model() const
Returns the model which the parameter widget is associated with.
Definition: qgsprocessingwidgetwrapper.cpp:94
qgsfieldexpressionwidget.h
QgsAggregateMappingModel::Aggregate::field
QgsField field
The field in its current status (it might have been renamed)
Definition: qgsprocessingaggregatewidgets.h:78
qgsprocessingaggregatewidgetwrapper.h
qgspanelwidget.h
QgsAbstractProcessingParameterWidgetWrapper::parameterDefinition
const QgsProcessingParameterDefinition * parameterDefinition() const
Returns the parameter definition associated with this wrapper.
Definition: qgsprocessingwidgetwrapper.cpp:182
QgsExpressionContextGenerator
Definition: qgsexpressioncontextgenerator.h:36
QgsAggregateMappingModel::Aggregate::delimiter
QString delimiter
Delimiter string.
Definition: qgsprocessingaggregatewidgets.h:75
QgsAggregateMappingModel::Aggregate::source
QString source
The source expression used as the input for the aggregate calculation.
Definition: qgsprocessingaggregatewidgets.h:69
QgsAbstractProcessingParameterWidgetWrapper::postInitialize
virtual void postInitialize(const QList< QgsAbstractProcessingParameterWidgetWrapper * > &wrappers)
Called after all wrappers have been created within a particular dialog or context,...
Definition: qgsprocessingwidgetwrapper.cpp:253
QgsAggregateMappingModel::Aggregate
The Aggregate struct holds information about an aggregate column.
Definition: qgsprocessingaggregatewidgets.h:66
QgsField
Definition: qgsfield.h:49