QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
qgsprocessingfieldmapwidgetwrapper.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsprocessingfieldmapwidgetwrapper.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_qgsprocessingfieldmapwidgetwrapper.cpp"
32
34
35//
36// QgsProcessingFieldMapPanelWidget
37//
38
39
40QgsProcessingFieldMapPanelWidget::QgsProcessingFieldMapPanelWidget( QWidget *parent )
41 : QgsPanelWidget( parent )
42{
43 setupUi( this );
44
45 mModel = mFieldsView->model();
46 mFieldsView->setDestinationEditable( true );
47
48 mLayerCombo->setAllowEmptyLayer( true );
49 mLayerCombo->setFilters( Qgis::LayerFilter::VectorLayer );
50
51 connect( mResetButton, &QPushButton::clicked, this, &QgsProcessingFieldMapPanelWidget::loadFieldsFromLayer );
52 connect( mAddButton, &QPushButton::clicked, this, &QgsProcessingFieldMapPanelWidget::addField );
53 connect( mDeleteButton, &QPushButton::clicked, mFieldsView, &QgsFieldMappingWidget::removeSelectedFields );
54 connect( mUpButton, &QPushButton::clicked, mFieldsView, &QgsFieldMappingWidget::moveSelectedFieldsUp );
55 connect( mDownButton, &QPushButton::clicked, mFieldsView, &QgsFieldMappingWidget::moveSelectedFieldsDown );
56 connect( mInvertSelectionButton, &QPushButton::clicked, mFieldsView, &QgsFieldMappingWidget::invertSelection );
57 connect( mLoadLayerFieldsButton, &QPushButton::clicked, this, &QgsProcessingFieldMapPanelWidget::loadLayerFields );
58
59
60 connect( mFieldsView, &QgsFieldMappingWidget::changed, this, [this] {
61 if ( !mBlockChangedSignal )
62 {
63 emit changed();
64 }
65 } );
66}
67
68void QgsProcessingFieldMapPanelWidget::setLayer( QgsVectorLayer *layer )
69{
70 if ( layer == mLayer )
71 return;
72
73 mLayer = layer;
74 mFieldsView->setSourceLayer( mLayer );
75 if ( mModel->rowCount() == 0 )
76 {
77 loadFieldsFromLayer();
78 return;
79 }
80
81 if ( mSkipConfirmDialog )
82 {
83 return;
84 }
85
86 QMessageBox dlg( this );
87 dlg.setText( tr( "Do you want to reset the field mapping?" ) );
88 dlg.setStandardButtons(
89 QMessageBox::StandardButtons( QMessageBox::Yes | QMessageBox::No )
90 );
91 dlg.setDefaultButton( QMessageBox::No );
92 if ( dlg.exec() == QMessageBox::Yes )
93 {
94 loadFieldsFromLayer();
95 }
96}
97
98QgsVectorLayer *QgsProcessingFieldMapPanelWidget::layer()
99{
100 return mLayer;
101}
102
103QVariant QgsProcessingFieldMapPanelWidget::value() const
104{
105 const QList<QgsFieldMappingModel::Field> mapping = mFieldsView->mapping();
106
107 QVariantList results;
108 results.reserve( mapping.size() );
109 for ( const QgsFieldMappingModel::Field &field : mapping )
110 {
111 QVariantMap def;
112 def.insert( QStringLiteral( "name" ), field.field.name() );
113 def.insert( QStringLiteral( "type" ), static_cast<int>( field.field.type() ) );
114 def.insert( QStringLiteral( "type_name" ), field.field.typeName() );
115 def.insert( QStringLiteral( "length" ), field.field.length() );
116 def.insert( QStringLiteral( "precision" ), field.field.precision() );
117 def.insert( QStringLiteral( "sub_type" ), static_cast<int>( field.field.subType() ) );
118 def.insert( QStringLiteral( "expression" ), field.expression );
119 def.insert( QStringLiteral( "alias" ), field.field.alias() );
120 def.insert( QStringLiteral( "comment" ), field.field.comment() );
121 results.append( def );
122 }
123 return results;
124}
125
126void QgsProcessingFieldMapPanelWidget::setValue( const QVariant &value )
127{
128 if ( value.userType() != QMetaType::Type::QVariantList )
129 return;
130
131 QgsFields destinationFields;
132 QMap<QString, QString> expressions;
133
134 const QgsFields layerFields = mLayer ? mLayer->fields() : QgsFields();
135 const QVariantList fields = value.toList();
136 for ( const QVariant &field : fields )
137 {
138 const QVariantMap map = field.toMap();
139 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() ) );
140 f.setAlias( map.value( QStringLiteral( "alias" ) ).toString() );
141 f.setComment( map.value( QStringLiteral( "comment" ) ).toString() );
142
143 if ( map.contains( QStringLiteral( "constraints" ) ) )
144 {
145 const QgsFieldConstraints::Constraints constraints = static_cast<QgsFieldConstraints::Constraints>( map.value( QStringLiteral( "constraints" ), 0 ).toInt() );
146 QgsFieldConstraints fieldConstraints;
147
148 if ( constraints & QgsFieldConstraints::ConstraintNotNull )
150 if ( constraints & QgsFieldConstraints::ConstraintUnique )
154
155 f.setConstraints( fieldConstraints );
156 }
157
158 if ( !map.value( QStringLiteral( "expression" ) ).toString().isEmpty() )
159 {
160 expressions.insert( f.name(), map.value( QStringLiteral( "expression" ) ).toString() );
161 }
162
163 destinationFields.append( f );
164 }
165
166 mBlockChangedSignal = true;
167 if ( destinationFields.size() > 0 )
168 mFieldsView->setDestinationFields( destinationFields, expressions );
169 mBlockChangedSignal = false;
170
171 emit changed();
172}
173
174void QgsProcessingFieldMapPanelWidget::registerExpressionContextGenerator( const QgsExpressionContextGenerator *generator )
175{
176 mFieldsView->registerExpressionContextGenerator( generator );
177}
178
179void QgsProcessingFieldMapPanelWidget::loadFieldsFromLayer()
180{
181 if ( mLayer )
182 {
183 mFieldsView->setSourceFields( mLayer->fields() );
184 mFieldsView->setDestinationFields( mLayer->fields() );
185 }
186}
187
188void QgsProcessingFieldMapPanelWidget::addField()
189{
190 const int rowCount = mModel->rowCount();
191 mModel->appendField( QgsField( QStringLiteral( "new_field" ) ) );
192 const QModelIndex index = mModel->index( rowCount, 0 );
193 mFieldsView->selectionModel()->select(
194 index,
195 QItemSelectionModel::SelectionFlags(
196 QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current | QItemSelectionModel::Rows
197 )
198 );
199 mFieldsView->scrollTo( index );
200}
201
202void QgsProcessingFieldMapPanelWidget::loadLayerFields()
203{
204 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerCombo->currentLayer() ) )
205 {
206 mFieldsView->setDestinationFields( vl->fields() );
207 }
208}
209
210//
211// QgsProcessingFieldMapParameterDefinitionWidget
212//
213
214QgsProcessingFieldMapParameterDefinitionWidget::QgsProcessingFieldMapParameterDefinitionWidget( QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent )
215 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
216{
217 QVBoxLayout *vlayout = new QVBoxLayout();
218 vlayout->setContentsMargins( 0, 0, 0, 0 );
219
220 vlayout->addWidget( new QLabel( tr( "Parent layer" ) ) );
221
222 mParentLayerComboBox = new QComboBox();
223 mParentLayerComboBox->addItem( tr( "None" ), QVariant() );
224
225 QString initialParent;
226 if ( const QgsProcessingParameterFieldMapping *mapParam = dynamic_cast<const QgsProcessingParameterFieldMapping *>( definition ) )
227 initialParent = mapParam->parentLayerParameterName();
228
229 if ( auto *lModel = widgetContext.model() )
230 {
231 // populate combo box with other model input choices
232 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
233 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
234 {
235 if ( const QgsProcessingParameterFeatureSource *definition = dynamic_cast<const QgsProcessingParameterFeatureSource *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
236 {
237 mParentLayerComboBox->addItem( definition->description(), definition->name() );
238 if ( !initialParent.isEmpty() && initialParent == definition->name() )
239 {
240 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
241 }
242 }
243 else if ( const QgsProcessingParameterVectorLayer *definition = dynamic_cast<const QgsProcessingParameterVectorLayer *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
244 {
245 mParentLayerComboBox->addItem( definition->description(), definition->name() );
246 if ( !initialParent.isEmpty() && initialParent == definition->name() )
247 {
248 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
249 }
250 }
251 }
252 }
253
254 if ( mParentLayerComboBox->count() == 1 && !initialParent.isEmpty() )
255 {
256 // if no parent candidates found, we just add the existing one as a placeholder
257 mParentLayerComboBox->addItem( initialParent, initialParent );
258 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
259 }
260
261 vlayout->addWidget( mParentLayerComboBox );
262 setLayout( vlayout );
263}
264
265QgsProcessingParameterDefinition *QgsProcessingFieldMapParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
266{
267 auto param = std::make_unique<QgsProcessingParameterFieldMapping>( name, description, mParentLayerComboBox->currentData().toString() );
268 param->setFlags( flags );
269 return param.release();
270}
271
272//
273// QgsProcessingFieldMapWidgetWrapper
274//
275
276QgsProcessingFieldMapWidgetWrapper::QgsProcessingFieldMapWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
277 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
278{
279}
280
281QString QgsProcessingFieldMapWidgetWrapper::parameterType() const
282{
284}
285
286QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingFieldMapWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
287{
288 return new QgsProcessingFieldMapWidgetWrapper( parameter, type );
289}
290
291QWidget *QgsProcessingFieldMapWidgetWrapper::createWidget()
292{
293 mPanel = new QgsProcessingFieldMapPanelWidget( nullptr );
294 mPanel->setToolTip( parameterDefinition()->toolTip() );
295 mPanel->registerExpressionContextGenerator( this );
296
297 connect( mPanel, &QgsProcessingFieldMapPanelWidget::changed, this, [this] {
298 emit widgetValueHasChanged( this );
299 } );
300
301 return mPanel;
302}
303
304QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingFieldMapWidgetWrapper::createParameterDefinitionWidget( QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm )
305{
306 return new QgsProcessingFieldMapParameterDefinitionWidget( context, widgetContext, definition, algorithm );
307}
308
309void QgsProcessingFieldMapWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
310{
312 switch ( type() )
313 {
316 {
317 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
318 {
319 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterFieldMapping *>( parameterDefinition() )->parentLayerParameterName() )
320 {
321 setParentLayerWrapperValue( wrapper );
322 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] {
323 setParentLayerWrapperValue( wrapper );
324 } );
325 break;
326 }
327 }
328 break;
329 }
330
332 break;
333 }
334}
335
336int QgsProcessingFieldMapWidgetWrapper::stretch() const
337{
338 return 1;
339}
340
341void QgsProcessingFieldMapWidgetWrapper::setParentLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
342{
343 // evaluate value to layer
344 QgsProcessingContext *context = nullptr;
345 std::unique_ptr<QgsProcessingContext> tmpContext;
346 if ( mProcessingContextGenerator )
347 context = mProcessingContextGenerator->processingContext();
348
349 if ( !context )
350 {
351 tmpContext = std::make_unique<QgsProcessingContext>();
352 context = tmpContext.get();
353 }
354
355 QgsVectorLayer *layer = QgsProcessingParameters::parameterAsVectorLayer( parentWrapper->parameterDefinition(), parentWrapper->parameterValue(), *context );
356 if ( !layer )
357 {
358 if ( mPanel )
359 mPanel->setLayer( nullptr );
360 return;
361 }
362
363 // need to grab ownership of layer if required - otherwise layer may be deleted when context
364 // goes out of scope
365 std::unique_ptr<QgsMapLayer> ownedLayer( context->takeResultLayer( layer->id() ) );
366 if ( ownedLayer && ownedLayer->type() == Qgis::LayerType::Vector )
367 {
368 mParentLayer.reset( qobject_cast<QgsVectorLayer *>( ownedLayer.release() ) );
369 layer = mParentLayer.get();
370 }
371 else
372 {
373 // don't need ownership of this layer - it wasn't owned by context (so e.g. is owned by the project)
374 }
375
376 if ( mPanel )
377 mPanel->setLayer( layer );
378}
379
380void QgsProcessingFieldMapWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext & )
381{
382 if ( mPanel )
383 mPanel->setValue( value );
384}
385
386QVariant QgsProcessingFieldMapWidgetWrapper::widgetValue() const
387{
388 return mPanel ? mPanel->value() : QVariant();
389}
390
391QString QgsProcessingFieldMapWidgetWrapper::modelerExpressionFormatString() const
392{
393 return tr( "an array of map items, each containing a 'name', 'type' and 'expression' values (and optional 'length' and 'precision' values)." );
394}
395
396const QgsVectorLayer *QgsProcessingFieldMapWidgetWrapper::linkedVectorLayer() const
397{
398 if ( mPanel && mPanel->layer() )
399 return mPanel->layer();
400
402}
403
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.
Abstract interface for generating an expression context.
Stores information about constraints which may be present on a field.
@ ConstraintNotNull
Field may not be null.
@ ConstraintUnique
Field must have a unique value.
@ ConstraintExpression
Field has an expression constraint set. See constraintExpression().
void setConstraint(Constraint constraint, ConstraintOrigin origin=ConstraintOriginLayer)
Sets a constraint on the field.
QFlags< Constraint > Constraints
bool removeSelectedFields()
Removes the currently selected field from the model.
bool moveSelectedFieldsDown()
Moves down the currently selected field.
bool moveSelectedFieldsUp()
Moves up currently selected field.
void invertSelection()
Invert the field selection state.
void changed()
Emitted when the fields defined in the widget are changed.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:54
Container of fields for a vector layer.
Definition qgsfields.h:46
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:73
int size() const
Returns number of items.
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...
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 parameter for "field mapping" configurations, which consist of a definition of desired output field...
static QString typeName()
Returns the type name for the parameter class.
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 Field struct holds information about a mapped field.