QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
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 "qgspanelwidget.h"
22
23#include <QBoxLayout>
24#include <QItemSelectionModel>
25#include <QLineEdit>
26#include <QMessageBox>
27#include <QPushButton>
28#include <QStandardItemModel>
29#include <QToolButton>
30
31#include "moc_qgsprocessingaggregatewidgetwrapper.cpp"
32
34
35
36//
37// QgsProcessingAggregatePanelWidget
38//
39
40
41QgsProcessingAggregatePanelWidget::QgsProcessingAggregatePanelWidget( QWidget *parent )
42 : QgsPanelWidget( parent )
43{
44 setupUi( this );
45
46 mModel = mFieldsView->model();
47
48 mLayerCombo->setAllowEmptyLayer( true );
49 mLayerCombo->setFilters( Qgis::LayerFilter::VectorLayer );
50
51 connect( mResetButton, &QPushButton::clicked, this, &QgsProcessingAggregatePanelWidget::loadFieldsFromLayer );
52 connect( mAddButton, &QPushButton::clicked, this, &QgsProcessingAggregatePanelWidget::addField );
53 connect( mDeleteButton, &QPushButton::clicked, mFieldsView, &QgsAggregateMappingWidget::removeSelectedFields );
54 connect( mUpButton, &QPushButton::clicked, mFieldsView, &QgsAggregateMappingWidget::moveSelectedFieldsUp );
55 connect( mDownButton, &QPushButton::clicked, mFieldsView, &QgsAggregateMappingWidget::moveSelectedFieldsDown );
56 connect( mLoadLayerFieldsButton, &QPushButton::clicked, this, &QgsProcessingAggregatePanelWidget::loadLayerFields );
57
58 connect( mFieldsView, &QgsAggregateMappingWidget::changed, this, [this] {
59 if ( !mBlockChangedSignal )
60 {
61 emit changed();
62 }
63 } );
64}
65
66void QgsProcessingAggregatePanelWidget::setLayer( QgsVectorLayer *layer )
67{
68 if ( layer == mLayer )
69 return;
70
71 mLayer = layer;
72 mFieldsView->setSourceLayer( mLayer );
73 if ( mModel->rowCount() == 0 )
74 {
75 loadFieldsFromLayer();
76 return;
77 }
78
79 if ( mSkipConfirmDialog )
80 {
81 return;
82 }
83
84 QMessageBox dlg( this );
85 dlg.setText( tr( "Do you want to reset the field mapping?" ) );
86 dlg.setStandardButtons(
87 QMessageBox::StandardButtons( QMessageBox::Yes | QMessageBox::No )
88 );
89 dlg.setDefaultButton( QMessageBox::No );
90 if ( dlg.exec() == QMessageBox::Yes )
91 {
92 loadFieldsFromLayer();
93 }
94}
95
96QgsVectorLayer *QgsProcessingAggregatePanelWidget::layer()
97{
98 return mLayer;
99}
100
101QVariant QgsProcessingAggregatePanelWidget::value() const
102{
103 const QList<QgsAggregateMappingModel::Aggregate> mapping = mFieldsView->mapping();
104
105 QVariantList results;
106 results.reserve( mapping.size() );
107 for ( const QgsAggregateMappingModel::Aggregate &aggregate : mapping )
108 {
109 QVariantMap def;
110 def.insert( QStringLiteral( "name" ), aggregate.field.name() );
111 def.insert( QStringLiteral( "type" ), static_cast<int>( aggregate.field.type() ) );
112 def.insert( QStringLiteral( "type_name" ), aggregate.field.typeName() );
113 def.insert( QStringLiteral( "length" ), aggregate.field.length() );
114 def.insert( QStringLiteral( "precision" ), aggregate.field.precision() );
115 def.insert( QStringLiteral( "sub_type" ), static_cast<int>( aggregate.field.subType() ) );
116 def.insert( QStringLiteral( "input" ), aggregate.source );
117 def.insert( QStringLiteral( "aggregate" ), aggregate.aggregate );
118 def.insert( QStringLiteral( "delimiter" ), aggregate.delimiter );
119 results.append( def );
120 }
121 return results;
122}
123
124void QgsProcessingAggregatePanelWidget::setValue( const QVariant &value )
125{
126 if ( value.userType() != QMetaType::Type::QVariantList )
127 return;
128
129 QList<QgsAggregateMappingModel::Aggregate> aggregates;
130
131 const QVariantList fields = value.toList();
132 aggregates.reserve( fields.size() );
133 for ( const QVariant &field : fields )
134 {
135 const QVariantMap map = field.toMap();
136 const QgsField f( map.value( QStringLiteral( "name" ) ).toString(), static_cast<QMetaType::Type>( map.value( QStringLiteral( "type" ), static_cast<int>( QMetaType::Type::UnknownType ) ).toInt() ), map.value( QStringLiteral( "type_name" ), QVariant::typeToName( static_cast<QMetaType::Type>( map.value( QStringLiteral( "type" ), static_cast<int>( QMetaType::Type::UnknownType ) ).toInt() ) ) ).toString(), map.value( QStringLiteral( "length" ), 0 ).toInt(), map.value( QStringLiteral( "precision" ), 0 ).toInt(), QString(), static_cast<QMetaType::Type>( map.value( QStringLiteral( "sub_type" ), QgsVariantUtils::createNullVariant( QMetaType::Type::UnknownType ) ).toInt() ) );
137
139 aggregate.field = f;
140
141 aggregate.source = map.value( QStringLiteral( "input" ) ).toString();
142 aggregate.aggregate = map.value( QStringLiteral( "aggregate" ) ).toString();
143 aggregate.delimiter = map.value( QStringLiteral( "delimiter" ) ).toString();
144
145 aggregates.append( aggregate );
146 }
147
148 mBlockChangedSignal = true;
149
150 if ( aggregates.size() > 0 )
151 mFieldsView->setMapping( aggregates );
152
153 mBlockChangedSignal = false;
154
155 emit changed();
156}
157
158void QgsProcessingAggregatePanelWidget::registerExpressionContextGenerator( const QgsExpressionContextGenerator *generator )
159{
160 mFieldsView->registerExpressionContextGenerator( generator );
161}
162
163void QgsProcessingAggregatePanelWidget::loadFieldsFromLayer()
164{
165 if ( mLayer )
166 {
167 mFieldsView->setSourceFields( mLayer->fields() );
168 }
169}
170
171void QgsProcessingAggregatePanelWidget::addField()
172{
173 const int rowCount = mModel->rowCount();
174 mModel->appendField( QgsField( QStringLiteral( "new_field" ) ) );
175 const QModelIndex index = mModel->index( rowCount, 0 );
176 mFieldsView->selectionModel()->select(
177 index,
178 QItemSelectionModel::SelectionFlags(
179 QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current | QItemSelectionModel::Rows
180 )
181 );
182 mFieldsView->scrollTo( index );
183}
184
185void QgsProcessingAggregatePanelWidget::loadLayerFields()
186{
187 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerCombo->currentLayer() ) )
188 {
189 mFieldsView->setSourceFields( vl->fields() );
190 }
191}
192
193//
194// QgsProcessingAggregateParameterDefinitionWidget
195//
196
197QgsProcessingAggregateParameterDefinitionWidget::QgsProcessingAggregateParameterDefinitionWidget( QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent )
198 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
199{
200 QVBoxLayout *vlayout = new QVBoxLayout();
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 ( auto *lModel = widgetContext.model() )
213 {
214 // populate combo box with other model input choices
215 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
216 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
217 {
218 if ( const QgsProcessingParameterFeatureSource *definition = dynamic_cast<const QgsProcessingParameterFeatureSource *>( lModel->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 *>( lModel->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
248QgsProcessingParameterDefinition *QgsProcessingAggregateParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
249{
250 auto param = std::make_unique<QgsProcessingParameterAggregate>( name, description, mParentLayerComboBox->currentData().toString() );
251 param->setFlags( flags );
252 return param.release();
253}
254
255//
256// QgsProcessingAggregateWidgetWrapper
257//
258
259QgsProcessingAggregateWidgetWrapper::QgsProcessingAggregateWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
260 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
261{
262}
263
264QString QgsProcessingAggregateWidgetWrapper::parameterType() const
265{
267}
268
269QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingAggregateWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
270{
271 return new QgsProcessingAggregateWidgetWrapper( parameter, type );
272}
273
274QWidget *QgsProcessingAggregateWidgetWrapper::createWidget()
275{
276 mPanel = new QgsProcessingAggregatePanelWidget( nullptr );
277 mPanel->setToolTip( parameterDefinition()->toolTip() );
278 mPanel->registerExpressionContextGenerator( this );
279
280 connect( mPanel, &QgsProcessingAggregatePanelWidget::changed, this, [this] {
281 emit widgetValueHasChanged( this );
282 } );
283
284 return mPanel;
285}
286
287QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingAggregateWidgetWrapper::createParameterDefinitionWidget( QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm )
288{
289 return new QgsProcessingAggregateParameterDefinitionWidget( context, widgetContext, definition, algorithm );
290}
291
292void QgsProcessingAggregateWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
293{
295 switch ( type() )
296 {
299 {
300 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
301 {
302 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterAggregate *>( parameterDefinition() )->parentLayerParameterName() )
303 {
304 setParentLayerWrapperValue( wrapper );
305 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] {
306 setParentLayerWrapperValue( wrapper );
307 } );
308 break;
309 }
310 }
311 break;
312 }
313
315 break;
316 }
317}
318
319int QgsProcessingAggregateWidgetWrapper::stretch() const
320{
321 return 1;
322}
323
324void QgsProcessingAggregateWidgetWrapper::setParentLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
325{
326 // evaluate value to layer
327 QgsProcessingContext *context = nullptr;
328 std::unique_ptr<QgsProcessingContext> tmpContext;
329 if ( mProcessingContextGenerator )
330 context = mProcessingContextGenerator->processingContext();
331
332 if ( !context )
333 {
334 tmpContext = std::make_unique<QgsProcessingContext>();
335 context = tmpContext.get();
336 }
337
338 QgsVectorLayer *layer = QgsProcessingParameters::parameterAsVectorLayer( parentWrapper->parameterDefinition(), parentWrapper->parameterValue(), *context );
339 if ( !layer )
340 {
341 if ( mPanel )
342 mPanel->setLayer( nullptr );
343 return;
344 }
345
346 // need to grab ownership of layer if required - otherwise layer may be deleted when context
347 // goes out of scope
348 std::unique_ptr<QgsMapLayer> ownedLayer( context->takeResultLayer( layer->id() ) );
349 if ( ownedLayer && ownedLayer->type() == Qgis::LayerType::Vector )
350 {
351 mParentLayer.reset( qobject_cast<QgsVectorLayer *>( ownedLayer.release() ) );
352 layer = mParentLayer.get();
353 }
354 else
355 {
356 // don't need ownership of this layer - it wasn't owned by context (so e.g. is owned by the project)
357 }
358
359 if ( mPanel )
360 mPanel->setLayer( layer );
361}
362
363void QgsProcessingAggregateWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext & )
364{
365 if ( mPanel )
366 mPanel->setValue( value );
367}
368
369QVariant QgsProcessingAggregateWidgetWrapper::widgetValue() const
370{
371 return mPanel ? mPanel->value() : QVariant();
372}
373
374QString QgsProcessingAggregateWidgetWrapper::modelerExpressionFormatString() const
375{
376 return tr( "an array of map items, each containing a 'name', 'type', 'aggregate' and 'input' value (and optional 'length' and 'precision' values)." );
377}
378
379const QgsVectorLayer *QgsProcessingAggregateWidgetWrapper::linkedVectorLayer() const
380{
381 if ( mPanel && mPanel->layer() )
382 return mPanel->layer();
383
385}
386
ProcessingMode
Types of modes which Processing widgets can be created for.
Definition qgis.h:3671
@ Batch
Batch processing mode.
Definition qgis.h:3673
@ Modeler
Modeler mode.
Definition qgis.h:3674
@ Standard
Standard (single-run) algorithm mode.
Definition qgis.h:3672
@ Vector
Vector layer.
Definition qgis.h:191
QFlags< ProcessingParameterFlag > ProcessingParameterFlags
Flags which dictate the behavior of Processing parameters.
Definition qgis.h:3777
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:54
QString id
Definition qgsmaplayer.h:83
Base class for any widget that can be shown as an 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...
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.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
Represents a vector layer which manages a vector based dataset.
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
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).