QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 {
99  model()->setSourceFields( sourceFields );
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 }
The QgsAggregateMappingModel holds mapping information for defining sets of aggregates of fields from...
The QgsAggregateMappingWidget class creates a mapping for defining sets of aggregates of fields from ...
Abstract interface for generating an expression context.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
The QgsFieldExpressionWidget class reates a widget to choose fields and edit expressions It contains ...
void setAllowEmptyFieldName(bool allowEmpty)
Sets whether an optional empty field ("not set") option is shown in the combo box.
void setField(const QString &fieldName)
sets the current field or expression in the widget
void setFields(const QgsFields &fields)
Sets the fields used in the widget to fields, this allows the widget to work without a layer.
void setLayer(QgsMapLayer *layer)
Sets the layer used to display the fields and expression.
void registerExpressionContextGenerator(const QgsExpressionContextGenerator *generator)
Register an expression context generator class that will be used to retrieve an expression context fo...
void setAllowEvalErrors(bool allowEvalErrors)
Allow accepting expressions with evaluation errors.
void fieldChanged(const QString &fieldName)
Emitted when the currently selected field changes.
QString currentField(bool *isExpression=nullptr, bool *isValid=nullptr) const
currentField returns the currently selected field or expression if allowed
The QgsFieldMappingModel holds mapping information for mapping from one set of QgsFields to another,...
@ DestinationType
Destination field QVariant::Type casted to (int)
static const QMap< QVariant::Type, QString > dataTypes()
Returns a static map of supported data types.
void appendField(const QgsField &field, const QString &expression=QString())
Appends a new field to the model, with an optional expression.
QMap< QString, QgsProperty > fieldPropertyMap() const
Returns a map of destination field name to QgsProperty definition for field value,...
QList< QgsFieldMappingModel::Field > mapping() const
Returns a list of Field objects representing the current status of the model.
QVariant data(const QModelIndex &index, int role) const override
void setBaseExpressionContextGenerator(const QgsExpressionContextGenerator *generator)
Sets the base expression context generator, which will generate the expression contexts for expressio...
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...
void setSourceFields(const QgsFields &sourceFields)
Set source fields to sourceFields.
void setFieldPropertyMap(const QMap< QString, QgsProperty > &map)
Sets a map of destination field name to QgsProperty definition for field value.
The QgsFieldMappingWidget class creates a mapping from one set of QgsFields to another,...
bool removeSelectedFields()
Removes the currently selected field from the model.
void setSourceLayer(QgsVectorLayer *layer)
Sets a source layer to use when generating expression previews in the widget.
QgsFieldMappingModel * model() const
Returns the underlying mapping model.
void registerExpressionContextGenerator(const QgsExpressionContextGenerator *generator)
Register an expression context generator class that will be used to retrieve an expression context fo...
bool moveSelectedFieldsDown()
Moves down the currently selected field.
QItemSelectionModel * selectionModel()
Returns the selection model.
void setSourceFields(const QgsFields &sourceFields)
Set source fields of the underlying mapping model to sourceFields.
QMap< QString, QgsProperty > fieldPropertyMap() const
Returns a map of destination field name to QgsProperty definition for field value,...
bool moveSelectedFieldsUp()
Moves up currently selected field.
QList< QgsFieldMappingModel::Field > mapping() const
Returns a list of Field objects representing the current status of the underlying mapping model.
void setFieldPropertyMap(const QMap< QString, QgsProperty > &map)
Sets a map of destination field name to QgsProperty definition for field value.
QgsVectorLayer * sourceLayer()
Returns the source layer for use when generating expression previews.
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...
void setDestinationEditable(bool editable)
Sets the destination fields editable state to editable.
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,...
void appendField(const QgsField &field, const QString &expression=QString())
Appends a new field to the model, with an optional expression.
void scrollTo(const QModelIndex &index) const
Scroll the fields view to index.
void changed()
Emitted when the fields defined in the widget are changed.
bool destinationEditable() const
Returns true if the destination fields are editable in the model.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
Container of fields for a vector layer.
Definition: qgsfields.h:45
Base class for any widget that can be shown as a inline panel.
Represents a vector layer which manages a vector based data sets.
const QgsField & field
Definition: qgsfield.h:472