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