QGIS API Documentation  3.37.0-Master (a5b4d9743e8)
qgsattributetabledelegate.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  QgsAttributeTableDelegate.cpp
3  --------------------------------------
4  Date : Feb 2009
5  Copyright : (C) 2009 Vita Cizek
6  Email : weetya (at) gmail.com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include <QItemDelegate>
17 #include <QLineEdit>
18 #include <QComboBox>
19 #include <QPainter>
20 #include <QToolButton>
21 
24 #include "qgsattributetablemodel.h"
25 #include "qgsattributetableview.h"
27 #include "qgseditorwidgetwrapper.h"
29 #include "qgslogger.h"
30 #include "qgsvectordataprovider.h"
31 #include "qgsactionmanager.h"
32 #include "qgsgui.h"
33 #include "qgsvectorlayerjoininfo.h"
35 #include "qgsrendercontext.h"
36 
37 QgsVectorLayer *QgsAttributeTableDelegate::layer( const QAbstractItemModel *model )
38 {
39  const QgsAttributeTableModel *tm = qobject_cast<const QgsAttributeTableModel *>( model );
40  if ( tm )
41  return tm->layer();
42 
43  const QgsAttributeTableFilterModel *fm = qobject_cast<const QgsAttributeTableFilterModel *>( model );
44  if ( fm )
45  return fm->layer();
46 
47  return nullptr;
48 }
49 
50 const QgsAttributeTableModel *QgsAttributeTableDelegate::masterModel( const QAbstractItemModel *model )
51 {
52  const QgsAttributeTableModel *tm = qobject_cast<const QgsAttributeTableModel *>( model );
53  if ( tm )
54  return tm;
55 
56  const QgsAttributeTableFilterModel *fm = qobject_cast<const QgsAttributeTableFilterModel *>( model );
57  if ( fm )
58  return fm->masterModel();
59 
60  return nullptr;
61 }
62 
63 QWidget *QgsAttributeTableDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
64 {
65  Q_UNUSED( option )
66  QgsVectorLayer *vl = layer( index.model() );
67  if ( !vl )
68  return nullptr;
69 
70  const int fieldIdx = index.model()->data( index, static_cast< int >( QgsAttributeTableModel::CustomRole::FieldIndex ) ).toInt();
71  QgsAttributeEditorContext context( masterModel( index.model() )->editorContext(), QgsAttributeEditorContext::Popup );
72 
73  // Update the editor form context with the feature being edited
74  const QgsFeatureId fid( index.model()->data( index, static_cast< int >( QgsAttributeTableModel::CustomRole::FeatureId ) ).toLongLong() );
75  context.setFormFeature( vl->getFeature( fid ) );
76 
77  QgsEditorWidgetWrapper *eww = QgsGui::editorWidgetRegistry()->create( vl, fieldIdx, nullptr, parent, context );
78  QWidget *w = eww->widget();
79 
80  w->setAutoFillBackground( true );
81  w->setFocusPolicy( Qt::StrongFocus ); // to make sure QMouseEvents are propagated to the editor widget
82 
83  const int fieldOrigin = vl->fields().fieldOrigin( fieldIdx );
84  bool readOnly = true;
85  if ( fieldOrigin == QgsFields::OriginJoin )
86  {
87  int srcFieldIndex;
88  const QgsVectorLayerJoinInfo *info = vl->joinBuffer()->joinForFieldIndex( fieldIdx, vl->fields(), srcFieldIndex );
89 
90  if ( info && info->isEditable() )
91  readOnly = info->joinLayer()->editFormConfig().readOnly( srcFieldIndex );
92  }
93  else
94  readOnly = vl->editFormConfig().readOnly( fieldIdx );
95 
96  eww->setEnabled( !readOnly );
97 
98  return w;
99 }
100 
101 void QgsAttributeTableDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
102 {
103  QgsVectorLayer *vl = layer( model );
104  if ( !vl )
105  return;
106 
107  const int fieldIdx = model->data( index, static_cast< int >( QgsAttributeTableModel::CustomRole::FieldIndex ) ).toInt();
108  const QgsFeatureId fid = model->data( index, static_cast< int >( QgsAttributeTableModel::CustomRole::FeatureId ) ).toLongLong();
109  const QVariant oldValue = model->data( index, Qt::EditRole );
110 
112  if ( !eww )
113  return;
114 
115  QList<int> indexes = QList<int>() << fieldIdx;
116  QVariantList newValues = QVariantList() << eww->value();
117  const QStringList additionalFields = eww->additionalFields();
118  for ( const QString &fieldName : additionalFields )
119  {
120  indexes << eww->layer()->fields().lookupField( fieldName );
121  }
122  newValues.append( eww->additionalFieldValues() );
123 
124  if ( ( oldValue != newValues.at( 0 ) && newValues.at( 0 ).isValid() )
125  || QgsVariantUtils::isNull( oldValue ) != QgsVariantUtils::isNull( newValues.at( 0 ) )
126  || newValues.count() > 1 )
127  {
128  // This fixes https://github.com/qgis/QGIS/issues/24398
129  QgsFeatureRequest request( fid );
131  QgsFeature feature;
132  vl->getFeatures( request ).nextFeature( feature );
133  if ( feature.isValid() )
134  {
135  vl->beginEditCommand( tr( "Attribute changed" ) );
136  for ( int i = 0; i < newValues.count(); i++ )
137  vl->changeAttributeValue( fid, indexes.at( i ), newValues.at( i ), feature.attribute( indexes.at( i ) ) );
138  vl->endEditCommand();
139  }
140  }
141 }
142 
143 void QgsAttributeTableDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
144 {
146  if ( !eww )
147  return;
148 
149  const QVariant value = index.model()->data( index, Qt::EditRole );
150  const QStringList additionalFields = eww->additionalFields();
151 
152  if ( !additionalFields.empty() )
153  {
154  const QgsAttributeTableModel *model = masterModel( index.model() );
155  if ( model )
156  {
157  const QgsFeature feat = model->feature( index );
158  QVariantList additionalFieldValues;
159  for ( const QString &fieldName : additionalFields )
160  {
161  additionalFieldValues << feat.attribute( fieldName );
162  }
163  eww->setValues( value, additionalFieldValues );
164  }
165  }
166  else
167  {
168  eww->setValues( value, QVariantList() );
169  }
170 }
171 
173 {
174  mFeatureSelectionModel = featureSelectionModel;
175 }
176 
177 void QgsAttributeTableDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
178 {
179  const QgsAttributeTableFilterModel::ColumnType columnType = static_cast<QgsAttributeTableFilterModel::ColumnType>( index.model()->data( index, static_cast< int >( QgsAttributeTableFilterModel::CustomRole::Type ) ).toInt() );
180 
182  {
183  emit actionColumnItemPainted( index );
184  }
185  else
186  {
187  const QgsFeatureId fid = index.model()->data( index, static_cast< int >( QgsAttributeTableModel::CustomRole::FeatureId ) ).toLongLong();
188 
189  QStyleOptionViewItem myOpt = option;
190 
191  if ( QgsVariantUtils::isNull( index.model()->data( index, Qt::EditRole ) ) )
192  {
193  myOpt.font.setItalic( true );
194  myOpt.palette.setColor( QPalette::Text, QColor( "gray" ) );
195  }
196 
197  if ( mFeatureSelectionModel && mFeatureSelectionModel->isSelected( fid ) )
198  myOpt.state |= QStyle::State_Selected;
199 
200  QItemDelegate::paint( painter, myOpt, index );
201 
202  if ( option.state & QStyle::State_HasFocus )
203  {
204  const QRect r = option.rect.adjusted( 1, 1, -1, -1 );
205  const QPen p( QBrush( QColor( 0, 255, 127 ) ), 2 );
206  const QgsScopedQPainterState painterState( painter );
207  painter->setPen( p );
208  painter->drawRect( r );
209  }
210  }
211 }
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
This class contains context information for attribute editor widgets.
void setFormFeature(const QgsFeature &feature)
Set current feature for the currently edited form or table row.
@ Popup
A widget was opened as a popup (e.g. attribute table editor widget)
void actionColumnItemPainted(const QModelIndex &index) const
Emitted when an action column item is painted.
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override
Sets data from editor back to model.
QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Used to create an editor for when the user tries to change the contents of a cell.
void setEditorData(QWidget *editor, const QModelIndex &index) const override
Sets data from model into the editor.
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Overloads the paint method form the QItemDelegate base class.
void setFeatureSelectionModel(QgsFeatureSelectionModel *featureSelectionModel)
QgsVectorLayer * layer() const
Returns the layer this filter acts on.
@ Type
The type of a given column.
@ ColumnTypeActionButton
This column shows action buttons.
QgsAttributeTableModel * masterModel() const
Returns the table model this filter is using.
A model backed by a QgsVectorLayerCache which is able to provide feature/attribute information to a Q...
QgsFeature feature(const QModelIndex &idx) const
Returns the feature attributes at given model index.
QgsVectorLayer * layer() const
Returns the layer this model uses as backend.
const QgsAttributeEditorContext & editorContext() const
Returns the context in which this table is shown.
@ FeatureId
Get the feature id of the feature in this row.
@ FieldIndex
Get the field index of this column.
bool readOnly(int idx) const
This returns true if the field is manually set to read only or if the field does not support editing ...
QgsEditorWidgetWrapper * create(const QString &widgetId, QgsVectorLayer *vl, int fieldIdx, const QVariantMap &config, QWidget *editor, QWidget *parent, const QgsAttributeEditorContext &context=QgsAttributeEditorContext())
Create an attribute editor widget wrapper of a given type for a given field.
Manages an editor widget Widget and wrapper share the same parent.
virtual QVariant value() const =0
Will be used to access the widget's value.
virtual QVariantList additionalFieldValues() const
Will be used to access the widget's values for potential additional fields handled by the widget.
virtual QStringList additionalFields() const
Returns the list of additional fields which the editor handles.
void setEnabled(bool enabled) override
Is used to enable or disable the edit functionality of the managed widget.
void setValues(const QVariant &value, const QVariantList &additionalValues)
Is called when the value of the widget or additional field values needs to be changed.
static QgsEditorWidgetWrapper * fromWidget(QWidget *widget)
Will return a wrapper for a given widget.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
virtual bool isSelected(QgsFeatureId fid)
Returns the selection status of a given feature id.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:216
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:335
@ OriginJoin
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
Definition: qgsfields.h:52
FieldOrigin fieldOrigin(int fieldIdx) const
Returns the field's origin (value from an enumeration).
Definition: qgsfields.cpp:189
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:359
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories.
Definition: qgsgui.cpp:89
Scoped object for saving and restoring a QPainter object's state.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
const QgsVectorLayerJoinInfo * joinForFieldIndex(int index, const QgsFields &fields, int &sourceFieldIndex) const
Finds the vector join for a layer field index.
Defines left outer join from our vector layer to some other vector layer.
bool isEditable() const
Returns whether joined fields may be edited through the form of the target layer.
QgsVectorLayer * joinLayer() const
Returns joined layer (may be nullptr if the reference was set by layer ID and not resolved yet)
Represents a vector layer which manages a vector based data sets.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
void endEditCommand()
Finish edit command and add it to undo/redo stack.
QgsVectorLayerJoinBuffer * joinBuffer()
Returns the join buffer object.
QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant(), bool skipDefaultValues=false)
Changes an attribute value for a feature (but does not immediately commit the changes).
void beginEditCommand(const QString &text)
Create edit command for undo/redo operations.
QgsEditFormConfig editFormConfig
QWidget * widget()
Access the widget managed by this wrapper.
QgsVectorLayer * layer() const
Returns the vector layer associated with the widget.
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28