QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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 
22 #include <QTableView>
23 #include <QVBoxLayout>
24 
25 #ifdef ENABLE_MODELTEST
26 #include "modeltest.h"
27 #endif
28 
30  const QgsFields &sourceFields,
31  const QgsFields &destinationFields,
32  const QMap<QString, QString> &expressions )
33  : QgsPanelWidget( parent )
34 {
35  QVBoxLayout *verticalLayout = new QVBoxLayout();
36  verticalLayout->setContentsMargins( 0, 0, 0, 0 );
37  mTableView = new QTableView();
38  verticalLayout->addWidget( mTableView );
39  setLayout( verticalLayout );
40 
41  mModel = new QgsFieldMappingModel( sourceFields, destinationFields, expressions, this );
42 
43 #ifdef ENABLE_MODELTEST
44  new ModelTest( mModel, this );
45 #endif
46 
47  mTableView->setModel( mModel );
48  mTableView->setItemDelegateForColumn( static_cast<int>( QgsFieldMappingModel::ColumnDataIndex::SourceExpression ), new ExpressionDelegate( mTableView ) );
49  mTableView->setItemDelegateForColumn( static_cast<int>( QgsFieldMappingModel::ColumnDataIndex::DestinationType ), new TypeDelegate( mTableView ) );
50  updateColumns();
51  // Make sure columns are updated when rows are added
52  connect( mModel, &QgsFieldMappingModel::rowsInserted, this, [ = ] { updateColumns(); } );
53  connect( mModel, &QgsFieldMappingModel::modelReset, this, [ = ] { updateColumns(); } );
54  connect( mModel, &QgsFieldMappingModel::dataChanged, this, &QgsFieldMappingWidget::changed );
55  connect( mModel, &QgsFieldMappingModel::rowsInserted, this, &QgsFieldMappingWidget::changed );
56  connect( mModel, &QgsFieldMappingModel::rowsRemoved, this, &QgsFieldMappingWidget::changed );
57  connect( mModel, &QgsFieldMappingModel::modelReset, this, &QgsFieldMappingWidget::changed );
58 }
59 
61 {
62  qobject_cast<QgsFieldMappingModel *>( mModel )->setDestinationEditable( editable );
63  updateColumns();
64 }
65 
67 {
68  return qobject_cast<QgsFieldMappingModel *>( mModel )->destinationEditable();
69 }
70 
72 {
73  return qobject_cast<QgsFieldMappingModel *>( mModel );
74 }
75 
76 QList<QgsFieldMappingModel::Field> QgsFieldMappingWidget::mapping() const
77 {
78  return model()->mapping();
79 }
80 
81 QMap<QString, QgsProperty> QgsFieldMappingWidget::fieldPropertyMap() const
82 {
83  return model()->fieldPropertyMap();
84 }
85 
86 void QgsFieldMappingWidget::setFieldPropertyMap( const QMap<QString, QgsProperty> &map )
87 {
88  model()->setFieldPropertyMap( map );
89 }
90 
92 {
93  return mTableView->selectionModel();
94 }
95 
97 {
99 }
100 
101 void QgsFieldMappingWidget::setDestinationFields( const QgsFields &destinationFields, const QMap<QString, QString> &expressions )
102 {
103  model()->setDestinationFields( destinationFields, expressions );
104 }
105 
106 void QgsFieldMappingWidget::scrollTo( const QModelIndex &index ) const
107 {
108  mTableView->scrollTo( index );
109 }
110 
112 {
114 }
115 
116 void QgsFieldMappingWidget::appendField( const QgsField &field, const QString &expression )
117 {
118  model()->appendField( field, expression );
119 }
120 
122 {
123  if ( ! mTableView->selectionModel()->hasSelection() )
124  return false;
125 
126  std::list<int> rowsToRemove { selectedRows() };
127  rowsToRemove.reverse();
128  for ( int row : rowsToRemove )
129  {
130  if ( ! model()->removeField( model()->index( row, 0, QModelIndex() ) ) )
131  {
132  return false;
133  }
134  }
135  return true;
136 }
137 
139 {
140  if ( ! mTableView->selectionModel()->hasSelection() )
141  return false;
142 
143  const std::list<int> rowsToMoveUp { selectedRows() };
144  for ( int row : rowsToMoveUp )
145  {
146  if ( ! model()->moveUp( model()->index( row, 0, QModelIndex() ) ) )
147  {
148  return false;
149  }
150  }
151  return true;
152 }
153 
155 {
156  if ( ! mTableView->selectionModel()->hasSelection() )
157  return false;
158 
159  std::list<int> rowsToMoveDown { selectedRows() };
160  rowsToMoveDown.reverse();
161  for ( int row : rowsToMoveDown )
162  {
163  if ( ! model()->moveDown( model()->index( row, 0, QModelIndex() ) ) )
164  {
165  return false;
166  }
167  }
168  return true;
169 }
170 
171 void QgsFieldMappingWidget::updateColumns()
172 {
173  for ( int i = 0; i < mModel->rowCount(); ++i )
174  {
175  mTableView->openPersistentEditor( mModel->index( i, static_cast<int>( QgsFieldMappingModel::ColumnDataIndex::SourceExpression ) ) );
176  mTableView->openPersistentEditor( mModel->index( i, static_cast<int>( QgsFieldMappingModel::ColumnDataIndex::DestinationType ) ) );
177  }
178 
179  for ( int i = 0; i < mModel->columnCount(); ++i )
180  {
181  mTableView->resizeColumnToContents( i );
182  }
183 }
184 
185 std::list<int> QgsFieldMappingWidget::selectedRows()
186 {
187  std::list<int> rows;
188  if ( mTableView->selectionModel()->hasSelection() )
189  {
190  const QModelIndexList constSelection { mTableView->selectionModel()->selectedIndexes() };
191  for ( const QModelIndex &index : constSelection )
192  {
193  rows.push_back( index.row() );
194  }
195  rows.sort();
196  rows.unique();
197  }
198  return rows;
199 }
200 
201 //
202 // ExpressionDelegate
203 //
204 
205 QgsFieldMappingWidget::ExpressionDelegate::ExpressionDelegate( QObject *parent )
206  : QStyledItemDelegate( parent )
207 {
208 }
209 
210 void QgsFieldMappingWidget::ExpressionDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
211 {
212  QgsFieldExpressionWidget *editorWidget { qobject_cast<QgsFieldExpressionWidget *>( editor ) };
213  if ( ! editorWidget )
214  return;
215 
216  bool isExpression;
217  bool isValid;
218  const QString currentValue { editorWidget->currentField( &isExpression, &isValid ) };
219  if ( isExpression )
220  {
221  model->setData( index, currentValue, Qt::EditRole );
222  }
223  else
224  {
225  model->setData( index, QgsExpression::quotedColumnRef( currentValue ), Qt::EditRole );
226  }
227 }
228 
229 void QgsFieldMappingWidget::ExpressionDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
230 {
231  QgsFieldExpressionWidget *editorWidget { qobject_cast<QgsFieldExpressionWidget *>( editor ) };
232  if ( ! editorWidget )
233  return;
234 
235  const QVariant value = index.model()->data( index, Qt::EditRole );
236  editorWidget->setField( value.toString() );
237 }
238 
239 QWidget *QgsFieldMappingWidget::ExpressionDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
240 {
241  Q_UNUSED( option )
242  QgsFieldExpressionWidget *editor = new QgsFieldExpressionWidget( parent );
243  editor->setAutoFillBackground( true );
244  editor->setAllowEvalErrors( false );
245  editor->setAllowEmptyFieldName( true );
246  if ( const QgsFieldMappingModel *model = qobject_cast<const QgsFieldMappingModel *>( index.model() ) )
247  {
248  editor->registerExpressionContextGenerator( model->contextGenerator() );
249  editor->setFields( model->sourceFields() );
250  }
251  else if ( const QgsAggregateMappingModel *model = qobject_cast<const QgsAggregateMappingModel *>( index.model() ) )
252  {
253  editor->registerExpressionContextGenerator( model->contextGenerator() );
254  editor->setFields( model->sourceFields() );
255  }
256  else
257  {
258  Q_ASSERT( false );
259  }
260  editor->setField( index.model()->data( index, Qt::DisplayRole ).toString() );
261  connect( editor,
262  qgis::overload<const QString &, bool >::of( &QgsFieldExpressionWidget::fieldChanged ),
263  this,
264  [ = ]( const QString & fieldName, bool isValid )
265  {
266  Q_UNUSED( fieldName )
267  Q_UNUSED( isValid )
268  const_cast< QgsFieldMappingWidget::ExpressionDelegate *>( this )->emit commitData( editor );
269  } );
270  return editor;
271 }
272 
273 
274 //
275 // TypeDelegate
276 //
277 
278 QgsFieldMappingWidget::TypeDelegate::TypeDelegate( QObject *parent )
279  : QStyledItemDelegate( parent )
280 {
281 }
282 
283 QWidget *QgsFieldMappingWidget::TypeDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
284 {
285  Q_UNUSED( option )
286  QComboBox *editor = new QComboBox( parent );
287 
288  const QMap<QVariant::Type, QString> typeList { QgsFieldMappingModel::dataTypes() };
289  int i = 0;
290  for ( auto it = typeList.constBegin(); it != typeList.constEnd(); ++it )
291  {
292  editor->addItem( typeList[ it.key() ] );
293  editor->setItemData( i, static_cast<int>( it.key() ), Qt::UserRole );
294  ++i;
295  }
296 
297  const QgsFieldMappingModel *model { qobject_cast<const QgsFieldMappingModel *>( index.model() ) };
298 
299  if ( model && !model->destinationEditable() )
300  {
301  editor->setEnabled( false );
302  }
303  else
304  {
305  connect( editor,
306  qgis::overload<int >::of( &QComboBox::currentIndexChanged ),
307  this,
308  [ = ]( int currentIndex )
309  {
310  Q_UNUSED( currentIndex )
311  const_cast< QgsFieldMappingWidget::TypeDelegate *>( this )->emit commitData( editor );
312  } );
313  }
314  return editor;
315 }
316 
317 void QgsFieldMappingWidget::TypeDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
318 {
319  QComboBox *editorWidget { qobject_cast<QComboBox *>( editor ) };
320  if ( ! editorWidget )
321  return;
322 
323  const QVariant value { index.model()->data( index, Qt::EditRole ) };
324  editorWidget->setCurrentIndex( editorWidget->findData( value ) );
325 }
326 
327 void QgsFieldMappingWidget::TypeDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
328 {
329  QComboBox *editorWidget { qobject_cast<QComboBox *>( editor ) };
330  if ( ! editorWidget )
331  return;
332 
333  const QVariant currentValue { editorWidget->currentData( ) };
334  model->setData( index, currentValue, Qt::EditRole );
335 }
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:344
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
Definition: qgsfields.h:44
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:116
QgsFieldMappingWidget::fieldPropertyMap
QMap< QString, QgsProperty > fieldPropertyMap() const
Returns a map of destination field name to QgsProperty definition for field value,...
Definition: qgsfieldmappingwidget.cpp:81
QgsFieldMappingWidget::destinationEditable
bool destinationEditable() const
Returns true if the destination fields are editable in the model.
Definition: qgsfieldmappingwidget.cpp:66
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
QgsFieldMappingModel::dataTypes
static const QMap< QVariant::Type, QString > dataTypes()
Returns a static map of supported data types.
Definition: qgsfieldmappingmodel.cpp:388
QgsAggregateMappingModel::moveUp
bool moveUp(const QModelIndex &index)
Moves down the field at index.
Definition: qgsprocessingaggregatewidgets.cpp:332
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:101
QgsFieldMappingModel
Definition: qgsfieldmappingmodel.h:39
QgsFieldMappingModel::mapping
QList< QgsFieldMappingModel::Field > mapping() const
Returns a list of Field objects representing the current status of the model.
Definition: qgsfieldmappingmodel.cpp:405
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:460
QgsFieldMappingModel::setBaseExpressionContextGenerator
void setBaseExpressionContextGenerator(const QgsExpressionContextGenerator *generator)
Sets the base expression context generator, which will generate the expression contexts for expressio...
Definition: qgsfieldmappingmodel.cpp:339
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:29
QgsFieldMappingWidget::scrollTo
void scrollTo(const QModelIndex &index) const
Scroll the fields view to index.
Definition: qgsfieldmappingwidget.cpp:106
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:86
QgsFieldMappingWidget::selectionModel
QItemSelectionModel * selectionModel()
Returns the selection model.
Definition: qgsfieldmappingwidget.cpp:91
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:76
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:29
QgsAggregateMappingModel::removeField
bool removeField(const QModelIndex &index)
Removes the field at index from the model, returns true on success.
Definition: qgsprocessingaggregatewidgets.cpp:317
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:424
QgsFieldMappingWidget::setDestinationEditable
void setDestinationEditable(bool editable)
Sets the destination fields editable state to editable.
Definition: qgsfieldmappingwidget.cpp:60
QgsFieldMappingWidget::setSourceFields
void setSourceFields(const QgsFields &sourceFields)
Set source fields of the underlying mapping model to sourceFields.
Definition: qgsfieldmappingwidget.cpp:96
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:337
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
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:111
QgsFieldMappingModel::ColumnDataIndex::SourceExpression
@ SourceExpression
Expression.
QgsFieldMappingWidget::removeSelectedFields
bool removeSelectedFields()
Removes the currently selected field from the model.
Definition: qgsfieldmappingwidget.cpp:121
QgsFieldMappingWidget::model
QgsFieldMappingModel * model() const
Returns the underlying mapping model.
Definition: qgsfieldmappingwidget.cpp:71
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
Definition: qgsprocessingaggregatewidgets.h:40
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:410
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:154
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:138
QgsFieldExpressionWidget
The QgsFieldExpressionWidget class reates a widget to choose fields and edit expressions It contains ...
Definition: qgsfieldexpressionwidget.h:46
QgsExpressionContextGenerator
Definition: qgsexpressioncontextgenerator.h:36
QgsFieldMappingModel::ColumnDataIndex::DestinationType
@ DestinationType
Destination field QVariant::Type casted to (int)
QgsField
Definition: qgsfield.h:49