QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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 #include <QItemSelectionModel>
25 
26 #include "qgspanelwidget.h"
27 
28 #include "qgsprocessingcontext.h"
30 
34 
36 
37 
38 //
39 // QgsProcessingAggregatePanelWidget
40 //
41 
42 
43 QgsProcessingAggregatePanelWidget::QgsProcessingAggregatePanelWidget( QWidget *parent )
44  : QgsPanelWidget( parent )
45 {
46  setupUi( this );
47 
48  mModel = mFieldsView->model();
49 
50  mLayerCombo->setAllowEmptyLayer( true );
51  mLayerCombo->setFilters( QgsMapLayerProxyModel::VectorLayer );
52 
53  connect( mResetButton, &QPushButton::clicked, this, &QgsProcessingAggregatePanelWidget::loadFieldsFromLayer );
54  connect( mAddButton, &QPushButton::clicked, this, &QgsProcessingAggregatePanelWidget::addField );
55  connect( mDeleteButton, &QPushButton::clicked, mFieldsView, &QgsAggregateMappingWidget::removeSelectedFields );
56  connect( mUpButton, &QPushButton::clicked, mFieldsView, &QgsAggregateMappingWidget::moveSelectedFieldsUp );
57  connect( mDownButton, &QPushButton::clicked, mFieldsView, &QgsAggregateMappingWidget::moveSelectedFieldsDown );
58  connect( mLoadLayerFieldsButton, &QPushButton::clicked, this, &QgsProcessingAggregatePanelWidget::loadLayerFields );
59 
60  connect( mFieldsView, &QgsAggregateMappingWidget::changed, this, [ = ]
61  {
62  if ( !mBlockChangedSignal )
63  {
64  emit changed();
65  }
66  } );
67 }
68 
69 void QgsProcessingAggregatePanelWidget::setLayer( QgsVectorLayer *layer )
70 {
71  if ( layer == mLayer )
72  return;
73 
74  mLayer = layer;
75  mFieldsView->setSourceLayer( mLayer );
76  if ( mModel->rowCount() == 0 )
77  {
78  loadFieldsFromLayer();
79  return;
80  }
81 
82  QMessageBox dlg( this );
83  dlg.setText( tr( "Do you want to reset the field mapping?" ) );
84  dlg.setStandardButtons(
85  QMessageBox::StandardButtons( QMessageBox::Yes |
86  QMessageBox::No ) );
87  dlg.setDefaultButton( QMessageBox::No );
88  if ( dlg.exec() == QMessageBox::Yes )
89  {
90  loadFieldsFromLayer();
91  }
92 }
93 
94 QgsVectorLayer *QgsProcessingAggregatePanelWidget::layer()
95 {
96  return mLayer;
97 }
98 
99 QVariant QgsProcessingAggregatePanelWidget::value() const
100 {
101  const QList<QgsAggregateMappingModel::Aggregate> mapping = mFieldsView->mapping();
102 
103  QVariantList results;
104  results.reserve( mapping.size() );
105  for ( const QgsAggregateMappingModel::Aggregate &aggregate : mapping )
106  {
107  QVariantMap def;
108  def.insert( QStringLiteral( "name" ), aggregate.field.name() );
109  def.insert( QStringLiteral( "type" ), static_cast< int >( aggregate.field.type() ) );
110  def.insert( QStringLiteral( "type_name" ), aggregate.field.typeName() );
111  def.insert( QStringLiteral( "length" ), aggregate.field.length() );
112  def.insert( QStringLiteral( "precision" ), aggregate.field.precision() );
113  def.insert( QStringLiteral( "sub_type" ), static_cast< int >( aggregate.field.subType() ) );
114  def.insert( QStringLiteral( "input" ), aggregate.source );
115  def.insert( QStringLiteral( "aggregate" ), aggregate.aggregate );
116  def.insert( QStringLiteral( "delimiter" ), aggregate.delimiter );
117  results.append( def );
118  }
119  return results;
120 }
121 
122 void QgsProcessingAggregatePanelWidget::setValue( const QVariant &value )
123 {
124  if ( value.type() != QVariant::List )
125  return;
126 
127  QList< QgsAggregateMappingModel::Aggregate > aggregates;
128 
129  const QVariantList fields = value.toList();
130  aggregates.reserve( fields.size() );
131  for ( const QVariant &field : fields )
132  {
133  const QVariantMap map = field.toMap();
134  const QgsField f( map.value( QStringLiteral( "name" ) ).toString(),
135  static_cast< QVariant::Type >( map.value( QStringLiteral( "type" ), QVariant::Invalid ).toInt() ),
136  map.value( QStringLiteral( "type_name" ), QVariant::typeToName( static_cast< QVariant::Type >( map.value( QStringLiteral( "type" ), QVariant::Invalid ).toInt() ) ) ).toString(),
137  map.value( QStringLiteral( "length" ), 0 ).toInt(),
138  map.value( QStringLiteral( "precision" ), 0 ).toInt(),
139  QString(),
140  static_cast< QVariant::Type >( map.value( QStringLiteral( "sub_type" ), QVariant::Invalid ).toInt() ) );
141 
143  aggregate.field = f;
144 
145  aggregate.source = map.value( QStringLiteral( "input" ) ).toString();
146  aggregate.aggregate = map.value( QStringLiteral( "aggregate" ) ).toString();
147  aggregate.delimiter = map.value( QStringLiteral( "delimiter" ) ).toString();
148 
149  aggregates.append( aggregate );
150  }
151 
152  mBlockChangedSignal = true;
153 
154  if ( aggregates.size() > 0 )
155  mFieldsView->setMapping( aggregates );
156 
157  mBlockChangedSignal = false;
158 
159  emit changed();
160 }
161 
162 void QgsProcessingAggregatePanelWidget::registerExpressionContextGenerator( const QgsExpressionContextGenerator *generator )
163 {
164  mFieldsView->registerExpressionContextGenerator( generator );
165 }
166 
167 void QgsProcessingAggregatePanelWidget::loadFieldsFromLayer()
168 {
169  if ( mLayer )
170  {
171  mFieldsView->setSourceFields( mLayer->fields() );
172  }
173 }
174 
175 void QgsProcessingAggregatePanelWidget::addField()
176 {
177  const int rowCount = mModel->rowCount();
178  mModel->appendField( QgsField( QStringLiteral( "new_field" ) ) );
179  const QModelIndex index = mModel->index( rowCount, 0 );
180  mFieldsView->selectionModel()->select(
181  index,
182  QItemSelectionModel::SelectionFlags(
183  QItemSelectionModel::Clear |
184  QItemSelectionModel::Select |
185  QItemSelectionModel::Current |
186  QItemSelectionModel::Rows ) );
187  mFieldsView->scrollTo( index );
188 }
189 
190 void QgsProcessingAggregatePanelWidget::loadLayerFields()
191 {
192  if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( mLayerCombo->currentLayer() ) )
193  {
194  mFieldsView->setSourceFields( vl->fields() );
195  }
196 }
197 
198 //
199 // QgsProcessingAggregateParameterDefinitionWidget
200 //
201 
202 QgsProcessingAggregateParameterDefinitionWidget::QgsProcessingAggregateParameterDefinitionWidget( QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent )
203  : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
204 {
205  QVBoxLayout *vlayout = new QVBoxLayout();
206  vlayout->setContentsMargins( 0, 0, 0, 0 );
207 
208  vlayout->addWidget( new QLabel( tr( "Parent layer" ) ) );
209 
210  mParentLayerComboBox = new QComboBox();
211  mParentLayerComboBox->addItem( tr( "None" ), QVariant() );
212 
213  QString initialParent;
214  if ( const QgsProcessingParameterAggregate *aggregateParam = dynamic_cast<const QgsProcessingParameterAggregate *>( definition ) )
215  initialParent = aggregateParam->parentLayerParameterName();
216 
217  if ( auto *lModel = widgetContext.model() )
218  {
219  // populate combo box with other model input choices
220  const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
221  for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
222  {
223  if ( const QgsProcessingParameterFeatureSource *definition = dynamic_cast< const QgsProcessingParameterFeatureSource * >( lModel->parameterDefinition( it.value().parameterName() ) ) )
224  {
225  mParentLayerComboBox-> addItem( definition->description(), definition->name() );
226  if ( !initialParent.isEmpty() && initialParent == definition->name() )
227  {
228  mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
229  }
230  }
231  else if ( const QgsProcessingParameterVectorLayer *definition = dynamic_cast< const QgsProcessingParameterVectorLayer * >( lModel->parameterDefinition( it.value().parameterName() ) ) )
232  {
233  mParentLayerComboBox-> addItem( definition->description(), definition->name() );
234  if ( !initialParent.isEmpty() && initialParent == definition->name() )
235  {
236  mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
237  }
238  }
239  }
240  }
241 
242  if ( mParentLayerComboBox->count() == 1 && !initialParent.isEmpty() )
243  {
244  // if no parent candidates found, we just add the existing one as a placeholder
245  mParentLayerComboBox->addItem( initialParent, initialParent );
246  mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
247  }
248 
249  vlayout->addWidget( mParentLayerComboBox );
250  setLayout( vlayout );
251 }
252 
253 QgsProcessingParameterDefinition *QgsProcessingAggregateParameterDefinitionWidget::createParameter( const QString &name, const QString &description, QgsProcessingParameterDefinition::Flags flags ) const
254 {
255  auto param = std::make_unique< QgsProcessingParameterAggregate >( name, description, mParentLayerComboBox->currentData().toString() );
256  param->setFlags( flags );
257  return param.release();
258 }
259 
260 //
261 // QgsProcessingAggregateWidgetWrapper
262 //
263 
264 QgsProcessingAggregateWidgetWrapper::QgsProcessingAggregateWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type, QWidget *parent )
265  : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
266 {
267 }
268 
269 QString QgsProcessingAggregateWidgetWrapper::parameterType() const
270 {
272 }
273 
274 QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingAggregateWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type )
275 {
276  return new QgsProcessingAggregateWidgetWrapper( parameter, type );
277 }
278 
279 QWidget *QgsProcessingAggregateWidgetWrapper::createWidget()
280 {
281  mPanel = new QgsProcessingAggregatePanelWidget( nullptr );
282  mPanel->setToolTip( parameterDefinition()->toolTip() );
283  mPanel->registerExpressionContextGenerator( this );
284 
285  connect( mPanel, &QgsProcessingAggregatePanelWidget::changed, this, [ = ]
286  {
287  emit widgetValueHasChanged( this );
288  } );
289 
290  return mPanel;
291 }
292 
293 QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingAggregateWidgetWrapper::createParameterDefinitionWidget( QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm )
294 {
295  return new QgsProcessingAggregateParameterDefinitionWidget( context, widgetContext, definition, algorithm );
296 }
297 
298 void QgsProcessingAggregateWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
299 {
301  switch ( type() )
302  {
305  {
306  for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
307  {
308  if ( wrapper->parameterDefinition()->name() == static_cast< const QgsProcessingParameterAggregate * >( parameterDefinition() )->parentLayerParameterName() )
309  {
310  setParentLayerWrapperValue( wrapper );
312  {
313  setParentLayerWrapperValue( wrapper );
314  } );
315  break;
316  }
317  }
318  break;
319  }
320 
322  break;
323  }
324 }
325 
326 int QgsProcessingAggregateWidgetWrapper::stretch() const
327 {
328  return 1;
329 }
330 
331 void QgsProcessingAggregateWidgetWrapper::setParentLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
332 {
333  // evaluate value to layer
334  QgsProcessingContext *context = nullptr;
335  std::unique_ptr< QgsProcessingContext > tmpContext;
336  if ( mProcessingContextGenerator )
337  context = mProcessingContextGenerator->processingContext();
338 
339  if ( !context )
340  {
341  tmpContext = std::make_unique< QgsProcessingContext >();
342  context = tmpContext.get();
343  }
344 
345  QgsVectorLayer *layer = QgsProcessingParameters::parameterAsVectorLayer( parentWrapper->parameterDefinition(), parentWrapper->parameterValue(), *context );
346  if ( !layer )
347  {
348  if ( mPanel )
349  mPanel->setLayer( nullptr );
350  return;
351  }
352 
353  // need to grab ownership of layer if required - otherwise layer may be deleted when context
354  // goes out of scope
355  std::unique_ptr< QgsMapLayer > ownedLayer( context->takeResultLayer( layer->id() ) );
356  if ( ownedLayer && ownedLayer->type() == QgsMapLayerType::VectorLayer )
357  {
358  mParentLayer.reset( qobject_cast< QgsVectorLayer * >( ownedLayer.release() ) );
359  layer = mParentLayer.get();
360  }
361  else
362  {
363  // don't need ownership of this layer - it wasn't owned by context (so e.g. is owned by the project)
364  }
365 
366  if ( mPanel )
367  mPanel->setLayer( layer );
368 }
369 
370 void QgsProcessingAggregateWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext & )
371 {
372  if ( mPanel )
373  mPanel->setValue( value );
374 }
375 
376 QVariant QgsProcessingAggregateWidgetWrapper::widgetValue() const
377 {
378  return mPanel ? mPanel->value() : QVariant();
379 }
380 
381 QStringList QgsProcessingAggregateWidgetWrapper::compatibleParameterTypes() const
382 {
383  return QStringList()
385 }
386 
387 QStringList QgsProcessingAggregateWidgetWrapper::compatibleOutputTypes() const
388 {
389  return QStringList();
390 }
391 
392 QString QgsProcessingAggregateWidgetWrapper::modelerExpressionFormatString() const
393 {
394  return tr( "an array of map items, each containing a 'name', 'type', 'aggregate' and 'input' value (and optional 'length' and 'precision' values)." );
395 }
396 
397 const QgsVectorLayer *QgsProcessingAggregateWidgetWrapper::linkedVectorLayer() const
398 {
399  if ( mPanel && mPanel->layer() )
400  return mPanel->layer();
401 
403 }
404 
406 
407 
A widget wrapper for Processing parameter value widgets.
QVariant parameterValue() const
Returns the current value of the parameter.
void widgetValueHasChanged(QgsAbstractProcessingParameterWidgetWrapper *wrapper)
Emitted whenever the parameter value (as defined by the wrapped widget) is changed.
virtual void postInitialize(const QList< QgsAbstractProcessingParameterWidgetWrapper * > &wrappers)
Called after all wrappers have been created within a particular dialog or context,...
virtual const QgsVectorLayer * linkedVectorLayer() const
Returns the optional vector layer associated with this widget wrapper, or nullptr if no vector layer ...
const QgsProcessingParameterDefinition * parameterDefinition() const
Returns the parameter definition associated with this wrapper.
void changed()
Emitted when the aggregates defined in the widget are changed.
bool moveSelectedFieldsDown()
Moves down currently selected field.
bool moveSelectedFieldsUp()
Moves up currently selected field.
bool removeSelectedFields()
Removes the currently selected field from the model.
Abstract interface for generating an expression context.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
Base class for any widget that can be shown as a inline panel.
Abstract base class for widgets which allow users to specify the properties of a Processing parameter...
Abstract base class for processing algorithms.
Contains information about the context in which a processing algorithm is executed.
QgsMapLayer * takeResultLayer(const QString &id)
Takes the result map layer with matching id from the context and transfers ownership of it back to th...
WidgetType
Types of dialogs which Processing widgets can be created for.
@ Modeler
Modeler dialog.
@ Standard
Standard algorithm dialog.
@ Batch
Batch processing dialog.
A parameter for "aggregate" configurations, which consist of a definition of desired output fields,...
static QString typeName()
Returns the type name for the parameter class.
Base class for the definition of processing parameters.
QString description() const
Returns the description for the parameter.
QString name() const
Returns the name of the parameter.
An input feature source (such as vector layers) parameter for processing algorithms.
A vector layer (with or without geometry) parameter for processing algorithms.
Contains settings which reflect the context in which a Processing parameter widget is shown,...
QgsProcessingModelAlgorithm * model() const
Returns the model which the parameter widget is associated with.
static QgsVectorLayer * parameterAsVectorLayer(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Evaluates the parameter with matching definition to a vector layer.
Represents a vector layer which manages a vector based data sets.
@ VectorLayer
Vector layer.
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
const QgsField & field
Definition: qgsfield.h:463
The Aggregate struct holds information about an aggregate column.
QString source
The source expression used as the input for the aggregate calculation.
QgsField field
The field in its current status (it might have been renamed)