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