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