QGIS API Documentation 3.41.0-Master (cea29feecf2)
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#include "moc_qgsprocessingaggregatewidgetwrapper.cpp"
18
19#include <QBoxLayout>
20#include <QLineEdit>
21#include <QMessageBox>
22#include <QPushButton>
23#include <QStandardItemModel>
24#include <QToolButton>
25#include <QItemSelectionModel>
26
27#include "qgspanelwidget.h"
28
31
33
35
36
37//
38// QgsProcessingAggregatePanelWidget
39//
40
41
42QgsProcessingAggregatePanelWidget::QgsProcessingAggregatePanelWidget( QWidget *parent )
43 : QgsPanelWidget( parent )
44{
45 setupUi( this );
46
47 mModel = mFieldsView->model();
48
49 mLayerCombo->setAllowEmptyLayer( true );
50 mLayerCombo->setFilters( Qgis::LayerFilter::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 if ( !mBlockChangedSignal )
61 {
62 emit changed();
63 }
64 } );
65}
66
67void QgsProcessingAggregatePanelWidget::setLayer( QgsVectorLayer *layer )
68{
69 if ( layer == mLayer )
70 return;
71
72 mLayer = layer;
73 mFieldsView->setSourceLayer( mLayer );
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 | QMessageBox::No )
84 );
85 dlg.setDefaultButton( QMessageBox::No );
86 if ( dlg.exec() == QMessageBox::Yes )
87 {
88 loadFieldsFromLayer();
89 }
90}
91
92QgsVectorLayer *QgsProcessingAggregatePanelWidget::layer()
93{
94 return mLayer;
95}
96
97QVariant 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( "type_name" ), aggregate.field.typeName() );
109 def.insert( QStringLiteral( "length" ), aggregate.field.length() );
110 def.insert( QStringLiteral( "precision" ), aggregate.field.precision() );
111 def.insert( QStringLiteral( "sub_type" ), static_cast<int>( aggregate.field.subType() ) );
112 def.insert( QStringLiteral( "input" ), aggregate.source );
113 def.insert( QStringLiteral( "aggregate" ), aggregate.aggregate );
114 def.insert( QStringLiteral( "delimiter" ), aggregate.delimiter );
115 results.append( def );
116 }
117 return results;
118}
119
120void QgsProcessingAggregatePanelWidget::setValue( const QVariant &value )
121{
122 if ( value.userType() != QMetaType::Type::QVariantList )
123 return;
124
125 QList<QgsAggregateMappingModel::Aggregate> aggregates;
126
127 const QVariantList fields = value.toList();
128 aggregates.reserve( fields.size() );
129 for ( const QVariant &field : fields )
130 {
131 const QVariantMap map = field.toMap();
132 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() ) );
133
135 aggregate.field = f;
136
137 aggregate.source = map.value( QStringLiteral( "input" ) ).toString();
138 aggregate.aggregate = map.value( QStringLiteral( "aggregate" ) ).toString();
139 aggregate.delimiter = map.value( QStringLiteral( "delimiter" ) ).toString();
140
141 aggregates.append( aggregate );
142 }
143
144 mBlockChangedSignal = true;
145
146 if ( aggregates.size() > 0 )
147 mFieldsView->setMapping( aggregates );
148
149 mBlockChangedSignal = false;
150
151 emit changed();
152}
153
154void QgsProcessingAggregatePanelWidget::registerExpressionContextGenerator( const QgsExpressionContextGenerator *generator )
155{
156 mFieldsView->registerExpressionContextGenerator( generator );
157}
158
159void QgsProcessingAggregatePanelWidget::loadFieldsFromLayer()
160{
161 if ( mLayer )
162 {
163 mFieldsView->setSourceFields( mLayer->fields() );
164 }
165}
166
167void QgsProcessingAggregatePanelWidget::addField()
168{
169 const int rowCount = mModel->rowCount();
170 mModel->appendField( QgsField( QStringLiteral( "new_field" ) ) );
171 const QModelIndex index = mModel->index( rowCount, 0 );
172 mFieldsView->selectionModel()->select(
173 index,
174 QItemSelectionModel::SelectionFlags(
175 QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current | QItemSelectionModel::Rows
176 )
177 );
178 mFieldsView->scrollTo( index );
179}
180
181void QgsProcessingAggregatePanelWidget::loadLayerFields()
182{
183 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerCombo->currentLayer() ) )
184 {
185 mFieldsView->setSourceFields( vl->fields() );
186 }
187}
188
189//
190// QgsProcessingAggregateParameterDefinitionWidget
191//
192
193QgsProcessingAggregateParameterDefinitionWidget::QgsProcessingAggregateParameterDefinitionWidget( QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent )
194 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
195{
196 QVBoxLayout *vlayout = new QVBoxLayout();
197 vlayout->setContentsMargins( 0, 0, 0, 0 );
198
199 vlayout->addWidget( new QLabel( tr( "Parent layer" ) ) );
200
201 mParentLayerComboBox = new QComboBox();
202 mParentLayerComboBox->addItem( tr( "None" ), QVariant() );
203
204 QString initialParent;
205 if ( const QgsProcessingParameterAggregate *aggregateParam = dynamic_cast<const QgsProcessingParameterAggregate *>( definition ) )
206 initialParent = aggregateParam->parentLayerParameterName();
207
208 if ( auto *lModel = widgetContext.model() )
209 {
210 // populate combo box with other model input choices
211 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
212 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
213 {
214 if ( const QgsProcessingParameterFeatureSource *definition = dynamic_cast<const QgsProcessingParameterFeatureSource *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
215 {
216 mParentLayerComboBox->addItem( definition->description(), definition->name() );
217 if ( !initialParent.isEmpty() && initialParent == definition->name() )
218 {
219 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
220 }
221 }
222 else if ( const QgsProcessingParameterVectorLayer *definition = dynamic_cast<const QgsProcessingParameterVectorLayer *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
223 {
224 mParentLayerComboBox->addItem( definition->description(), definition->name() );
225 if ( !initialParent.isEmpty() && initialParent == definition->name() )
226 {
227 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
228 }
229 }
230 }
231 }
232
233 if ( mParentLayerComboBox->count() == 1 && !initialParent.isEmpty() )
234 {
235 // if no parent candidates found, we just add the existing one as a placeholder
236 mParentLayerComboBox->addItem( initialParent, initialParent );
237 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
238 }
239
240 vlayout->addWidget( mParentLayerComboBox );
241 setLayout( vlayout );
242}
243
244QgsProcessingParameterDefinition *QgsProcessingAggregateParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
245{
246 auto param = std::make_unique<QgsProcessingParameterAggregate>( name, description, mParentLayerComboBox->currentData().toString() );
247 param->setFlags( flags );
248 return param.release();
249}
250
251//
252// QgsProcessingAggregateWidgetWrapper
253//
254
255QgsProcessingAggregateWidgetWrapper::QgsProcessingAggregateWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type, QWidget *parent )
256 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
257{
258}
259
260QString QgsProcessingAggregateWidgetWrapper::parameterType() const
261{
263}
264
265QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingAggregateWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type )
266{
267 return new QgsProcessingAggregateWidgetWrapper( parameter, type );
268}
269
270QWidget *QgsProcessingAggregateWidgetWrapper::createWidget()
271{
272 mPanel = new QgsProcessingAggregatePanelWidget( nullptr );
273 mPanel->setToolTip( parameterDefinition()->toolTip() );
274 mPanel->registerExpressionContextGenerator( this );
275
276 connect( mPanel, &QgsProcessingAggregatePanelWidget::changed, this, [=] {
277 emit widgetValueHasChanged( this );
278 } );
279
280 return mPanel;
281}
282
283QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingAggregateWidgetWrapper::createParameterDefinitionWidget( QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm )
284{
285 return new QgsProcessingAggregateParameterDefinitionWidget( context, widgetContext, definition, algorithm );
286}
287
288void QgsProcessingAggregateWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
289{
291 switch ( type() )
292 {
295 {
296 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
297 {
298 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterAggregate *>( parameterDefinition() )->parentLayerParameterName() )
299 {
300 setParentLayerWrapperValue( wrapper );
302 setParentLayerWrapperValue( wrapper );
303 } );
304 break;
305 }
306 }
307 break;
308 }
309
311 break;
312 }
313}
314
315int QgsProcessingAggregateWidgetWrapper::stretch() const
316{
317 return 1;
318}
319
320void QgsProcessingAggregateWidgetWrapper::setParentLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
321{
322 // evaluate value to layer
323 QgsProcessingContext *context = nullptr;
324 std::unique_ptr<QgsProcessingContext> tmpContext;
325 if ( mProcessingContextGenerator )
326 context = mProcessingContextGenerator->processingContext();
327
328 if ( !context )
329 {
330 tmpContext = std::make_unique<QgsProcessingContext>();
331 context = tmpContext.get();
332 }
333
334 QgsVectorLayer *layer = QgsProcessingParameters::parameterAsVectorLayer( parentWrapper->parameterDefinition(), parentWrapper->parameterValue(), *context );
335 if ( !layer )
336 {
337 if ( mPanel )
338 mPanel->setLayer( nullptr );
339 return;
340 }
341
342 // need to grab ownership of layer if required - otherwise layer may be deleted when context
343 // goes out of scope
344 std::unique_ptr<QgsMapLayer> ownedLayer( context->takeResultLayer( layer->id() ) );
345 if ( ownedLayer && ownedLayer->type() == Qgis::LayerType::Vector )
346 {
347 mParentLayer.reset( qobject_cast<QgsVectorLayer *>( ownedLayer.release() ) );
348 layer = mParentLayer.get();
349 }
350 else
351 {
352 // don't need ownership of this layer - it wasn't owned by context (so e.g. is owned by the project)
353 }
354
355 if ( mPanel )
356 mPanel->setLayer( layer );
357}
358
359void QgsProcessingAggregateWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext & )
360{
361 if ( mPanel )
362 mPanel->setValue( value );
363}
364
365QVariant QgsProcessingAggregateWidgetWrapper::widgetValue() const
366{
367 return mPanel ? mPanel->value() : QVariant();
368}
369
370QStringList QgsProcessingAggregateWidgetWrapper::compatibleParameterTypes() const
371{
372 return QStringList()
374}
375
376QStringList QgsProcessingAggregateWidgetWrapper::compatibleOutputTypes() const
377{
378 return QStringList();
379}
380
381QString QgsProcessingAggregateWidgetWrapper::modelerExpressionFormatString() const
382{
383 return tr( "an array of map items, each containing a 'name', 'type', 'aggregate' and 'input' value (and optional 'length' and 'precision' values)." );
384}
385
386const QgsVectorLayer *QgsProcessingAggregateWidgetWrapper::linkedVectorLayer() const
387{
388 if ( mPanel && mPanel->layer() )
389 return mPanel->layer();
390
392}
393
@ Vector
Vector layer.
QFlags< ProcessingParameterFlag > ProcessingParameterFlags
Flags which dictate the behavior of Processing parameters.
Definition qgis.h:3544
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:53
QString id
Definition qgsmaplayer.h:79
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.
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 data sets.
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)