QGIS API Documentation  3.23.0-Master (c716e02dd3)
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 ( const 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 ( const 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 ( const 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  if ( const QgsFieldMappingModel *model = qobject_cast<const QgsFieldMappingModel *>( index.model() ) )
257  {
258  editor->registerExpressionContextGenerator( model->contextGenerator() );
259  editor->setFields( model->sourceFields() );
260  }
261  else if ( const QgsAggregateMappingModel *model = qobject_cast<const QgsAggregateMappingModel *>( index.model() ) )
262  {
263  editor->registerExpressionContextGenerator( model->contextGenerator() );
264  editor->setFields( model->sourceFields() );
265  }
266  else
267  {
268  Q_ASSERT( false );
269  }
270 
271  if ( QgsFieldMappingWidget *mappingWidget = qobject_cast< QgsFieldMappingWidget *>( ExpressionDelegate::parent() ) )
272  {
273  if ( mappingWidget->sourceLayer() )
274  editor->setLayer( mappingWidget->sourceLayer() );
275  }
276  else if ( QgsAggregateMappingWidget *aggregateWidget = qobject_cast< QgsAggregateMappingWidget *>( ExpressionDelegate::parent() ) )
277  {
278  if ( aggregateWidget->sourceLayer() )
279  editor->setLayer( aggregateWidget->sourceLayer() );
280  }
281 
282  editor->setField( index.model()->data( index, Qt::DisplayRole ).toString() );
283  connect( editor,
284  qOverload<const QString &, bool >( &QgsFieldExpressionWidget::fieldChanged ),
285  this,
286  [ = ]( const QString & fieldName, bool isValid )
287  {
288  Q_UNUSED( fieldName )
289  Q_UNUSED( isValid )
290  const_cast< QgsFieldMappingWidget::ExpressionDelegate *>( this )->emit commitData( editor );
291  } );
292  return editor;
293 }
294 
295 
296 //
297 // TypeDelegate
298 //
299 
300 QgsFieldMappingWidget::TypeDelegate::TypeDelegate( QObject *parent )
301  : QStyledItemDelegate( parent )
302 {
303 }
304 
305 QWidget *QgsFieldMappingWidget::TypeDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
306 {
307  Q_UNUSED( option )
308  QComboBox *editor = new QComboBox( parent );
309 
310  const QMap<QVariant::Type, QString> typeList { QgsFieldMappingModel::dataTypes() };
311  int i = 0;
312  for ( auto it = typeList.constBegin(); it != typeList.constEnd(); ++it )
313  {
314  editor->addItem( typeList[ it.key() ] );
315  editor->setItemData( i, static_cast<int>( it.key() ), Qt::UserRole );
316  ++i;
317  }
318 
319  const QgsFieldMappingModel *model { qobject_cast<const QgsFieldMappingModel *>( index.model() ) };
320 
321  if ( model && !model->destinationEditable() )
322  {
323  editor->setEnabled( false );
324  }
325  else
326  {
327  connect( editor,
328  qOverload<int >( &QComboBox::currentIndexChanged ),
329  this,
330  [ = ]( int currentIndex )
331  {
332  Q_UNUSED( currentIndex )
333  const_cast< QgsFieldMappingWidget::TypeDelegate *>( this )->emit commitData( editor );
334  } );
335  }
336  return editor;
337 }
338 
339 void QgsFieldMappingWidget::TypeDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
340 {
341  QComboBox *editorWidget { qobject_cast<QComboBox *>( editor ) };
342  if ( ! editorWidget )
343  return;
344 
345  const QVariant value = index.model()->data( index, Qt::EditRole );
346  editorWidget->setCurrentIndex( editorWidget->findData( value ) );
347 }
348 
349 void QgsFieldMappingWidget::TypeDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
350 {
351  QComboBox *editorWidget { qobject_cast<QComboBox *>( editor ) };
352  if ( ! editorWidget )
353  return;
354 
355  const QVariant currentValue = editorWidget->currentData( );
356  model->setData( index, currentValue, Qt::EditRole );
357 }
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 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:463