QGIS API Documentation 3.27.0-Master (9c08adf5ef)
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
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 QgsFieldMappingExpressionDelegate( this ) );
51 mTableView->setItemDelegateForColumn( static_cast<int>( QgsFieldMappingModel::ColumnDataIndex::DestinationType ), new QgsFieldMappingTypeDelegate( 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
78QList<QgsFieldMappingModel::Field> QgsFieldMappingWidget::mapping() const
79{
80 return model()->mapping();
81}
82
83QMap<QString, QgsProperty> QgsFieldMappingWidget::fieldPropertyMap() const
84{
85 return model()->fieldPropertyMap();
86}
87
88void 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
113void QgsFieldMappingWidget::setDestinationFields( const QgsFields &destinationFields, const QMap<QString, QString> &expressions )
114{
115 model()->setDestinationFields( destinationFields, expressions );
116}
117
118void QgsFieldMappingWidget::scrollTo( const QModelIndex &index ) const
119{
120 mTableView->scrollTo( index );
121}
122
124{
126}
127
128void 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
183void 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
197std::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
214
215//
216// QgsFieldMappingExpressionDelegate
217//
218
219QgsFieldMappingExpressionDelegate::QgsFieldMappingExpressionDelegate( QObject *parent )
220 : QStyledItemDelegate( parent )
221{
222}
223
224void QgsFieldMappingExpressionDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
225{
226 QgsFieldExpressionWidget *editorWidget { qobject_cast<QgsFieldExpressionWidget *>( editor ) };
227 if ( ! editorWidget )
228 return;
229
230 bool isExpression;
231 bool isValid;
232 const QString currentValue { editorWidget->currentField( &isExpression, &isValid ) };
233 if ( isExpression )
234 {
235 model->setData( index, currentValue, Qt::EditRole );
236 }
237 else
238 {
239 model->setData( index, QgsExpression::quotedColumnRef( currentValue ), Qt::EditRole );
240 }
241}
242
243void QgsFieldMappingExpressionDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
244{
245 QgsFieldExpressionWidget *editorWidget { qobject_cast<QgsFieldExpressionWidget *>( editor ) };
246 if ( ! editorWidget )
247 return;
248
249 const QVariant value = index.model()->data( index, Qt::EditRole );
250 editorWidget->setField( value.toString() );
251}
252
253QWidget *QgsFieldMappingExpressionDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
254{
255 Q_UNUSED( option )
257 editor->setAutoFillBackground( true );
258 editor->setAllowEvalErrors( false );
259 if ( const QgsFieldMappingModel *model = qobject_cast<const QgsFieldMappingModel *>( index.model() ) )
260 {
261 editor->registerExpressionContextGenerator( model->contextGenerator() );
262 editor->setFields( model->sourceFields() );
263 }
264 else if ( const QgsAggregateMappingModel *model = qobject_cast<const QgsAggregateMappingModel *>( index.model() ) )
265 {
266 editor->registerExpressionContextGenerator( model->contextGenerator() );
267 editor->setFields( model->sourceFields() );
268 }
269 else
270 {
271 Q_ASSERT( false );
272 }
273
274 if ( QgsFieldMappingWidget *mappingWidget = qobject_cast< QgsFieldMappingWidget *>( QgsFieldMappingExpressionDelegate::parent() ) )
275 {
276 if ( mappingWidget->sourceLayer() )
277 editor->setLayer( mappingWidget->sourceLayer() );
278 }
279 else if ( QgsAggregateMappingWidget *aggregateWidget = qobject_cast< QgsAggregateMappingWidget *>( QgsFieldMappingExpressionDelegate::parent() ) )
280 {
281 if ( aggregateWidget->sourceLayer() )
282 editor->setLayer( aggregateWidget->sourceLayer() );
283 }
284
285 editor->setField( index.model()->data( index, Qt::DisplayRole ).toString() );
286 connect( editor,
287 qOverload<const QString &, bool >( &QgsFieldExpressionWidget::fieldChanged ),
288 this,
289 [ = ]( const QString & fieldName, bool isValid )
290 {
291 Q_UNUSED( fieldName )
292 Q_UNUSED( isValid )
293 const_cast< QgsFieldMappingExpressionDelegate *>( this )->emit commitData( editor );
294 } );
295 return editor;
296}
297
298
299//
300// QgsFieldMappingTypeDelegate
301//
302
303QgsFieldMappingTypeDelegate::QgsFieldMappingTypeDelegate( QObject *parent )
304 : QStyledItemDelegate( parent )
305{
306}
307
308QWidget *QgsFieldMappingTypeDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
309{
310 Q_UNUSED( option )
311 QComboBox *editor = new QComboBox( parent );
312
313 const QList<QgsVectorDataProvider::NativeType> typeList = QgsFieldMappingModel::supportedDataTypes();
314 for ( int i = 0; i < typeList.size(); i++ )
315 {
316 editor->addItem( QgsFields::iconForFieldType( typeList[i].mType, typeList[i].mSubType ), typeList[i].mTypeDesc );
317 editor->setItemData( i, typeList[i].mTypeName, Qt::UserRole );
318 }
319
320 const QgsFieldMappingModel *model { qobject_cast<const QgsFieldMappingModel *>( index.model() ) };
321
322 if ( model && !model->destinationEditable() )
323 {
324 editor->setEnabled( false );
325 }
326 else
327 {
328 connect( editor,
329 qOverload<int >( &QComboBox::currentIndexChanged ),
330 this,
331 [ = ]( int currentIndex )
332 {
333 Q_UNUSED( currentIndex )
334 const_cast< QgsFieldMappingTypeDelegate *>( this )->emit commitData( editor );
335 } );
336 }
337 return editor;
338}
339
340void QgsFieldMappingTypeDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
341{
342 QComboBox *editorWidget { qobject_cast<QComboBox *>( editor ) };
343 if ( ! editorWidget )
344 return;
345
346 const QVariant value = index.model()->data( index, Qt::EditRole );
347 editorWidget->setCurrentIndex( editorWidget->findData( value ) );
348}
349
350void QgsFieldMappingTypeDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
351{
352 QComboBox *editorWidget { qobject_cast<QComboBox *>( editor ) };
353 if ( ! editorWidget )
354 return;
355
356 const QVariant currentValue = editorWidget->currentData( );
357 model->setData( index, currentValue, Qt::EditRole );
358}
359
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