QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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 
25 #include "qgspanelwidget.h"
26 
27 #include "qgsprocessingcontext.h"
28 #include "qgsprocessingmodelalgorithm.h"
29 
31 
33 
34 //
35 // QgsProcessingFieldMapPanelWidget
36 //
37 
38 
39 QgsProcessingFieldMapPanelWidget::QgsProcessingFieldMapPanelWidget( QWidget *parent )
40  : QgsPanelWidget( parent )
41 {
42  setupUi( this );
43 
44  mModel = mFieldsView->model();
45  mFieldsView->setDestinationEditable( true );
46 
47  mLayerCombo->setAllowEmptyLayer( true );
48  mLayerCombo->setFilters( QgsMapLayerProxyModel::VectorLayer );
49 
50  connect( mResetButton, &QPushButton::clicked, this, &QgsProcessingFieldMapPanelWidget::loadFieldsFromLayer );
51  connect( mAddButton, &QPushButton::clicked, this, &QgsProcessingFieldMapPanelWidget::addField );
52  connect( mDeleteButton, &QPushButton::clicked, mFieldsView, &QgsFieldMappingWidget::removeSelectedFields );
53  connect( mUpButton, &QPushButton::clicked, mFieldsView, &QgsFieldMappingWidget::moveSelectedFieldsUp );
54  connect( mDownButton, &QPushButton::clicked, mFieldsView, &QgsFieldMappingWidget::moveSelectedFieldsDown );
55  connect( mLoadLayerFieldsButton, &QPushButton::clicked, this, &QgsProcessingFieldMapPanelWidget::loadLayerFields );
56 
57  connect( mFieldsView, &QgsFieldMappingWidget::changed, this, [ = ]
58  {
59  if ( !mBlockChangedSignal )
60  {
61  emit changed();
62  }
63  } );
64 }
65 
66 void QgsProcessingFieldMapPanelWidget::setLayer( QgsVectorLayer *layer )
67 {
68  if ( layer == mLayer )
69  return;
70 
71  mLayer = layer;
72  if ( mModel->rowCount() == 0 )
73  {
74  loadFieldsFromLayer();
75  return;
76  }
77 
78  QMessageBox dlg( this );
79  dlg.setText( tr( "Do you want to reset the field mapping?" ) );
80  dlg.setStandardButtons(
81  QMessageBox::StandardButtons( QMessageBox::Yes |
82  QMessageBox::No ) );
83  dlg.setDefaultButton( QMessageBox::No );
84  if ( dlg.exec() == QMessageBox::Yes )
85  {
86  loadFieldsFromLayer();
87  }
88 }
89 
90 QgsVectorLayer *QgsProcessingFieldMapPanelWidget::layer()
91 {
92  return mLayer;
93 }
94 
95 QVariant QgsProcessingFieldMapPanelWidget::value() const
96 {
97  const QList<QgsFieldMappingModel::Field> mapping = mFieldsView->mapping();
98 
99  QVariantList results;
100  results.reserve( mapping.size() );
101  for ( const QgsFieldMappingModel::Field &field : mapping )
102  {
103  QVariantMap def;
104  def.insert( QStringLiteral( "name" ), field.field.name() );
105  def.insert( QStringLiteral( "type" ), static_cast< int >( field.field.type() ) );
106  def.insert( QStringLiteral( "length" ), field.field.length() );
107  def.insert( QStringLiteral( "precision" ), field.field.precision() );
108  def.insert( QStringLiteral( "expression" ), field.expression );
109  results.append( def );
110  }
111  return results;
112 }
113 
114 void QgsProcessingFieldMapPanelWidget::setValue( const QVariant &value )
115 {
116  if ( value.type() != QVariant::List )
117  return;
118 
119  QgsFields destinationFields;
120  QMap<QString, QString> expressions;
121 
122  const QVariantList fields = value.toList();
123  for ( const QVariant &field : fields )
124  {
125  const QVariantMap map = field.toMap();
126  QgsField f( map.value( QStringLiteral( "name" ) ).toString(),
127  static_cast< QVariant::Type >( map.value( QStringLiteral( "type" ), QVariant::Invalid ).toInt() ),
128  QVariant::typeToName( static_cast< QVariant::Type >( map.value( QStringLiteral( "type" ), QVariant::Invalid ).toInt() ) ),
129  map.value( QStringLiteral( "length" ), 0 ).toInt(),
130  map.value( QStringLiteral( "precision" ), 0 ).toInt() );
131 
132  if ( !map.value( QStringLiteral( "expression" ) ).toString().isEmpty() )
133  {
134  expressions.insert( f.name(), map.value( QStringLiteral( "expression" ) ).toString() );
135  }
136 
137  destinationFields.append( f );
138  }
139 
140  mBlockChangedSignal = true;
141  if ( destinationFields.size() > 0 )
142  mFieldsView->setDestinationFields( destinationFields, expressions );
143  mBlockChangedSignal = false;
144 
145  emit changed();
146 }
147 
148 void QgsProcessingFieldMapPanelWidget::registerExpressionContextGenerator( const QgsExpressionContextGenerator *generator )
149 {
150  mFieldsView->registerExpressionContextGenerator( generator );
151 }
152 
153 void QgsProcessingFieldMapPanelWidget::loadFieldsFromLayer()
154 {
155  if ( mLayer )
156  {
157  mFieldsView->setSourceFields( mLayer->fields() );
158  mFieldsView->setDestinationFields( mLayer->fields() );
159  }
160 }
161 
162 void QgsProcessingFieldMapPanelWidget::addField()
163 {
164  const int rowCount = mModel->rowCount();
165  mModel->appendField( QgsField( QStringLiteral( "new_field" ) ) );
166  QModelIndex index = mModel->index( rowCount, 0 );
167  mFieldsView->selectionModel()->select(
168  index,
169  QItemSelectionModel::SelectionFlags(
170  QItemSelectionModel::Clear |
171  QItemSelectionModel::Select |
172  QItemSelectionModel::Current |
173  QItemSelectionModel::Rows ) );
174  mFieldsView->scrollTo( index );
175 }
176 
177 void QgsProcessingFieldMapPanelWidget::loadLayerFields()
178 {
179  if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( mLayerCombo->currentLayer() ) )
180  {
181  mFieldsView->setSourceFields( vl->fields() );
182  mFieldsView->setDestinationFields( vl->fields() );
183  }
184 }
185 
186 //
187 // QgsProcessingFieldMapParameterDefinitionWidget
188 //
189 
190 QgsProcessingFieldMapParameterDefinitionWidget::QgsProcessingFieldMapParameterDefinitionWidget( QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent )
191  : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
192 {
193  QVBoxLayout *vlayout = new QVBoxLayout();
194  vlayout->setMargin( 0 );
195  vlayout->setContentsMargins( 0, 0, 0, 0 );
196 
197  vlayout->addWidget( new QLabel( tr( "Parent layer" ) ) );
198 
199  mParentLayerComboBox = new QComboBox();
200  mParentLayerComboBox->addItem( tr( "None" ), QVariant() );
201 
202  QString initialParent;
203  if ( const QgsProcessingParameterFieldMapping *mapParam = dynamic_cast<const QgsProcessingParameterFieldMapping *>( definition ) )
204  initialParent = mapParam->parentLayerParameterName();
205 
206  if ( widgetContext.model() )
207  {
208  // populate combo box with other model input choices
209  const QMap<QString, QgsProcessingModelParameter> components = widgetContext.model()->parameterComponents();
210  for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
211  {
212  if ( const QgsProcessingParameterFeatureSource *definition = dynamic_cast< const QgsProcessingParameterFeatureSource * >( widgetContext.model()->parameterDefinition( it.value().parameterName() ) ) )
213  {
214  mParentLayerComboBox-> addItem( definition->description(), definition->name() );
215  if ( !initialParent.isEmpty() && initialParent == definition->name() )
216  {
217  mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
218  }
219  }
220  else if ( const QgsProcessingParameterVectorLayer *definition = dynamic_cast< const QgsProcessingParameterVectorLayer * >( widgetContext.model()->parameterDefinition( it.value().parameterName() ) ) )
221  {
222  mParentLayerComboBox-> addItem( definition->description(), definition->name() );
223  if ( !initialParent.isEmpty() && initialParent == definition->name() )
224  {
225  mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
226  }
227  }
228  }
229  }
230 
231  if ( mParentLayerComboBox->count() == 1 && !initialParent.isEmpty() )
232  {
233  // if no parent candidates found, we just add the existing one as a placeholder
234  mParentLayerComboBox->addItem( initialParent, initialParent );
235  mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
236  }
237 
238  vlayout->addWidget( mParentLayerComboBox );
239  setLayout( vlayout );
240 }
241 
242 QgsProcessingParameterDefinition *QgsProcessingFieldMapParameterDefinitionWidget::createParameter( const QString &name, const QString &description, QgsProcessingParameterDefinition::Flags flags ) const
243 {
244  auto param = qgis::make_unique< QgsProcessingParameterFieldMapping >( name, description, mParentLayerComboBox->currentData().toString() );
245  param->setFlags( flags );
246  return param.release();
247 }
248 
249 //
250 // QgsProcessingFieldMapWidgetWrapper
251 //
252 
253 QgsProcessingFieldMapWidgetWrapper::QgsProcessingFieldMapWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type, QWidget *parent )
254  : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
255 {
256 }
257 
258 QString QgsProcessingFieldMapWidgetWrapper::parameterType() const
259 {
261 }
262 
263 QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingFieldMapWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type )
264 {
265  return new QgsProcessingFieldMapWidgetWrapper( parameter, type );
266 }
267 
268 QWidget *QgsProcessingFieldMapWidgetWrapper::createWidget()
269 {
270  mPanel = new QgsProcessingFieldMapPanelWidget( nullptr );
271  mPanel->setToolTip( parameterDefinition()->toolTip() );
272  mPanel->registerExpressionContextGenerator( this );
273 
274  connect( mPanel, &QgsProcessingFieldMapPanelWidget::changed, this, [ = ]
275  {
276  emit widgetValueHasChanged( this );
277  } );
278 
279  return mPanel;
280 }
281 
282 QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingFieldMapWidgetWrapper::createParameterDefinitionWidget( QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm )
283 {
284  return new QgsProcessingFieldMapParameterDefinitionWidget( context, widgetContext, definition, algorithm );
285 }
286 
287 void QgsProcessingFieldMapWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
288 {
290  switch ( type() )
291  {
294  {
295  for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
296  {
297  if ( wrapper->parameterDefinition()->name() == static_cast< const QgsProcessingParameterFieldMapping * >( parameterDefinition() )->parentLayerParameterName() )
298  {
299  setParentLayerWrapperValue( wrapper );
301  {
302  setParentLayerWrapperValue( wrapper );
303  } );
304  break;
305  }
306  }
307  break;
308  }
309 
311  break;
312  }
313 }
314 
315 int QgsProcessingFieldMapWidgetWrapper::stretch() const
316 {
317  return 1;
318 }
319 
320 void QgsProcessingFieldMapWidgetWrapper::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 = qgis::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() == QgsMapLayerType::VectorLayer )
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 
359 void QgsProcessingFieldMapWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext & )
360 {
361  if ( mPanel )
362  mPanel->setValue( value );
363 }
364 
365 QVariant QgsProcessingFieldMapWidgetWrapper::widgetValue() const
366 {
367  return mPanel ? mPanel->value() : QVariant();
368 }
369 
370 QStringList QgsProcessingFieldMapWidgetWrapper::compatibleParameterTypes() const
371 {
372  return QStringList()
374 }
375 
376 QStringList QgsProcessingFieldMapWidgetWrapper::compatibleOutputTypes() const
377 {
378  return QStringList();
379 }
380 
381 QString QgsProcessingFieldMapWidgetWrapper::modelerExpressionFormatString() const
382 {
383  return tr( "an array of map items, each containing a 'name', 'type' and 'expression' values (and optional 'length' and 'precision' values)." );
384 }
385 
386 const QgsVectorLayer *QgsProcessingFieldMapWidgetWrapper::linkedVectorLayer() const
387 {
388  if ( mPanel && mPanel->layer() )
389  return mPanel->layer();
390 
392 }
393 
395 
396 
QgsProcessingParameterWidgetContext
Definition: qgsprocessingwidgetwrapper.h:99
qgsprocessingparameterfieldmap.h
QgsMapLayerType::VectorLayer
@ VectorLayer
QgsProcessingParameterDefinition::description
QString description() const
Returns the description for the parameter.
Definition: qgsprocessingparameters.h:470
algorithm
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
QgsProcessingContext::takeResultLayer
QgsMapLayer * takeResultLayer(const QString &id)
Takes the result map layer with matching id from the context and transfers ownership of it back to th...
Definition: qgsprocessingcontext.cpp:108
QgsFields
Definition: qgsfields.h:44
QgsProcessingParameterDefinition
Definition: qgsprocessingparameters.h:330
QgsProcessingParameterFeatureSource
Definition: qgsprocessingparameters.h:2612
QgsFields::append
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
QgsAbstractProcessingParameterWidgetWrapper
Definition: qgsprocessingwidgetwrapper.h:266
QgsProcessingGui::Standard
@ Standard
Standard algorithm dialog.
Definition: qgsprocessinggui.h:40
QgsFields::toList
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
Definition: qgsfields.cpp:212
QgsMapLayerProxyModel::VectorLayer
@ VectorLayer
Definition: qgsmaplayerproxymodel.h:50
QgsPanelWidget
Base class for any widget that can be shown as a inline panel.
Definition: qgspanelwidget.h:29
QgsProcessingParameters::parameterAsVectorLayer
static QgsVectorLayer * parameterAsVectorLayer(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Evaluates the parameter with matching definition to a vector layer.
Definition: qgsprocessingparameters.cpp:945
QgsProcessingContext
Definition: qgsprocessingcontext.h:43
QgsProcessingParameterDefinition::name
QString name() const
Returns the name of the parameter.
Definition: qgsprocessingparameters.h:456
QgsProcessingGui::Batch
@ Batch
Batch processing dialog.
Definition: qgsprocessinggui.h:41
QgsFields::size
int size() const
Returns number of items.
Definition: qgsfields.cpp:138
QgsMapLayer::id
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
Definition: qgsmaplayer.cpp:148
QgsProcessingGui::Modeler
@ Modeler
Modeler dialog.
Definition: qgsprocessinggui.h:42
QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged
void widgetValueHasChanged(QgsAbstractProcessingParameterWidgetWrapper *wrapper)
Emitted whenever the parameter value (as defined by the wrapped widget) is changed.
QgsAbstractProcessingParameterWidgetWrapper::linkedVectorLayer
virtual const QgsVectorLayer * linkedVectorLayer() const
Returns the optional vector layer associated with this widget wrapper, or nullptr if no vector layer ...
Definition: qgsprocessingwidgetwrapper.cpp:246
QgsProcessingParameterVectorLayer
Definition: qgsprocessingparameters.h:2382
QgsProcessingAbstractParameterDefinitionWidget
Abstract base class for widgets which allow users to specify the properties of a Processing parameter...
Definition: qgsprocessingparameterdefinitionwidget.h:43
QgsProcessingParameterFieldMapping
Definition: qgsprocessingparameterfieldmap.h:31
QgsFieldMappingModel::Field
The Field struct holds information about a mapped field.
Definition: qgsfieldmappingmodel.h:78
QgsProcessingGui::WidgetType
WidgetType
Types of dialogs which Processing widgets can be created for.
Definition: qgsprocessinggui.h:38
QgsProcessingAlgorithm
Definition: qgsprocessingalgorithm.h:51
QgsProcessingParameterFieldMapping::typeName
static QString typeName()
Returns the type name for the parameter class.
Definition: qgsprocessingparameterfieldmap.h:60
QgsVectorLayer
Definition: qgsvectorlayer.h:385
QgsFieldMappingWidget::removeSelectedFields
bool removeSelectedFields()
Removes the currently selected field from the model.
Definition: qgsfieldmappingwidget.cpp:121
QgsAbstractProcessingParameterWidgetWrapper::parameterValue
QVariant parameterValue() const
Returns the current value of the parameter.
Definition: qgsprocessingwidgetwrapper.cpp:202
qgsprocessingcontext.h
QgsProcessingParameterWidgetContext::model
QgsProcessingModelAlgorithm * model() const
Returns the model which the parameter widget is associated with.
Definition: qgsprocessingwidgetwrapper.cpp:94
QgsFieldMappingWidget::moveSelectedFieldsDown
bool moveSelectedFieldsDown()
Moves down the currently selected field.
Definition: qgsfieldmappingwidget.cpp:154
qgsprocessingfieldmapwidgetwrapper.h
qgspanelwidget.h
QgsFieldMappingWidget::changed
void changed()
Emitted when the fields defined in the widget are changed.
QgsAbstractProcessingParameterWidgetWrapper::parameterDefinition
const QgsProcessingParameterDefinition * parameterDefinition() const
Returns the parameter definition associated with this wrapper.
Definition: qgsprocessingwidgetwrapper.cpp:182
QgsFieldMappingWidget::moveSelectedFieldsUp
bool moveSelectedFieldsUp()
Moves up currently selected field.
Definition: qgsfieldmappingwidget.cpp:138
QgsExpressionContextGenerator
Definition: qgsexpressioncontextgenerator.h:36
QgsAbstractProcessingParameterWidgetWrapper::postInitialize
virtual void postInitialize(const QList< QgsAbstractProcessingParameterWidgetWrapper * > &wrappers)
Called after all wrappers have been created within a particular dialog or context,...
Definition: qgsprocessingwidgetwrapper.cpp:253
QgsField
Definition: qgsfield.h:49