QGIS API Documentation
qgsattributetableview.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  QgsAttributeTableView.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 <QKeyEvent>
17 #include <QSettings>
18 #include <QHeaderView>
19 #include <QMenu>
20 #include <QToolButton>
21 #include <QHBoxLayout>
22 
23 #include "qgsactionmanager.h"
24 #include "qgsattributetableview.h"
25 #include "qgsattributetablemodel.h"
28 #include "qgsvectorlayer.h"
29 #include "qgsvectorlayercache.h"
31 #include "qgsvectordataprovider.h"
32 #include "qgslogger.h"
33 #include "qgsmapcanvas.h"
35 
37  : QTableView( parent )
38  , mMasterModel( nullptr )
39  , mFilterModel( nullptr )
40  , mFeatureSelectionModel( nullptr )
41  , mFeatureSelectionManager( nullptr )
42  , mModel( nullptr )
43  , mActionPopup( nullptr )
44  , mRowSectionAnchor( 0 )
45  , mCtrlDragSelectionFlag( QItemSelectionModel::Select )
46  , mActionWidget( nullptr )
47 {
48  QSettings settings;
49  restoreGeometry( settings.value( "/BetterAttributeTable/geometry" ).toByteArray() );
50 
51  //verticalHeader()->setDefaultSectionSize( 20 );
53 
54  // We need mouse move events to create the action button on hover
55  setMouseTracking( true );
56 
57  mTableDelegate = new QgsAttributeTableDelegate( this );
58  setItemDelegate( mTableDelegate );
59 
60  setSelectionBehavior( QAbstractItemView::SelectRows );
61  setSelectionMode( QAbstractItemView::ExtendedSelection );
62  setSortingEnabled( true ); // At this point no data is in the model yet, so actually nothing is sorted.
63  horizontalHeader()->setSortIndicatorShown( false ); // So hide the indicator to avoid confusion.
64 
66 
67  connect( verticalHeader(), SIGNAL( sectionPressed( int ) ), this, SLOT( selectRow( int ) ) );
68  connect( verticalHeader(), SIGNAL( sectionEntered( int ) ), this, SLOT( _q_selectRow( int ) ) );
69  connect( horizontalHeader(), SIGNAL( sectionResized( int, int, int ) ), this, SLOT( columnSizeChanged( int, int, int ) ) );
70  connect( horizontalHeader(), SIGNAL( sortIndicatorChanged( int, Qt::SortOrder ) ), this, SLOT( showHorizontalSortIndicator() ) );
71 }
72 
74 {
75  if ( object == verticalHeader()->viewport() )
76  {
77  switch ( event->type() )
78  {
79  case QEvent::MouseButtonPress:
80  mFeatureSelectionModel->enableSync( false );
81  break;
82 
83  case QEvent::MouseButtonRelease:
84  mFeatureSelectionModel->enableSync( true );
85  break;
86 
87  default:
88  break;
89  }
90  }
91  return false;
92 }
93 
95 {
96  if ( mFilterModel )
97  {
98  // Cleanup old model stuff if present
99  disconnect( mFilterModel, SIGNAL( filterAboutToBeInvalidated() ), this, SLOT( onFilterAboutToBeInvalidated() ) );
100  disconnect( mFilterModel, SIGNAL( filterInvalidated() ), this, SLOT( onFilterInvalidated() ) );
101  }
102 
103  mFilterModel = filterModel;
104  QTableView::setModel( filterModel );
105 
106  connect( mFilterModel, SIGNAL( destroyed() ), this, SLOT( modelDeleted() ) );
107 
108  delete mFeatureSelectionModel;
109  mFeatureSelectionModel = nullptr;
110 
111  if ( filterModel )
112  {
113  if ( !mFeatureSelectionManager )
114  {
115  mFeatureSelectionManager = new QgsVectorLayerSelectionManager( mFilterModel->layer(), mFilterModel );
116  }
117 
118  mFeatureSelectionModel = new QgsFeatureSelectionModel( mFilterModel, mFilterModel, mFeatureSelectionManager, mFilterModel );
119  setSelectionModel( mFeatureSelectionModel );
120  mTableDelegate->setFeatureSelectionModel( mFeatureSelectionModel );
121  connect( mFeatureSelectionModel, SIGNAL( requestRepaint( QModelIndexList ) ), this, SLOT( repaintRequested( QModelIndexList ) ) );
122  connect( mFeatureSelectionModel, SIGNAL( requestRepaint() ), this, SLOT( repaintRequested() ) );
123  }
124 
125  mActionWidget = createActionWidget( 0 );
126  mActionWidget->setVisible( false );
127  updateActionImage( mActionWidget );
128 }
129 
131 {
132  if ( mFeatureSelectionManager )
133  delete mFeatureSelectionManager;
134 
135  mFeatureSelectionManager = featureSelectionManager;
136 
137  if ( mFeatureSelectionModel )
138  mFeatureSelectionModel->setFeatureSelectionManager( mFeatureSelectionManager );
139 }
140 
141 QWidget* QgsAttributeTableView::createActionWidget( QgsFeatureId fid )
142 {
143  QgsAttributeTableConfig attributeTableConfig = mFilterModel->layer()->attributeTableConfig();
144  QgsActionManager* actions = mFilterModel->layer()->actions();
145 
146  QToolButton* toolButton = nullptr;
147  QWidget* container = nullptr;
148 
149  if ( attributeTableConfig.actionWidgetStyle() == QgsAttributeTableConfig::DropDown )
150  {
151  toolButton = new QToolButton( this );
152  toolButton->setPopupMode( QToolButton::MenuButtonPopup );
153  container = toolButton;
154  }
155  else
156  {
157  container = new QWidget( this );
158  container->setLayout( new QHBoxLayout() );
159  container->layout()->setMargin( 0 );
160  }
161 
162  for ( int i = 0; i < actions->size(); ++i )
163  {
164  const QgsAction& action = actions->at( i );
165 
166  if ( !action.showInAttributeTable() )
167  continue;
168 
169  QAction* act = new QAction( action.icon(), action.shortTitle().isEmpty() ? action.name() : action.shortTitle(), toolButton );
170  act->setToolTip( action.name() );
171  act->setData( i );
172  act->setProperty( "fid", fid );
173 
174  connect( act, SIGNAL( triggered( bool ) ), this, SLOT( actionTriggered() ) );
175 
176  if ( attributeTableConfig.actionWidgetStyle() == QgsAttributeTableConfig::DropDown )
177  {
178  toolButton->addAction( act );
179 
180  if ( actions->defaultAction() == i )
181  toolButton->setDefaultAction( act );
182 
183  container = toolButton;
184  }
185  else
186  {
187  QToolButton* btn = new QToolButton;
188  btn->setDefaultAction( act );
189  container->layout()->addWidget( btn );
190  }
191  }
192 
193  if ( toolButton && !toolButton->actions().isEmpty() && actions->defaultAction() == -1 )
194  toolButton->setDefaultAction( toolButton->actions().first() );
195 
196  return container;
197 }
198 
200 {
201  Q_UNUSED( e );
202  QSettings settings;
203  settings.setValue( "/BetterAttributeTable/geometry", QVariant( saveGeometry() ) );
204 }
205 
207 {
208  setSelectionMode( QAbstractItemView::NoSelection );
210  setSelectionMode( QAbstractItemView::ExtendedSelection );
211 }
212 
214 {
215  setSelectionMode( QAbstractItemView::NoSelection );
217  setSelectionMode( QAbstractItemView::ExtendedSelection );
218 }
219 
221 {
222  QModelIndex index = indexAt( event->pos() );
224  {
225  Q_ASSERT( index.isValid() );
226 
227  if ( !indexWidget( index ) )
228  setIndexWidget( index, createActionWidget( mFilterModel->data( index, QgsAttributeTableModel::FeatureIdRole ).toLongLong() ) );
229  }
230 
231  setSelectionMode( QAbstractItemView::NoSelection );
233  setSelectionMode( QAbstractItemView::ExtendedSelection );
234 }
235 
237 {
238  switch ( event->key() )
239  {
240 
241  // Default Qt behavior would be to change the selection.
242  // We don't make it that easy for the user to trash his selection.
243  case Qt::Key_Up:
244  case Qt::Key_Down:
245  case Qt::Key_Left:
246  case Qt::Key_Right:
247  setSelectionMode( QAbstractItemView::NoSelection );
248  QTableView::keyPressEvent( event );
249  setSelectionMode( QAbstractItemView::ExtendedSelection );
250  break;
251 
252  default:
253  QTableView::keyPressEvent( event );
254  break;
255  }
256 }
257 
258 void QgsAttributeTableView::repaintRequested( const QModelIndexList& indexes )
259 {
260  Q_FOREACH ( const QModelIndex& index, indexes )
261  {
262  update( index );
263  }
264 }
265 
267 {
268  setDirtyRegion( viewport()->rect() );
269 }
270 
272 {
273  QItemSelection selection;
274  selection.append( QItemSelectionRange( mFilterModel->index( 0, 0 ), mFilterModel->index( mFilterModel->rowCount() - 1, 0 ) ) );
275  mFeatureSelectionModel->selectFeatures( selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
276 }
277 
279 {
280  delete mActionPopup;
281  mActionPopup = nullptr;
282 
283  QModelIndex idx = indexAt( event->pos() );
284  if ( !idx.isValid() )
285  {
286  return;
287  }
288 
289  QgsVectorLayer *vlayer = mFilterModel->layer();
290  if ( !vlayer )
291  return;
292 
293  mActionPopup = new QMenu( this );
294 
295  mActionPopup->addAction( tr( "Select All" ), this, SLOT( selectAll() ), QKeySequence::SelectAll );
296 
297  // let some other parts of the application add some actions
298  emit willShowContextMenu( mActionPopup, idx );
299 
300  if ( !mActionPopup->actions().isEmpty() )
301  {
302  mActionPopup->popup( event->globalPos() );
303  }
304 }
305 
307 {
308  selectRow( row, true );
309 }
310 
312 {
313  selectRow( row, false );
314 }
315 
316 void QgsAttributeTableView::modelDeleted()
317 {
318  mFilterModel = nullptr;
319  mFeatureSelectionManager = nullptr;
320  mFeatureSelectionModel = nullptr;
321 }
322 
323 void QgsAttributeTableView::selectRow( int row, bool anchor )
324 {
325  if ( selectionBehavior() == QTableView::SelectColumns
326  || ( selectionMode() == QTableView::SingleSelection
327  && selectionBehavior() == QTableView::SelectItems ) )
328  return;
329 
330  if ( row >= 0 && row < model()->rowCount() )
331  {
332  int column = horizontalHeader()->logicalIndexAt( isRightToLeft() ? viewport()->width() : 0 );
333  QModelIndex index = model()->index( row, column );
335  selectionModel()->setCurrentIndex( index, QItemSelectionModel::NoUpdate );
336  if (( anchor && !( command & QItemSelectionModel::Current ) )
337  || ( selectionMode() == QTableView::SingleSelection ) )
338  mRowSectionAnchor = row;
339 
340  if ( selectionMode() != QTableView::SingleSelection
341  && command.testFlag( QItemSelectionModel::Toggle ) )
342  {
343  if ( anchor )
344  mCtrlDragSelectionFlag = mFeatureSelectionModel->isSelected( index )
345  ? QItemSelectionModel::Deselect : QItemSelectionModel::Select;
346  command &= ~QItemSelectionModel::Toggle;
347  command |= mCtrlDragSelectionFlag;
348  if ( !anchor )
349  command |= QItemSelectionModel::Current;
350  }
351 
352  QModelIndex tl = model()->index( qMin( mRowSectionAnchor, row ), 0 );
353  QModelIndex br = model()->index( qMax( mRowSectionAnchor, row ), model()->columnCount() - 1 );
354  if ( verticalHeader()->sectionsMoved() && tl.row() != br.row() )
355  setSelection( visualRect( tl ) | visualRect( br ), command );
356  else
357  mFeatureSelectionModel->selectFeatures( QItemSelection( tl, br ), command );
358  }
359 }
360 
361 void QgsAttributeTableView::showHorizontalSortIndicator()
362 {
364 }
365 
366 void QgsAttributeTableView::actionTriggered()
367 {
368  QAction* action = qobject_cast<QAction*>( sender() );
369  QgsFeatureId fid = action->property( "fid" ).toLongLong();
370 
371  QgsFeature f;
372  mFilterModel->layerCache()->getFeatures( QgsFeatureRequest( fid ) ).nextFeature( f );
373 
374  mFilterModel->layer()->actions()->doAction( action->data().toInt(), f );
375 }
376 
377 void QgsAttributeTableView::columnSizeChanged( int index, int oldWidth, int newWidth )
378 {
379  Q_UNUSED( oldWidth )
380  if ( mFilterModel->actionColumnIndex() == index )
381  {
382  mActionWidget->resize( newWidth, mActionWidget->height() );
383  updateActionImage( mActionWidget );
384  }
385 }
386 
387 void QgsAttributeTableView::updateActionImage( QWidget* widget )
388 {
389  QImage image( widget->size(), QImage::Format_ARGB32_Premultiplied );
390  QPainter painter( &image );
391  widget->render( &painter );
392  mTableDelegate->setActionWidgetImage( image );
393 }
QLayout * layout() const
bool showInAttributeTable() const
Wheter this action should be shown on the attribute table.
Definition: qgsaction.h:118
qlonglong toLongLong(bool *ok) const
QgsActionManager * actions()
Get all layer actions defined on this layer.
void setDirtyRegion(const QRegion &region)
virtual QVariant data(const QModelIndex &index, int role) const override
QByteArray toByteArray() const
static unsigned index
const QgsAction & at(int idx) const
Get the action at the specified index.
virtual bool isSelected(QgsFeatureId fid)
Returns the selection status of a given feature id.
Type type() const
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const =0
void willShowContextMenu(QMenu *menu, const QModelIndex &atIndex)
Is emitted, in order to provide a hook to add aditional menu entries to the context menu...
int size() const
Get the number of actions managed by this.
virtual void setSelection(const QRect &rect, QFlags< QItemSelectionModel::SelectionFlag > flags)
void setSelectionMode(QAbstractItemView::SelectionMode mode)
QWidget * indexWidget(const QModelIndex &index) const
int actionColumnIndex() const
Get the index of the first column that contains an action widget.
QgsAttributeTableView(QWidget *parent=nullptr)
QItemSelectionModel * selectionModel() const
void addAction(QAction *action)
void setDefaultAction(QAction *action)
QObject * sender() const
QVariant data() const
void setHighlightSections(bool highlight)
void addAction(QAction *action)
void setSortingEnabled(bool enable)
virtual void setVisible(bool visible)
QWidget * viewport() const
void setSortIndicatorShown(bool show)
QHeaderView * verticalHeader() const
virtual bool eventFilter(QObject *object, QEvent *event) override
This event filter is installed on the verticalHeader to intercept mouse press and release events...
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
void setSelectionBehavior(QAbstractItemView::SelectionBehavior behavior)
ActionWidgetStyle actionWidgetStyle() const
Get the style of the action widget.
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
virtual void selectAll() override
const QPoint & globalPos() const
QString tr(const char *sourceText, const char *disambiguation, int n)
void enableSync(bool enable)
Enables or disables synchronisation to the QgsVectorLayer When synchronisation is disabled...
virtual void mouseReleaseEvent(QMouseEvent *event)
void update()
virtual int rowCount(const QModelIndex &parent) const
void setFeatureSelectionModel(QgsFeatureSelectionModel *featureSelectionModel)
void setToolTip(const QString &tip)
void mouseReleaseEvent(QMouseEvent *event) override
Called for mouse release events on a table cell.
Get the feature id of the feature in this row.
int width() const
QgsVectorLayer * layer() const
Returns the layer this filter acts on.
void setValue(const QString &key, const QVariant &value)
void resize(int w, int h)
virtual QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
bool isValid() const
virtual QModelIndex indexAt(const QPoint &pos) const
QString name() const
The name of the action. This may be a longer description.
Definition: qgsaction.h:97
void append(const T &value)
virtual void setModel(QAbstractItemModel *model)
virtual void setSelectionModel(QItemSelectionModel *selectionModel)
QVariant property(const char *name) const
void setLayout(QLayout *layout)
void installEventFilter(QObject *filterObj)
QgsVectorLayerCache * layerCache() const
Returns the layerCache this filter acts on.
void popup(const QPoint &p, QAction *atAction)
int toInt(bool *ok) const
void doAction(int index, const QgsFeature &feat, int defaultValueIndex=0)
Does the given values.
bool restoreGeometry(const QByteArray &geometry)
Utility class that encapsulates an action based on vector attributes.
Definition: qgsaction.h:25
bool isEmpty() const
void setItemDelegate(QAbstractItemDelegate *delegate)
int row() const
void mousePressEvent(QMouseEvent *event) override
Called for mouse press events on a table cell.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
virtual void setFeatureSelectionManager(QgsIFeatureSelectionManager *featureSelectionManager)
Storage and management of actions associated with a layer.
void setMargin(int margin)
virtual void setModel(QgsAttributeTableFilterModel *filterModel)
void addWidget(QWidget *w)
QRect rect() const
void setData(const QVariant &userData)
int key() const
void mouseMoveEvent(QMouseEvent *event) override
Called for mouse move events on a table cell.
virtual QRect visualRect(const QModelIndex &index) const =0
virtual void mouseMoveEvent(QMouseEvent *event)
virtual QItemSelectionModel::SelectionFlags selectionCommand(const QModelIndex &index, const QEvent *event) const
int logicalIndexAt(int position) const
QVariant value(const QString &key, const QVariant &defaultValue) const
virtual bool event(QEvent *event)
virtual void selectFeatures(const QItemSelection &selection, const SelectionFlags &command)
Select features on this table.
const QPoint & pos() const
QVariant data(int role) const
QByteArray saveGeometry() const
QString shortTitle() const
The short title is used to label user interface elements like buttons.
Definition: qgsaction.h:100
virtual void mousePressEvent(QMouseEvent *event)
A tool button with a dropdown to select the current action.
void setFeatureSelectionManager(QgsIFeatureSelectionManager *featureSelectionManager)
setFeatureSelectionManager
QWidget(QWidget *parent, QFlags< Qt::WindowType > f)
void setPopupMode(ToolButtonPopupMode mode)
void setIndexWidget(const QModelIndex &index, QWidget *widget)
A delegate item class for QgsAttributeTable (see Qt documentation for QItemDelegate).
void setMouseTracking(bool enable)
qint64 QgsFeatureId
Definition: qgsfeature.h:31
virtual void selectRow(int row)
void setCurrentIndex(const QModelIndex &index, QFlags< QItemSelectionModel::SelectionFlag > command)
bool setProperty(const char *name, const QVariant &value)
virtual void keyPressEvent(QKeyEvent *event)
virtual void _q_selectRow(int row)
const QPoint & pos() const
bool nextFeature(QgsFeature &f)
This is a container for configuration of the attribute table.
QAbstractItemModel * model() const
void setActionWidgetImage(const QImage &image)
Set an image that represents an action widget.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QList< QAction * > actions() const
Is an interface class to abstract feature selection handling.
void closeEvent(QCloseEvent *event) override
Saves geometry to the settings on close.
Represents a vector layer which manages a vector based data sets.
QgsAttributeTableConfig attributeTableConfig() const
Get the attribute table configuration object.
QHeaderView * horizontalHeader() const
QgsFeatureIterator getFeatures(const QgsFeatureRequest &featureRequest=QgsFeatureRequest())
Query this VectorLayerCache for features.
int defaultAction() const
Get the index of the default action.
void destroyed(QObject *obj)
void keyPressEvent(QKeyEvent *event) override
Called for key press events Disables selection change by only pressing an arrow key.
void contextMenuEvent(QContextMenuEvent *event) override
Is called when the context menu will be shown.
QIcon icon() const
The icon.
Definition: qgsaction.h:106
void render(QPaintDevice *target, const QPoint &targetOffset, const QRegion &sourceRegion, QFlags< QWidget::RenderFlag > renderFlags)