QGIS API Documentation  3.23.0-Master (22c16f2067)
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"
29 #include "qgsprocessingmodelalgorithm.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( "length" ), field.field.length() );
109  def.insert( QStringLiteral( "precision" ), field.field.precision() );
110  def.insert( QStringLiteral( "expression" ), field.expression );
111  results.append( def );
112  }
113  return results;
114 }
115 
116 void QgsProcessingFieldMapPanelWidget::setValue( const QVariant &value )
117 {
118  if ( value.type() != QVariant::List )
119  return;
120 
121  QgsFields destinationFields;
122  QMap<QString, QString> expressions;
123 
124  const QgsFields layerFields = mLayer ? mLayer->fields() : QgsFields();
125  const QVariantList fields = value.toList();
126  for ( const QVariant &field : fields )
127  {
128  const QVariantMap map = field.toMap();
129  QgsField f( map.value( QStringLiteral( "name" ) ).toString(),
130  static_cast< QVariant::Type >( map.value( QStringLiteral( "type" ), QVariant::Invalid ).toInt() ),
131  QVariant::typeToName( static_cast< QVariant::Type >( map.value( QStringLiteral( "type" ), QVariant::Invalid ).toInt() ) ),
132  map.value( QStringLiteral( "length" ), 0 ).toInt(),
133  map.value( QStringLiteral( "precision" ), 0 ).toInt() );
134 
135  if ( map.contains( QStringLiteral( "constraints" ) ) )
136  {
137  const QgsFieldConstraints::Constraints constraints = static_cast<QgsFieldConstraints::Constraints>( map.value( QStringLiteral( "constraints" ), 0 ).toInt() );
138  QgsFieldConstraints fieldConstraints;
139 
140  if ( constraints & QgsFieldConstraints::ConstraintNotNull )
142  if ( constraints & QgsFieldConstraints::ConstraintUnique )
144  if ( constraints & QgsFieldConstraints::ConstraintExpression )
146 
147  f.setConstraints( fieldConstraints );
148  }
149 
150  if ( !map.value( QStringLiteral( "expression" ) ).toString().isEmpty() )
151  {
152  expressions.insert( f.name(), map.value( QStringLiteral( "expression" ) ).toString() );
153  }
154 
155  destinationFields.append( f );
156  }
157 
158  mBlockChangedSignal = true;
159  if ( destinationFields.size() > 0 )
160  mFieldsView->setDestinationFields( destinationFields, expressions );
161  mBlockChangedSignal = false;
162 
163  emit changed();
164 }
165 
166 void QgsProcessingFieldMapPanelWidget::registerExpressionContextGenerator( const QgsExpressionContextGenerator *generator )
167 {
168  mFieldsView->registerExpressionContextGenerator( generator );
169 }
170 
171 void QgsProcessingFieldMapPanelWidget::loadFieldsFromLayer()
172 {
173  if ( mLayer )
174  {
175  mFieldsView->setSourceFields( mLayer->fields() );
176  mFieldsView->setDestinationFields( mLayer->fields() );
177  }
178 }
179 
180 void QgsProcessingFieldMapPanelWidget::addField()
181 {
182  const int rowCount = mModel->rowCount();
183  mModel->appendField( QgsField( QStringLiteral( "new_field" ) ) );
184  const QModelIndex index = mModel->index( rowCount, 0 );
185  mFieldsView->selectionModel()->select(
186  index,
187  QItemSelectionModel::SelectionFlags(
188  QItemSelectionModel::Clear |
189  QItemSelectionModel::Select |
190  QItemSelectionModel::Current |
191  QItemSelectionModel::Rows ) );
192  mFieldsView->scrollTo( index );
193 }
194 
195 void QgsProcessingFieldMapPanelWidget::loadLayerFields()
196 {
197  if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( mLayerCombo->currentLayer() ) )
198  {
199  mFieldsView->setSourceFields( vl->fields() );
200  mFieldsView->setDestinationFields( vl->fields() );
201  }
202 }
203 
204 //
205 // QgsProcessingFieldMapParameterDefinitionWidget
206 //
207 
208 QgsProcessingFieldMapParameterDefinitionWidget::QgsProcessingFieldMapParameterDefinitionWidget( QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent )
209  : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
210 {
211  QVBoxLayout *vlayout = new QVBoxLayout();
212  vlayout->setContentsMargins( 0, 0, 0, 0 );
213 
214  vlayout->addWidget( new QLabel( tr( "Parent layer" ) ) );
215 
216  mParentLayerComboBox = new QComboBox();
217  mParentLayerComboBox->addItem( tr( "None" ), QVariant() );
218 
219  QString initialParent;
220  if ( const QgsProcessingParameterFieldMapping *mapParam = dynamic_cast<const QgsProcessingParameterFieldMapping *>( definition ) )
221  initialParent = mapParam->parentLayerParameterName();
222 
223  if ( auto *lModel = widgetContext.model() )
224  {
225  // populate combo box with other model input choices
226  const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
227  for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
228  {
229  if ( const QgsProcessingParameterFeatureSource *definition = dynamic_cast< const QgsProcessingParameterFeatureSource * >( lModel->parameterDefinition( it.value().parameterName() ) ) )
230  {
231  mParentLayerComboBox-> addItem( definition->description(), definition->name() );
232  if ( !initialParent.isEmpty() && initialParent == definition->name() )
233  {
234  mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
235  }
236  }
237  else if ( const QgsProcessingParameterVectorLayer *definition = dynamic_cast< const QgsProcessingParameterVectorLayer * >( lModel->parameterDefinition( it.value().parameterName() ) ) )
238  {
239  mParentLayerComboBox-> addItem( definition->description(), definition->name() );
240  if ( !initialParent.isEmpty() && initialParent == definition->name() )
241  {
242  mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
243  }
244  }
245  }
246  }
247 
248  if ( mParentLayerComboBox->count() == 1 && !initialParent.isEmpty() )
249  {
250  // if no parent candidates found, we just add the existing one as a placeholder
251  mParentLayerComboBox->addItem( initialParent, initialParent );
252  mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
253  }
254 
255  vlayout->addWidget( mParentLayerComboBox );
256  setLayout( vlayout );
257 }
258 
259 QgsProcessingParameterDefinition *QgsProcessingFieldMapParameterDefinitionWidget::createParameter( const QString &name, const QString &description, QgsProcessingParameterDefinition::Flags flags ) const
260 {
261  auto param = std::make_unique< QgsProcessingParameterFieldMapping >( name, description, mParentLayerComboBox->currentData().toString() );
262  param->setFlags( flags );
263  return param.release();
264 }
265 
266 //
267 // QgsProcessingFieldMapWidgetWrapper
268 //
269 
270 QgsProcessingFieldMapWidgetWrapper::QgsProcessingFieldMapWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type, QWidget *parent )
271  : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
272 {
273 }
274 
275 QString QgsProcessingFieldMapWidgetWrapper::parameterType() const
276 {
278 }
279 
280 QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingFieldMapWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type )
281 {
282  return new QgsProcessingFieldMapWidgetWrapper( parameter, type );
283 }
284 
285 QWidget *QgsProcessingFieldMapWidgetWrapper::createWidget()
286 {
287  mPanel = new QgsProcessingFieldMapPanelWidget( nullptr );
288  mPanel->setToolTip( parameterDefinition()->toolTip() );
289  mPanel->registerExpressionContextGenerator( this );
290 
291  connect( mPanel, &QgsProcessingFieldMapPanelWidget::changed, this, [ = ]
292  {
293  emit widgetValueHasChanged( this );
294  } );
295 
296  return mPanel;
297 }
298 
299 QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingFieldMapWidgetWrapper::createParameterDefinitionWidget( QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm )
300 {
301  return new QgsProcessingFieldMapParameterDefinitionWidget( context, widgetContext, definition, algorithm );
302 }
303 
304 void QgsProcessingFieldMapWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
305 {
307  switch ( type() )
308  {
311  {
312  for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
313  {
314  if ( wrapper->parameterDefinition()->name() == static_cast< const QgsProcessingParameterFieldMapping * >( parameterDefinition() )->parentLayerParameterName() )
315  {
316  setParentLayerWrapperValue( wrapper );
318  {
319  setParentLayerWrapperValue( wrapper );
320  } );
321  break;
322  }
323  }
324  break;
325  }
326 
328  break;
329  }
330 }
331 
332 int QgsProcessingFieldMapWidgetWrapper::stretch() const
333 {
334  return 1;
335 }
336 
337 void QgsProcessingFieldMapWidgetWrapper::setParentLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
338 {
339  // evaluate value to layer
340  QgsProcessingContext *context = nullptr;
341  std::unique_ptr< QgsProcessingContext > tmpContext;
342  if ( mProcessingContextGenerator )
343  context = mProcessingContextGenerator->processingContext();
344 
345  if ( !context )
346  {
347  tmpContext = std::make_unique< QgsProcessingContext >();
348  context = tmpContext.get();
349  }
350 
351  QgsVectorLayer *layer = QgsProcessingParameters::parameterAsVectorLayer( parentWrapper->parameterDefinition(), parentWrapper->parameterValue(), *context );
352  if ( !layer )
353  {
354  if ( mPanel )
355  mPanel->setLayer( nullptr );
356  return;
357  }
358 
359  // need to grab ownership of layer if required - otherwise layer may be deleted when context
360  // goes out of scope
361  std::unique_ptr< QgsMapLayer > ownedLayer( context->takeResultLayer( layer->id() ) );
362  if ( ownedLayer && ownedLayer->type() == QgsMapLayerType::VectorLayer )
363  {
364  mParentLayer.reset( qobject_cast< QgsVectorLayer * >( ownedLayer.release() ) );
365  layer = mParentLayer.get();
366  }
367  else
368  {
369  // don't need ownership of this layer - it wasn't owned by context (so e.g. is owned by the project)
370  }
371 
372  if ( mPanel )
373  mPanel->setLayer( layer );
374 }
375 
376 void QgsProcessingFieldMapWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext & )
377 {
378  if ( mPanel )
379  mPanel->setValue( value );
380 }
381 
382 QVariant QgsProcessingFieldMapWidgetWrapper::widgetValue() const
383 {
384  return mPanel ? mPanel->value() : QVariant();
385 }
386 
387 QStringList QgsProcessingFieldMapWidgetWrapper::compatibleParameterTypes() const
388 {
389  return QStringList()
391 }
392 
393 QStringList QgsProcessingFieldMapWidgetWrapper::compatibleOutputTypes() const
394 {
395  return QStringList();
396 }
397 
398 QString QgsProcessingFieldMapWidgetWrapper::modelerExpressionFormatString() const
399 {
400  return tr( "an array of map items, each containing a 'name', 'type' and 'expression' values (and optional 'length' and 'precision' values)." );
401 }
402 
403 const QgsVectorLayer *QgsProcessingFieldMapWidgetWrapper::linkedVectorLayer() const
404 {
405  if ( mPanel && mPanel->layer() )
406  return mPanel->layer();
407 
409 }
410 
412 
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 name
Definition: qgsfield.h:60
int precision
Definition: qgsfield.h:57
int length
Definition: qgsfield.h:56
QVariant::Type type
Definition: qgsfield.h:58
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.