QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgsfieldmappingwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsfieldmappingwidget.cpp - QgsFieldMappingWidget
3 
4  ---------------------
5  begin : 16.3.2020
6  copyright : (C) 2020 by Alessandro Pasotti
7  email : elpaso at itopen dot it
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 
17 #include "qgsfieldmappingwidget.h"
19 #include "qgsexpression.h"
21 #include "qgsvectorlayer.h"
22 
23 #include <QTableView>
24 #include <QVBoxLayout>
25 
26 #ifdef ENABLE_MODELTEST
27 #include "modeltest.h"
28 #endif
29 
31  const QgsFields &sourceFields,
32  const QgsFields &destinationFields,
33  const QMap<QString, QString> &expressions )
34  : QgsPanelWidget( parent )
35 {
36  QVBoxLayout *verticalLayout = new QVBoxLayout();
37  verticalLayout->setContentsMargins( 0, 0, 0, 0 );
38  mTableView = new QTableView();
39  verticalLayout->addWidget( mTableView );
40  setLayout( verticalLayout );
41 
42  mModel = new QgsFieldMappingModel( sourceFields, destinationFields, expressions, this );
43 
44 #ifdef ENABLE_MODELTEST
45  new ModelTest( mModel, this );
46 #endif
47 
48  mTableView->setModel( mModel );
49  mTableView->setItemDelegateForColumn( static_cast<int>( QgsFieldMappingModel::ColumnDataIndex::SourceExpression ), new ExpressionDelegate( this ) );
50  mTableView->setItemDelegateForColumn( static_cast<int>( QgsFieldMappingModel::ColumnDataIndex::DestinationType ), new TypeDelegate( mTableView ) );
51  updateColumns();
52  // Make sure columns are updated when rows are added
53  connect( mModel, &QgsFieldMappingModel::rowsInserted, this, [ = ] { updateColumns(); } );
54  connect( mModel, &QgsFieldMappingModel::modelReset, this, [ = ] { updateColumns(); } );
55  connect( mModel, &QgsFieldMappingModel::dataChanged, this, &QgsFieldMappingWidget::changed );
56  connect( mModel, &QgsFieldMappingModel::rowsInserted, this, &QgsFieldMappingWidget::changed );
57  connect( mModel, &QgsFieldMappingModel::rowsRemoved, this, &QgsFieldMappingWidget::changed );
58  connect( mModel, &QgsFieldMappingModel::modelReset, this, &QgsFieldMappingWidget::changed );
59 }
60 
62 {
63  qobject_cast<QgsFieldMappingModel *>( mModel )->setDestinationEditable( editable );
64  updateColumns();
65 }
66 
68 {
69  return qobject_cast<QgsFieldMappingModel *>( mModel )->destinationEditable();
70 }
71 
73 {
74  return qobject_cast<QgsFieldMappingModel *>( mModel );
75 }
76 
77 QList<QgsFieldMappingModel::Field> QgsFieldMappingWidget::mapping() const
78 {
79  return model()->mapping();
80 }
81 
82 QMap<QString, QgsProperty> QgsFieldMappingWidget::fieldPropertyMap() const
83 {
84  return model()->fieldPropertyMap();
85 }
86 
87 void QgsFieldMappingWidget::setFieldPropertyMap( const QMap<QString, QgsProperty> &map )
88 {
89  model()->setFieldPropertyMap( map );
90 }
91 
93 {
94  return mTableView->selectionModel();
95 }
96 
98 {
100 }
101 
103 {
104  mSourceLayer = layer;
105 }
106 
108 {
109  return mSourceLayer;
110 }
111 
112 void QgsFieldMappingWidget::setDestinationFields( const QgsFields &destinationFields, const QMap<QString, QString> &expressions )
113 {
114  model()->setDestinationFields( destinationFields, expressions );
115 }
116 
117 void QgsFieldMappingWidget::scrollTo( const QModelIndex &index ) const
118 {
119  mTableView->scrollTo( index );
120 }
121 
123 {
125 }
126 
127 void QgsFieldMappingWidget::appendField( const QgsField &field, const QString &expression )
128 {
129  model()->appendField( field, expression );
130 }
131 
133 {
134  if ( ! mTableView->selectionModel()->hasSelection() )
135  return false;
136 
137  std::list<int> rowsToRemove { selectedRows() };
138  rowsToRemove.reverse();
139  for ( int row : rowsToRemove )
140  {
141  if ( ! model()->removeField( model()->index( row, 0, QModelIndex() ) ) )
142  {
143  return false;
144  }
145  }
146  return true;
147 }
148 
150 {
151  if ( ! mTableView->selectionModel()->hasSelection() )
152  return false;
153 
154  const std::list<int> rowsToMoveUp { selectedRows() };
155  for ( int row : rowsToMoveUp )
156  {
157  if ( ! model()->moveUp( model()->index( row, 0, QModelIndex() ) ) )
158  {
159  return false;
160  }
161  }
162  return true;
163 }
164 
166 {
167  if ( ! mTableView->selectionModel()->hasSelection() )
168  return false;
169 
170  std::list<int> rowsToMoveDown { selectedRows() };
171  rowsToMoveDown.reverse();
172  for ( int row : rowsToMoveDown )
173  {
174  if ( ! model()->moveDown( model()->index( row, 0, QModelIndex() ) ) )
175  {
176  return false;
177  }
178  }
179  return true;
180 }
181 
182 void QgsFieldMappingWidget::updateColumns()
183 {
184  for ( int i = 0; i < mModel->rowCount(); ++i )
185  {
186  mTableView->openPersistentEditor( mModel->index( i, static_cast<int>( QgsFieldMappingModel::ColumnDataIndex::SourceExpression ) ) );
187  mTableView->openPersistentEditor( mModel->index( i, static_cast<int>( QgsFieldMappingModel::ColumnDataIndex::DestinationType ) ) );
188  }
189 
190  for ( int i = 0; i < mModel->columnCount(); ++i )
191  {
192  mTableView->resizeColumnToContents( i );
193  }
194 }
195 
196 std::list<int> QgsFieldMappingWidget::selectedRows()
197 {
198  std::list<int> rows;
199  if ( mTableView->selectionModel()->hasSelection() )
200  {
201  const QModelIndexList constSelection { mTableView->selectionModel()->selectedIndexes() };
202  for ( const QModelIndex &index : constSelection )
203  {
204  rows.push_back( index.row() );
205  }
206  rows.sort();
207  rows.unique();
208  }
209  return rows;
210 }
211 
212 //
213 // ExpressionDelegate
214 //
215 
216 QgsFieldMappingWidget::ExpressionDelegate::ExpressionDelegate( QObject *parent )
217  : QStyledItemDelegate( parent )
218 {
219 }
220 
221 void QgsFieldMappingWidget::ExpressionDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
222 {
223  QgsFieldExpressionWidget *editorWidget { qobject_cast<QgsFieldExpressionWidget *>( editor ) };
224  if ( ! editorWidget )
225  return;
226 
227  bool isExpression;
228  bool isValid;
229  const QString currentValue { editorWidget->currentField( &isExpression, &isValid ) };
230  if ( isExpression )
231  {
232  model->setData( index, currentValue, Qt::EditRole );
233  }
234  else
235  {
236  model->setData( index, QgsExpression::quotedColumnRef( currentValue ), Qt::EditRole );
237  }
238 }
239 
240 void QgsFieldMappingWidget::ExpressionDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
241 {
242  QgsFieldExpressionWidget *editorWidget { qobject_cast<QgsFieldExpressionWidget *>( editor ) };
243  if ( ! editorWidget )
244  return;
245 
246  const QVariant value = index.model()->data( index, Qt::EditRole );
247  editorWidget->setField( value.toString() );
248 }
249 
250 QWidget *QgsFieldMappingWidget::ExpressionDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
251 {
252  Q_UNUSED( option )
253  QgsFieldExpressionWidget *editor = new QgsFieldExpressionWidget( parent );
254  editor->setAutoFillBackground( true );
255  editor->setAllowEvalErrors( false );
256  editor->setAllowEmptyFieldName( true );
257  if ( const QgsFieldMappingModel *model = qobject_cast<const QgsFieldMappingModel *>( index.model() ) )
258  {
259  editor->registerExpressionContextGenerator( model->contextGenerator() );
260  editor->setFields( model->sourceFields() );
261  }
262  else if ( const QgsAggregateMappingModel *model = qobject_cast<const QgsAggregateMappingModel *>( index.model() ) )
263  {
264  editor->registerExpressionContextGenerator( model->contextGenerator() );
265  editor->setFields( model->sourceFields() );
266  }
267  else
268  {
269  Q_ASSERT( false );
270  }
271 
272  if ( QgsFieldMappingWidget *mappingWidget = qobject_cast< QgsFieldMappingWidget *>( ExpressionDelegate::parent() ) )
273  {
274  if ( mappingWidget->sourceLayer() )
275  editor->setLayer( mappingWidget->sourceLayer() );
276  }
277  else if ( QgsAggregateMappingWidget *aggregateWidget = qobject_cast< QgsAggregateMappingWidget *>( ExpressionDelegate::parent() ) )
278  {
279  if ( aggregateWidget->sourceLayer() )
280  editor->setLayer( aggregateWidget->sourceLayer() );
281  }
282 
283  editor->setField( index.model()->data( index, Qt::DisplayRole ).toString() );
284  connect( editor,
285  qgis::overload<const QString &, bool >::of( &QgsFieldExpressionWidget::fieldChanged ),
286  this,
287  [ = ]( const QString & fieldName, bool isValid )
288  {
289  Q_UNUSED( fieldName )
290  Q_UNUSED( isValid )
291  const_cast< QgsFieldMappingWidget::ExpressionDelegate *>( this )->emit commitData( editor );
292  } );
293  return editor;
294 }
295 
296 
297 //
298 // TypeDelegate
299 //
300 
301 QgsFieldMappingWidget::TypeDelegate::TypeDelegate( QObject *parent )
302  : QStyledItemDelegate( parent )
303 {
304 }
305 
306 QWidget *QgsFieldMappingWidget::TypeDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
307 {
308  Q_UNUSED( option )
309  QComboBox *editor = new QComboBox( parent );
310 
311  const QMap<QVariant::Type, QString> typeList { QgsFieldMappingModel::dataTypes() };
312  int i = 0;
313  for ( auto it = typeList.constBegin(); it != typeList.constEnd(); ++it )
314  {
315  editor->addItem( typeList[ it.key() ] );
316  editor->setItemData( i, static_cast<int>( it.key() ), Qt::UserRole );
317  ++i;
318  }
319 
320  const QgsFieldMappingModel *model { qobject_cast<const QgsFieldMappingModel *>( index.model() ) };
321 
322  if ( model && !model->destinationEditable() )
323  {
324  editor->setEnabled( false );
325  }
326  else
327  {
328  connect( editor,
329  qgis::overload<int >::of( &QComboBox::currentIndexChanged ),
330  this,
331  [ = ]( int currentIndex )
332  {
333  Q_UNUSED( currentIndex )
334  const_cast< QgsFieldMappingWidget::TypeDelegate *>( this )->emit commitData( editor );
335  } );
336  }
337  return editor;
338 }
339 
340 void QgsFieldMappingWidget::TypeDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
341 {
342  QComboBox *editorWidget { qobject_cast<QComboBox *>( editor ) };
343  if ( ! editorWidget )
344  return;
345 
346  const QVariant value { index.model()->data( index, Qt::EditRole ) };
347  editorWidget->setCurrentIndex( editorWidget->findData( value ) );
348 }
349 
350 void QgsFieldMappingWidget::TypeDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
351 {
352  QComboBox *editorWidget { qobject_cast<QComboBox *>( editor ) };
353  if ( ! editorWidget )
354  return;
355 
356  const QVariant currentValue { editorWidget->currentData( ) };
357  model->setData( index, currentValue, Qt::EditRole );
358 }
QgsFieldMappingModel::setDestinationFields
void setDestinationFields(const QgsFields &destinationFields, const QMap< QString, QString > &expressions=QMap< QString, QString >())
Set destination fields to destinationFields, initial values for the expressions can be optionally spe...
Definition: qgsfieldmappingmodel.cpp:346
qgsexpression.h
QgsFieldExpressionWidget::currentField
QString currentField(bool *isExpression=nullptr, bool *isValid=nullptr) const
currentField returns the currently selected field or expression if allowed
Definition: qgsfieldexpressionwidget.cpp:144
QgsFields
Container of fields for a vector layer.
Definition: qgsfields.h:45
QgsFieldMappingWidget::appendField
void appendField(const QgsField &field, const QString &expression=QString())
Appends a new field to the model, with an optional expression.
Definition: qgsfieldmappingwidget.cpp:127
QgsFieldMappingWidget::fieldPropertyMap
QMap< QString, QgsProperty > fieldPropertyMap() const
Returns a map of destination field name to QgsProperty definition for field value,...
Definition: qgsfieldmappingwidget.cpp:82
QgsFieldMappingWidget::destinationEditable
bool destinationEditable() const
Returns true if the destination fields are editable in the model.
Definition: qgsfieldmappingwidget.cpp:67
QgsFieldExpressionWidget::setAllowEmptyFieldName
void setAllowEmptyFieldName(bool allowEmpty)
Sets whether an optional empty field ("not set") option is shown in the combo box.
Definition: qgsfieldexpressionwidget.cpp:87
field
const QgsField & field
Definition: qgsfield.h:456
QgsFieldMappingModel::dataTypes
static const QMap< QVariant::Type, QString > dataTypes()
Returns a static map of supported data types.
Definition: qgsfieldmappingmodel.cpp:390
QgsAggregateMappingModel::moveUp
bool moveUp(const QModelIndex &index)
Moves down the field at index.
Definition: qgsprocessingaggregatewidgets.cpp:335
QgsAggregateMappingWidget
The QgsAggregateMappingWidget class creates a mapping for defining sets of aggregates of fields from ...
Definition: qgsprocessingaggregatewidgets.h:150
QgsFieldExpressionWidget::registerExpressionContextGenerator
void registerExpressionContextGenerator(const QgsExpressionContextGenerator *generator)
Register an expression context generator class that will be used to retrieve an expression context fo...
Definition: qgsfieldexpressionwidget.cpp:165
QgsFieldMappingWidget::setDestinationFields
void setDestinationFields(const QgsFields &destinationFields, const QMap< QString, QString > &expressions=QMap< QString, QString >())
Set destination fields to destinationFields in the underlying model, initial values for the expressio...
Definition: qgsfieldmappingwidget.cpp:112
QgsFieldMappingModel
The QgsFieldMappingModel holds mapping information for mapping from one set of QgsFields to another,...
Definition: qgsfieldmappingmodel.h:40
QgsFieldMappingModel::mapping
QList< QgsFieldMappingModel::Field > mapping() const
Returns a list of Field objects representing the current status of the model.
Definition: qgsfieldmappingmodel.cpp:407
QgsFieldMappingModel::appendField
void appendField(const QgsField &field, const QString &expression=QString())
Appends a new field to the model, with an optional expression.
Definition: qgsfieldmappingmodel.cpp:462
QgsFieldMappingModel::setBaseExpressionContextGenerator
void setBaseExpressionContextGenerator(const QgsExpressionContextGenerator *generator)
Sets the base expression context generator, which will generate the expression contexts for expressio...
Definition: qgsfieldmappingmodel.cpp:341
QgsAggregateMappingModel::sourceFields
QgsFields sourceFields() const
Returns a list of source fields.
Definition: qgsprocessingaggregatewidgets.cpp:92
QgsPanelWidget
Base class for any widget that can be shown as a inline panel.
Definition: qgspanelwidget.h:30
QgsFieldMappingWidget::scrollTo
void scrollTo(const QModelIndex &index) const
Scroll the fields view to index.
Definition: qgsfieldmappingwidget.cpp:117
QgsFieldMappingWidget::setFieldPropertyMap
void setFieldPropertyMap(const QMap< QString, QgsProperty > &map)
Sets a map of destination field name to QgsProperty definition for field value.
Definition: qgsfieldmappingwidget.cpp:87
QgsFieldMappingWidget::selectionModel
QItemSelectionModel * selectionModel()
Returns the selection model.
Definition: qgsfieldmappingwidget.cpp:92
QgsFieldMappingWidget::setSourceLayer
void setSourceLayer(QgsVectorLayer *layer)
Sets a source layer to use when generating expression previews in the widget.
Definition: qgsfieldmappingwidget.cpp:102
QgsFieldMappingWidget::mapping
QList< QgsFieldMappingModel::Field > mapping() const
Returns a list of Field objects representing the current status of the underlying mapping model.
Definition: qgsfieldmappingwidget.cpp:77
QgsFieldExpressionWidget::fieldChanged
void fieldChanged(const QString &fieldName)
Emitted when the currently selected field changes.
QgsFieldMappingWidget::QgsFieldMappingWidget
QgsFieldMappingWidget(QWidget *parent=nullptr, const QgsFields &sourceFields=QgsFields(), const QgsFields &destinationFields=QgsFields(), const QMap< QString, QString > &expressions=QMap< QString, QString >())
Constructs a QgsFieldMappingWidget from a set of sourceFields and destinationFields,...
Definition: qgsfieldmappingwidget.cpp:30
QgsFieldMappingWidget::sourceLayer
QgsVectorLayer * sourceLayer()
Returns the source layer for use when generating expression previews.
Definition: qgsfieldmappingwidget.cpp:107
QgsAggregateMappingModel::removeField
bool removeField(const QModelIndex &index)
Removes the field at index from the model, returns true on success.
Definition: qgsprocessingaggregatewidgets.cpp:320
qgsfieldmappingwidget.h
QgsFieldMappingModel::setFieldPropertyMap
void setFieldPropertyMap(const QMap< QString, QgsProperty > &map)
Sets a map of destination field name to QgsProperty definition for field value.
Definition: qgsfieldmappingmodel.cpp:426
QgsFieldMappingWidget::setDestinationEditable
void setDestinationEditable(bool editable)
Sets the destination fields editable state to editable.
Definition: qgsfieldmappingwidget.cpp:61
QgsFieldMappingWidget::setSourceFields
void setSourceFields(const QgsFields &sourceFields)
Set source fields of the underlying mapping model to sourceFields.
Definition: qgsfieldmappingwidget.cpp:97
QgsFieldExpressionWidget::setField
void setField(const QString &fieldName)
sets the current field or expression in the widget
Definition: qgsfieldexpressionwidget.cpp:188
QgsAggregateMappingModel::moveDown
bool moveDown(const QModelIndex &index)
Moves up the field at index.
Definition: qgsprocessingaggregatewidgets.cpp:340
QgsFieldExpressionWidget::setFields
void setFields(const QgsFields &fields)
Sets the fields used in the widget to fields, this allows the widget to work without a layer.
Definition: qgsfieldexpressionwidget.cpp:221
qgsprocessingaggregatewidgets.h
qgsvectorlayer.h
QgsFieldMappingWidget::registerExpressionContextGenerator
void registerExpressionContextGenerator(const QgsExpressionContextGenerator *generator)
Register an expression context generator class that will be used to retrieve an expression context fo...
Definition: qgsfieldmappingwidget.cpp:122
QgsFieldMappingModel::ColumnDataIndex::SourceExpression
@ SourceExpression
Expression.
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:387
QgsFieldMappingWidget::removeSelectedFields
bool removeSelectedFields()
Removes the currently selected field from the model.
Definition: qgsfieldmappingwidget.cpp:132
QgsFieldMappingWidget::model
QgsFieldMappingModel * model() const
Returns the underlying mapping model.
Definition: qgsfieldmappingwidget.cpp:72
QgsFieldMappingModel::data
QVariant data(const QModelIndex &index, int role) const override
Definition: qgsfieldmappingmodel.cpp:97
QgsExpression::quotedColumnRef
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
Definition: qgsexpression.cpp:65
QgsAggregateMappingModel
The QgsAggregateMappingModel holds mapping information for defining sets of aggregates of fields from...
Definition: qgsprocessingaggregatewidgets.h:42
qgsfieldexpressionwidget.h
QgsFieldMappingModel::fieldPropertyMap
QMap< QString, QgsProperty > fieldPropertyMap() const
Returns a map of destination field name to QgsProperty definition for field value,...
Definition: qgsfieldmappingmodel.cpp:412
QgsFieldExpressionWidget::setAllowEvalErrors
void setAllowEvalErrors(bool allowEvalErrors)
Allow accepting expressions with evaluation errors.
Definition: qgsfieldexpressionwidget.cpp:316
QgsFieldMappingModel::setSourceFields
void setSourceFields(const QgsFields &sourceFields)
Set source fields to sourceFields.
Definition: qgsfieldmappingmodel.cpp:310
QgsFieldMappingWidget::moveSelectedFieldsDown
bool moveSelectedFieldsDown()
Moves down the currently selected field.
Definition: qgsfieldmappingwidget.cpp:165
QgsFieldMappingWidget::changed
void changed()
Emitted when the fields defined in the widget are changed.
QgsFieldMappingWidget::moveSelectedFieldsUp
bool moveSelectedFieldsUp()
Moves up currently selected field.
Definition: qgsfieldmappingwidget.cpp:149
QgsFieldExpressionWidget
The QgsFieldExpressionWidget class reates a widget to choose fields and edit expressions It contains ...
Definition: qgsfieldexpressionwidget.h:47
QgsExpressionContextGenerator
Abstract interface for generating an expression context.
Definition: qgsexpressioncontextgenerator.h:37
QgsFieldExpressionWidget::setLayer
void setLayer(QgsMapLayer *layer)
Sets the layer used to display the fields and expression.
Definition: qgsfieldexpressionwidget.cpp:170
QgsFieldMappingWidget
The QgsFieldMappingWidget class creates a mapping from one set of QgsFields to another,...
Definition: qgsfieldmappingwidget.h:40
QgsFieldMappingModel::ColumnDataIndex::DestinationType
@ DestinationType
Destination field QVariant::Type casted to (int)
QgsField
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:50