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