QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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 <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 
32 
34 
35 //
36 // QgsProcessingFieldMapPanelWidget
37 //
38 
39 
40 QgsProcessingFieldMapPanelWidget::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( QgsMapLayerProxyModel::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( mLoadLayerFieldsButton, &QPushButton::clicked, this, &QgsProcessingFieldMapPanelWidget::loadLayerFields );
57 
58  connect( mFieldsView, &QgsFieldMappingWidget::changed, this, [ = ]
59  {
60  if ( !mBlockChangedSignal )
61  {
62  emit changed();
63  }
64  } );
65 }
66 
67 void QgsProcessingFieldMapPanelWidget::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 |
84  QMessageBox::No ) );
85  dlg.setDefaultButton( QMessageBox::No );
86  if ( dlg.exec() == QMessageBox::Yes )
87  {
88  loadFieldsFromLayer();
89  }
90 }
91 
92 QgsVectorLayer *QgsProcessingFieldMapPanelWidget::layer()
93 {
94  return mLayer;
95 }
96 
97 QVariant QgsProcessingFieldMapPanelWidget::value() const
98 {
99  const QList<QgsFieldMappingModel::Field> mapping = mFieldsView->mapping();
100 
101  QVariantList results;
102  results.reserve( mapping.size() );
103  for ( const QgsFieldMappingModel::Field &field : mapping )
104  {
105  QVariantMap def;
106  def.insert( QStringLiteral( "name" ), field.field.name() );
107  def.insert( QStringLiteral( "type" ), static_cast< int >( field.field.type() ) );
108  def.insert( QStringLiteral( "type_name" ), field.field.typeName() );
109  def.insert( QStringLiteral( "length" ), field.field.length() );
110  def.insert( QStringLiteral( "precision" ), field.field.precision() );
111  def.insert( QStringLiteral( "sub_type" ), static_cast< int >( field.field.subType() ) );
112  def.insert( QStringLiteral( "expression" ), field.expression );
113  results.append( def );
114  }
115  return results;
116 }
117 
118 void QgsProcessingFieldMapPanelWidget::setValue( const QVariant &value )
119 {
120  if ( value.type() != QVariant::List )
121  return;
122 
123  QgsFields destinationFields;
124  QMap<QString, QString> expressions;
125 
126  const QgsFields layerFields = mLayer ? mLayer->fields() : QgsFields();
127  const QVariantList fields = value.toList();
128  for ( const QVariant &field : fields )
129  {
130  const QVariantMap map = field.toMap();
131  QgsField f( map.value( QStringLiteral( "name" ) ).toString(),
132  static_cast< QVariant::Type >( map.value( QStringLiteral( "type" ), QVariant::Invalid ).toInt() ),
133  map.value( QStringLiteral( "type_name" ), QVariant::typeToName( static_cast< QVariant::Type >( map.value( QStringLiteral( "type" ), QVariant::Invalid ).toInt() ) ) ).toString(),
134  map.value( QStringLiteral( "length" ), 0 ).toInt(),
135  map.value( QStringLiteral( "precision" ), 0 ).toInt(),
136  QString(),
137  static_cast< QVariant::Type >( map.value( QStringLiteral( "sub_type" ), QVariant::Invalid ).toInt() ) );
138 
139  if ( map.contains( QStringLiteral( "constraints" ) ) )
140  {
141  const QgsFieldConstraints::Constraints constraints = static_cast<QgsFieldConstraints::Constraints>( map.value( QStringLiteral( "constraints" ), 0 ).toInt() );
142  QgsFieldConstraints fieldConstraints;
143 
144  if ( constraints & QgsFieldConstraints::ConstraintNotNull )
146  if ( constraints & QgsFieldConstraints::ConstraintUnique )
148  if ( constraints & QgsFieldConstraints::ConstraintExpression )
150 
151  f.setConstraints( fieldConstraints );
152  }
153 
154  if ( !map.value( QStringLiteral( "expression" ) ).toString().isEmpty() )
155  {
156  expressions.insert( f.name(), map.value( QStringLiteral( "expression" ) ).toString() );
157  }
158 
159  destinationFields.append( f );
160  }
161 
162  mBlockChangedSignal = true;
163  if ( destinationFields.size() > 0 )
164  mFieldsView->setDestinationFields( destinationFields, expressions );
165  mBlockChangedSignal = false;
166 
167  emit changed();
168 }
169 
170 void QgsProcessingFieldMapPanelWidget::registerExpressionContextGenerator( const QgsExpressionContextGenerator *generator )
171 {
172  mFieldsView->registerExpressionContextGenerator( generator );
173 }
174 
175 void QgsProcessingFieldMapPanelWidget::loadFieldsFromLayer()
176 {
177  if ( mLayer )
178  {
179  mFieldsView->setSourceFields( mLayer->fields() );
180  mFieldsView->setDestinationFields( mLayer->fields() );
181  }
182 }
183 
184 void QgsProcessingFieldMapPanelWidget::addField()
185 {
186  const int rowCount = mModel->rowCount();
187  mModel->appendField( QgsField( QStringLiteral( "new_field" ) ) );
188  const QModelIndex index = mModel->index( rowCount, 0 );
189  mFieldsView->selectionModel()->select(
190  index,
191  QItemSelectionModel::SelectionFlags(
192  QItemSelectionModel::Clear |
193  QItemSelectionModel::Select |
194  QItemSelectionModel::Current |
195  QItemSelectionModel::Rows ) );
196  mFieldsView->scrollTo( index );
197 }
198 
199 void QgsProcessingFieldMapPanelWidget::loadLayerFields()
200 {
201  if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( mLayerCombo->currentLayer() ) )
202  {
203  mFieldsView->setSourceFields( vl->fields() );
204  mFieldsView->setDestinationFields( vl->fields() );
205  }
206 }
207 
208 //
209 // QgsProcessingFieldMapParameterDefinitionWidget
210 //
211 
212 QgsProcessingFieldMapParameterDefinitionWidget::QgsProcessingFieldMapParameterDefinitionWidget( QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent )
213  : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
214 {
215  QVBoxLayout *vlayout = new QVBoxLayout();
216  vlayout->setContentsMargins( 0, 0, 0, 0 );
217 
218  vlayout->addWidget( new QLabel( tr( "Parent layer" ) ) );
219 
220  mParentLayerComboBox = new QComboBox();
221  mParentLayerComboBox->addItem( tr( "None" ), QVariant() );
222 
223  QString initialParent;
224  if ( const QgsProcessingParameterFieldMapping *mapParam = dynamic_cast<const QgsProcessingParameterFieldMapping *>( definition ) )
225  initialParent = mapParam->parentLayerParameterName();
226 
227  if ( auto *lModel = widgetContext.model() )
228  {
229  // populate combo box with other model input choices
230  const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
231  for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
232  {
233  if ( const QgsProcessingParameterFeatureSource *definition = dynamic_cast< const QgsProcessingParameterFeatureSource * >( lModel->parameterDefinition( it.value().parameterName() ) ) )
234  {
235  mParentLayerComboBox-> addItem( definition->description(), definition->name() );
236  if ( !initialParent.isEmpty() && initialParent == definition->name() )
237  {
238  mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
239  }
240  }
241  else if ( const QgsProcessingParameterVectorLayer *definition = dynamic_cast< const QgsProcessingParameterVectorLayer * >( lModel->parameterDefinition( it.value().parameterName() ) ) )
242  {
243  mParentLayerComboBox-> addItem( definition->description(), definition->name() );
244  if ( !initialParent.isEmpty() && initialParent == definition->name() )
245  {
246  mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
247  }
248  }
249  }
250  }
251 
252  if ( mParentLayerComboBox->count() == 1 && !initialParent.isEmpty() )
253  {
254  // if no parent candidates found, we just add the existing one as a placeholder
255  mParentLayerComboBox->addItem( initialParent, initialParent );
256  mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
257  }
258 
259  vlayout->addWidget( mParentLayerComboBox );
260  setLayout( vlayout );
261 }
262 
263 QgsProcessingParameterDefinition *QgsProcessingFieldMapParameterDefinitionWidget::createParameter( const QString &name, const QString &description, QgsProcessingParameterDefinition::Flags flags ) const
264 {
265  auto param = std::make_unique< QgsProcessingParameterFieldMapping >( name, description, mParentLayerComboBox->currentData().toString() );
266  param->setFlags( flags );
267  return param.release();
268 }
269 
270 //
271 // QgsProcessingFieldMapWidgetWrapper
272 //
273 
274 QgsProcessingFieldMapWidgetWrapper::QgsProcessingFieldMapWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type, QWidget *parent )
275  : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
276 {
277 }
278 
279 QString QgsProcessingFieldMapWidgetWrapper::parameterType() const
280 {
282 }
283 
284 QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingFieldMapWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type )
285 {
286  return new QgsProcessingFieldMapWidgetWrapper( parameter, type );
287 }
288 
289 QWidget *QgsProcessingFieldMapWidgetWrapper::createWidget()
290 {
291  mPanel = new QgsProcessingFieldMapPanelWidget( nullptr );
292  mPanel->setToolTip( parameterDefinition()->toolTip() );
293  mPanel->registerExpressionContextGenerator( this );
294 
295  connect( mPanel, &QgsProcessingFieldMapPanelWidget::changed, this, [ = ]
296  {
297  emit widgetValueHasChanged( this );
298  } );
299 
300  return mPanel;
301 }
302 
303 QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingFieldMapWidgetWrapper::createParameterDefinitionWidget( QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm )
304 {
305  return new QgsProcessingFieldMapParameterDefinitionWidget( context, widgetContext, definition, algorithm );
306 }
307 
308 void QgsProcessingFieldMapWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
309 {
311  switch ( type() )
312  {
315  {
316  for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
317  {
318  if ( wrapper->parameterDefinition()->name() == static_cast< const QgsProcessingParameterFieldMapping * >( parameterDefinition() )->parentLayerParameterName() )
319  {
320  setParentLayerWrapperValue( wrapper );
322  {
323  setParentLayerWrapperValue( wrapper );
324  } );
325  break;
326  }
327  }
328  break;
329  }
330 
332  break;
333  }
334 }
335 
336 int QgsProcessingFieldMapWidgetWrapper::stretch() const
337 {
338  return 1;
339 }
340 
341 void 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() == QgsMapLayerType::VectorLayer )
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 
380 void QgsProcessingFieldMapWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext & )
381 {
382  if ( mPanel )
383  mPanel->setValue( value );
384 }
385 
386 QVariant QgsProcessingFieldMapWidgetWrapper::widgetValue() const
387 {
388  return mPanel ? mPanel->value() : QVariant();
389 }
390 
391 QStringList QgsProcessingFieldMapWidgetWrapper::compatibleParameterTypes() const
392 {
393  return QStringList()
395 }
396 
397 QStringList QgsProcessingFieldMapWidgetWrapper::compatibleOutputTypes() const
398 {
399  return QStringList();
400 }
401 
402 QString QgsProcessingFieldMapWidgetWrapper::modelerExpressionFormatString() const
403 {
404  return tr( "an array of map items, each containing a 'name', 'type' and 'expression' values (and optional 'length' and 'precision' values)." );
405 }
406 
407 const QgsVectorLayer *QgsProcessingFieldMapWidgetWrapper::linkedVectorLayer() const
408 {
409  if ( mPanel && mPanel->layer() )
410  return mPanel->layer();
411 
413 }
414 
416 
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.
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 changed()
Emitted when the fields defined in the widget are changed.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
QString typeName() const
Gets the field type.
Definition: qgsfield.cpp:139
QString name
Definition: qgsfield.h:60
int precision
Definition: qgsfield.h:57
int length
Definition: qgsfield.h:56
QVariant::Type type
Definition: qgsfield.h:58
QVariant::Type subType() const
If the field is a collection, gets its element's type.
Definition: qgsfield.cpp:134
Container of fields for a vector layer.
Definition: qgsfields.h:45
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false)
Definition: qgsfields.cpp:59
int size() const
Returns number of items.
Definition: qgsfields.cpp:138
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.
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.
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 Field struct holds information about a mapped field.