QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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 #include "qgsvectordataprovider.h"
23 
24 #include <QTableView>
25 #include <QVBoxLayout>
26 
27 #ifdef ENABLE_MODELTEST
28 #include "modeltest.h"
29 #endif
30 
32  const QgsFields &sourceFields,
33  const QgsFields &destinationFields,
34  const QMap<QString, QString> &expressions )
35  : QgsPanelWidget( parent )
36 {
37  QVBoxLayout *verticalLayout = new QVBoxLayout();
38  verticalLayout->setContentsMargins( 0, 0, 0, 0 );
39  mTableView = new QTableView();
40  verticalLayout->addWidget( mTableView );
41  setLayout( verticalLayout );
42 
43  mModel = new QgsFieldMappingModel( sourceFields, destinationFields, expressions, this );
44 
45 #ifdef ENABLE_MODELTEST
46  new ModelTest( mModel, this );
47 #endif
48 
49  mTableView->setModel( mModel );
50  mTableView->setItemDelegateForColumn( static_cast<int>( QgsFieldMappingModel::ColumnDataIndex::SourceExpression ), new ExpressionDelegate( this ) );
51  mTableView->setItemDelegateForColumn( static_cast<int>( QgsFieldMappingModel::ColumnDataIndex::DestinationType ), new TypeDelegate( mTableView ) );
52  updateColumns();
53  // Make sure columns are updated when rows are added
54  connect( mModel, &QgsFieldMappingModel::rowsInserted, this, [ = ] { updateColumns(); } );
55  connect( mModel, &QgsFieldMappingModel::modelReset, this, [ = ] { updateColumns(); } );
56  connect( mModel, &QgsFieldMappingModel::dataChanged, this, &QgsFieldMappingWidget::changed );
57  connect( mModel, &QgsFieldMappingModel::rowsInserted, this, &QgsFieldMappingWidget::changed );
58  connect( mModel, &QgsFieldMappingModel::rowsRemoved, this, &QgsFieldMappingWidget::changed );
59  connect( mModel, &QgsFieldMappingModel::modelReset, this, &QgsFieldMappingWidget::changed );
60 }
61 
63 {
64  qobject_cast<QgsFieldMappingModel *>( mModel )->setDestinationEditable( editable );
65  updateColumns();
66 }
67 
69 {
70  return qobject_cast<QgsFieldMappingModel *>( mModel )->destinationEditable();
71 }
72 
74 {
75  return qobject_cast<QgsFieldMappingModel *>( mModel );
76 }
77 
78 QList<QgsFieldMappingModel::Field> QgsFieldMappingWidget::mapping() const
79 {
80  return model()->mapping();
81 }
82 
83 QMap<QString, QgsProperty> QgsFieldMappingWidget::fieldPropertyMap() const
84 {
85  return model()->fieldPropertyMap();
86 }
87 
88 void QgsFieldMappingWidget::setFieldPropertyMap( const QMap<QString, QgsProperty> &map )
89 {
90  model()->setFieldPropertyMap( map );
91 }
92 
94 {
95  return mTableView->selectionModel();
96 }
97 
99 {
100  model()->setSourceFields( sourceFields );
101 }
102 
104 {
105  mSourceLayer = layer;
106 }
107 
109 {
110  return mSourceLayer;
111 }
112 
113 void QgsFieldMappingWidget::setDestinationFields( const QgsFields &destinationFields, const QMap<QString, QString> &expressions )
114 {
115  model()->setDestinationFields( destinationFields, expressions );
116 }
117 
118 void QgsFieldMappingWidget::scrollTo( const QModelIndex &index ) const
119 {
120  mTableView->scrollTo( index );
121 }
122 
124 {
126 }
127 
128 void QgsFieldMappingWidget::appendField( const QgsField &field, const QString &expression )
129 {
130  model()->appendField( field, expression );
131 }
132 
134 {
135  if ( ! mTableView->selectionModel()->hasSelection() )
136  return false;
137 
138  std::list<int> rowsToRemove { selectedRows() };
139  rowsToRemove.reverse();
140  for ( const int row : rowsToRemove )
141  {
142  if ( ! model()->removeField( model()->index( row, 0, QModelIndex() ) ) )
143  {
144  return false;
145  }
146  }
147  return true;
148 }
149 
151 {
152  if ( ! mTableView->selectionModel()->hasSelection() )
153  return false;
154 
155  const std::list<int> rowsToMoveUp { selectedRows() };
156  for ( const int row : rowsToMoveUp )
157  {
158  if ( ! model()->moveUp( model()->index( row, 0, QModelIndex() ) ) )
159  {
160  return false;
161  }
162  }
163  return true;
164 }
165 
167 {
168  if ( ! mTableView->selectionModel()->hasSelection() )
169  return false;
170 
171  std::list<int> rowsToMoveDown { selectedRows() };
172  rowsToMoveDown.reverse();
173  for ( const int row : rowsToMoveDown )
174  {
175  if ( ! model()->moveDown( model()->index( row, 0, QModelIndex() ) ) )
176  {
177  return false;
178  }
179  }
180  return true;
181 }
182 
183 void QgsFieldMappingWidget::updateColumns()
184 {
185  for ( int i = 0; i < mModel->rowCount(); ++i )
186  {
187  mTableView->openPersistentEditor( mModel->index( i, static_cast<int>( QgsFieldMappingModel::ColumnDataIndex::SourceExpression ) ) );
188  mTableView->openPersistentEditor( mModel->index( i, static_cast<int>( QgsFieldMappingModel::ColumnDataIndex::DestinationType ) ) );
189  }
190 
191  for ( int i = 0; i < mModel->columnCount(); ++i )
192  {
193  mTableView->resizeColumnToContents( i );
194  }
195 }
196 
197 std::list<int> QgsFieldMappingWidget::selectedRows()
198 {
199  std::list<int> rows;
200  if ( mTableView->selectionModel()->hasSelection() )
201  {
202  const QModelIndexList constSelection { mTableView->selectionModel()->selectedIndexes() };
203  for ( const QModelIndex &index : constSelection )
204  {
205  rows.push_back( index.row() );
206  }
207  rows.sort();
208  rows.unique();
209  }
210  return rows;
211 }
212 
213 //
214 // ExpressionDelegate
215 //
216 
217 QgsFieldMappingWidget::ExpressionDelegate::ExpressionDelegate( QObject *parent )
218  : QStyledItemDelegate( parent )
219 {
220 }
221 
222 void QgsFieldMappingWidget::ExpressionDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
223 {
224  QgsFieldExpressionWidget *editorWidget { qobject_cast<QgsFieldExpressionWidget *>( editor ) };
225  if ( ! editorWidget )
226  return;
227 
228  bool isExpression;
229  bool isValid;
230  const QString currentValue { editorWidget->currentField( &isExpression, &isValid ) };
231  if ( isExpression )
232  {
233  model->setData( index, currentValue, Qt::EditRole );
234  }
235  else
236  {
237  model->setData( index, QgsExpression::quotedColumnRef( currentValue ), Qt::EditRole );
238  }
239 }
240 
241 void QgsFieldMappingWidget::ExpressionDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
242 {
243  QgsFieldExpressionWidget *editorWidget { qobject_cast<QgsFieldExpressionWidget *>( editor ) };
244  if ( ! editorWidget )
245  return;
246 
247  const QVariant value = index.model()->data( index, Qt::EditRole );
248  editorWidget->setField( value.toString() );
249 }
250 
251 QWidget *QgsFieldMappingWidget::ExpressionDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
252 {
253  Q_UNUSED( option )
254  QgsFieldExpressionWidget *editor = new QgsFieldExpressionWidget( parent );
255  editor->setAutoFillBackground( true );
256  editor->setAllowEvalErrors( false );
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  qOverload<const QString &, bool >( &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 QList<QgsVectorDataProvider::NativeType> typeList = QgsFieldMappingModel::supportedDataTypes();
312  for ( int i = 0; i < typeList.size(); i++ )
313  {
314  editor->addItem( QgsFields::iconForFieldType( typeList[i].mType, typeList[i].mSubType ), typeList[i].mTypeDesc );
315  editor->setItemData( i, typeList[i].mTypeName, Qt::UserRole );
316  }
317 
318  const QgsFieldMappingModel *model { qobject_cast<const QgsFieldMappingModel *>( index.model() ) };
319 
320  if ( model && !model->destinationEditable() )
321  {
322  editor->setEnabled( false );
323  }
324  else
325  {
326  connect( editor,
327  qOverload<int >( &QComboBox::currentIndexChanged ),
328  this,
329  [ = ]( int currentIndex )
330  {
331  Q_UNUSED( currentIndex )
332  const_cast< QgsFieldMappingWidget::TypeDelegate *>( this )->emit commitData( editor );
333  } );
334  }
335  return editor;
336 }
337 
338 void QgsFieldMappingWidget::TypeDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
339 {
340  QComboBox *editorWidget { qobject_cast<QComboBox *>( editor ) };
341  if ( ! editorWidget )
342  return;
343 
344  const QVariant value = index.model()->data( index, Qt::EditRole );
345  editorWidget->setCurrentIndex( editorWidget->findData( value ) );
346 }
347 
348 void QgsFieldMappingWidget::TypeDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
349 {
350  QComboBox *editorWidget { qobject_cast<QComboBox *>( editor ) };
351  if ( ! editorWidget )
352  return;
353 
354  const QVariant currentValue = editorWidget->currentData( );
355  model->setData( index, currentValue, Qt::EditRole );
356 }
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 type string.
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...
static const QList< QgsVectorDataProvider::NativeType > supportedDataTypes()
Returns a static list of supported data types.
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
static QIcon iconForFieldType(QVariant::Type type, QVariant::Type subType=QVariant::Type::Invalid)
Returns an icon corresponding to a field type.
Definition: qgsfields.cpp:294
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