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