QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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
19#include "qgsexpression.h"
21#include "qgsvectorlayer.h"
23#include "QItemSelectionModel"
24
25#include <QTableView>
26#include <QVBoxLayout>
27
28#ifdef ENABLE_MODELTEST
29#include "modeltest.h"
30#endif
31
33 const QgsFields &sourceFields,
34 const QgsFields &destinationFields,
35 const QMap<QString, QString> &expressions )
36 : QgsPanelWidget( parent )
37{
38 QVBoxLayout *verticalLayout = new QVBoxLayout();
39 verticalLayout->setContentsMargins( 0, 0, 0, 0 );
40 mTableView = new QTableView();
41 verticalLayout->addWidget( mTableView );
42 setLayout( verticalLayout );
43
44 mModel = new QgsFieldMappingModel( sourceFields, destinationFields, expressions, this );
45
46#ifdef ENABLE_MODELTEST
47 new ModelTest( mModel, this );
48#endif
49
50 mTableView->setModel( mModel );
51 mTableView->setItemDelegateForColumn( static_cast<int>( QgsFieldMappingModel::ColumnDataIndex::SourceExpression ), new QgsFieldMappingExpressionDelegate( this ) );
52 mTableView->setItemDelegateForColumn( static_cast<int>( QgsFieldMappingModel::ColumnDataIndex::DestinationType ), new QgsFieldMappingTypeDelegate( mTableView ) );
53 updateColumns();
54 // Make sure columns are updated when rows are added
55 connect( mModel, &QgsFieldMappingModel::rowsInserted, this, [ = ] { updateColumns(); } );
56 connect( mModel, &QgsFieldMappingModel::modelReset, this, [ = ] { updateColumns(); } );
57 connect( mModel, &QgsFieldMappingModel::dataChanged, this, &QgsFieldMappingWidget::changed );
58 connect( mModel, &QgsFieldMappingModel::rowsInserted, this, &QgsFieldMappingWidget::changed );
59 connect( mModel, &QgsFieldMappingModel::rowsRemoved, this, &QgsFieldMappingWidget::changed );
60 connect( mModel, &QgsFieldMappingModel::modelReset, this, &QgsFieldMappingWidget::changed );
61}
62
64{
65 qobject_cast<QgsFieldMappingModel *>( mModel )->setDestinationEditable( editable );
66 updateColumns();
67}
68
70{
71 return qobject_cast<QgsFieldMappingModel *>( mModel )->destinationEditable();
72}
73
75{
76 return qobject_cast<QgsFieldMappingModel *>( mModel );
77}
78
79QList<QgsFieldMappingModel::Field> QgsFieldMappingWidget::mapping() const
80{
81 return model()->mapping();
82}
83
84QMap<QString, QgsProperty> QgsFieldMappingWidget::fieldPropertyMap() const
85{
86 return model()->fieldPropertyMap();
87}
88
89void QgsFieldMappingWidget::setFieldPropertyMap( const QMap<QString, QgsProperty> &map )
90{
91 model()->setFieldPropertyMap( map );
92}
93
95{
96 return mTableView->selectionModel();
97}
98
100{
101 model()->setSourceFields( sourceFields );
102}
103
105{
106 mSourceLayer = layer;
107}
108
110{
111 return mSourceLayer;
112}
113
114void QgsFieldMappingWidget::setDestinationFields( const QgsFields &destinationFields, const QMap<QString, QString> &expressions )
115{
116 model()->setDestinationFields( destinationFields, expressions );
117}
118
119void QgsFieldMappingWidget::scrollTo( const QModelIndex &index ) const
120{
121 mTableView->scrollTo( index );
122}
123
125{
127}
128
129void QgsFieldMappingWidget::appendField( const QgsField &field, const QString &expression )
130{
131 model()->appendField( field, expression );
132}
133
135{
136 if ( ! mTableView->selectionModel()->hasSelection() )
137 return false;
138
139 std::list<int> rowsToRemove { selectedRows() };
140 rowsToRemove.reverse();
141 for ( const int row : rowsToRemove )
142 {
143 if ( ! model()->removeField( model()->index( row, 0, QModelIndex() ) ) )
144 {
145 return false;
146 }
147 }
148 return true;
149}
150
152{
153 for ( int i = 0; i < mTableView->model()->rowCount(); ++i )
154 {
155 for ( int j = 0; j < mTableView->model()->columnCount(); j++ )
156 {
157 QModelIndex index = mTableView->model()->index( i, j );
158 mTableView->selectionModel()->select( index, QItemSelectionModel::Toggle );
159 }
160 }
161}
162
164{
165 if ( ! mTableView->selectionModel()->hasSelection() )
166 return false;
167
168 const std::list<int> rowsToMoveUp { selectedRows() };
169 for ( const int row : rowsToMoveUp )
170 {
171 if ( ! model()->moveUp( model()->index( row, 0, QModelIndex() ) ) )
172 {
173 return false;
174 }
175 }
176 return true;
177}
178
180{
181 if ( ! mTableView->selectionModel()->hasSelection() )
182 return false;
183
184 std::list<int> rowsToMoveDown { selectedRows() };
185 rowsToMoveDown.reverse();
186 for ( const int row : rowsToMoveDown )
187 {
188 if ( ! model()->moveDown( model()->index( row, 0, QModelIndex() ) ) )
189 {
190 return false;
191 }
192 }
193 return true;
194}
195
196void QgsFieldMappingWidget::updateColumns()
197{
198 for ( int i = 0; i < mModel->rowCount(); ++i )
199 {
200 mTableView->openPersistentEditor( mModel->index( i, static_cast<int>( QgsFieldMappingModel::ColumnDataIndex::SourceExpression ) ) );
201 mTableView->openPersistentEditor( mModel->index( i, static_cast<int>( QgsFieldMappingModel::ColumnDataIndex::DestinationType ) ) );
202 }
203
204 for ( int i = 0; i < mModel->columnCount(); ++i )
205 {
206 mTableView->resizeColumnToContents( i );
207 }
208}
209
210std::list<int> QgsFieldMappingWidget::selectedRows()
211{
212 std::list<int> rows;
213 if ( mTableView->selectionModel()->hasSelection() )
214 {
215 const QModelIndexList constSelection { mTableView->selectionModel()->selectedIndexes() };
216 for ( const QModelIndex &index : constSelection )
217 {
218 rows.push_back( index.row() );
219 }
220 rows.sort();
221 rows.unique();
222 }
223 return rows;
224}
225
227
228//
229// QgsFieldMappingExpressionDelegate
230//
231
232QgsFieldMappingExpressionDelegate::QgsFieldMappingExpressionDelegate( QObject *parent )
233 : QStyledItemDelegate( parent )
234{
235}
236
237void QgsFieldMappingExpressionDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
238{
239 QgsFieldExpressionWidget *editorWidget { qobject_cast<QgsFieldExpressionWidget *>( editor ) };
240 if ( ! editorWidget )
241 return;
242
243 bool isExpression;
244 bool isValid;
245 const QString currentValue { editorWidget->currentField( &isExpression, &isValid ) };
246 if ( isExpression )
247 {
248 model->setData( index, currentValue, Qt::EditRole );
249 }
250 else
251 {
252 model->setData( index, QgsExpression::quotedColumnRef( currentValue ), Qt::EditRole );
253 }
254}
255
256void QgsFieldMappingExpressionDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
257{
258 QgsFieldExpressionWidget *editorWidget { qobject_cast<QgsFieldExpressionWidget *>( editor ) };
259 if ( ! editorWidget )
260 return;
261
262 const QVariant value = index.model()->data( index, Qt::EditRole );
263 editorWidget->setField( value.toString() );
264}
265
266QWidget *QgsFieldMappingExpressionDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
267{
268 Q_UNUSED( option )
270 editor->setAutoFillBackground( true );
271 editor->setAllowEvalErrors( false );
272 if ( const QgsFieldMappingModel *model = qobject_cast<const QgsFieldMappingModel *>( index.model() ) )
273 {
274 editor->registerExpressionContextGenerator( model->contextGenerator() );
275 editor->setFields( model->sourceFields() );
276 }
277 else if ( const QgsAggregateMappingModel *model = qobject_cast<const QgsAggregateMappingModel *>( index.model() ) )
278 {
279 editor->registerExpressionContextGenerator( model->contextGenerator() );
280 editor->setFields( model->sourceFields() );
281 }
282 else
283 {
284 Q_ASSERT( false );
285 }
286
287 if ( QgsFieldMappingWidget *mappingWidget = qobject_cast< QgsFieldMappingWidget *>( QgsFieldMappingExpressionDelegate::parent() ) )
288 {
289 if ( mappingWidget->sourceLayer() )
290 editor->setLayer( mappingWidget->sourceLayer() );
291 }
292 else if ( QgsAggregateMappingWidget *aggregateWidget = qobject_cast< QgsAggregateMappingWidget *>( QgsFieldMappingExpressionDelegate::parent() ) )
293 {
294 if ( aggregateWidget->sourceLayer() )
295 editor->setLayer( aggregateWidget->sourceLayer() );
296 }
297
298 editor->setField( index.model()->data( index, Qt::DisplayRole ).toString() );
299 connect( editor,
300 qOverload<const QString & >( &QgsFieldExpressionWidget::fieldChanged ),
301 this,
302 [ = ]( const QString & fieldName )
303 {
304 Q_UNUSED( fieldName )
305 const_cast< QgsFieldMappingExpressionDelegate *>( this )->emit commitData( editor );
306 } );
307 return editor;
308}
309
310
311//
312// QgsFieldMappingTypeDelegate
313//
314
315QgsFieldMappingTypeDelegate::QgsFieldMappingTypeDelegate( QObject *parent )
316 : QStyledItemDelegate( parent )
317{
318}
319
320QWidget *QgsFieldMappingTypeDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
321{
322 Q_UNUSED( option )
323 QComboBox *editor = new QComboBox( parent );
324
325 const QList<QgsVectorDataProvider::NativeType> typeList = QgsFieldMappingModel::supportedDataTypes();
326 for ( int i = 0; i < typeList.size(); i++ )
327 {
328 editor->addItem( QgsFields::iconForFieldType( typeList[i].mType, typeList[i].mSubType, typeList[i].mTypeName ), typeList[i].mTypeDesc );
329 editor->setItemData( i, typeList[i].mTypeName, Qt::UserRole );
330 }
331
332 const QgsFieldMappingModel *model { qobject_cast<const QgsFieldMappingModel *>( index.model() ) };
333
334 if ( model && !model->destinationEditable() )
335 {
336 editor->setEnabled( false );
337 }
338 else
339 {
340 connect( editor,
341 qOverload<int >( &QComboBox::currentIndexChanged ),
342 this,
343 [ = ]( int currentIndex )
344 {
345 Q_UNUSED( currentIndex )
346 const_cast< QgsFieldMappingTypeDelegate *>( this )->emit commitData( editor );
347 } );
348 }
349 return editor;
350}
351
352void QgsFieldMappingTypeDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
353{
354 QComboBox *editorWidget { qobject_cast<QComboBox *>( editor ) };
355 if ( ! editorWidget )
356 return;
357
358 const QVariant value = index.model()->data( index, Qt::EditRole );
359 editorWidget->setCurrentIndex( editorWidget->findData( value ) );
360}
361
362void QgsFieldMappingTypeDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
363{
364 QComboBox *editorWidget { qobject_cast<QComboBox *>( editor ) };
365 if ( ! editorWidget )
366 return;
367
368 const QVariant currentValue = editorWidget->currentData( );
369 model->setData( index, currentValue, Qt::EditRole );
370}
371
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 creates 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.
void invertSelection()
Invert the field selection state.
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:53
Container of fields for a vector layer.
Definition: qgsfields.h:45
static QIcon iconForFieldType(QVariant::Type type, QVariant::Type subType=QVariant::Type::Invalid, const QString &typeString=QString())
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.