QGIS API Documentation  2.15.0-Master (5f66276)
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  , mFilterModel( nullptr )
39  , mFeatureSelectionModel( nullptr )
40  , mFeatureSelectionManager( nullptr )
41  , mActionPopup( nullptr )
42  , mRowSectionAnchor( 0 )
43  , mCtrlDragSelectionFlag( QItemSelectionModel::Select )
44 {
45  QSettings settings;
46  restoreGeometry( settings.value( "/BetterAttributeTable/geometry" ).toByteArray() );
47 
48  //verticalHeader()->setDefaultSectionSize( 20 );
50 
51  // We need mouse move events to create the action button on hover
52  mTableDelegate = new QgsAttributeTableDelegate( this );
53  setItemDelegate( mTableDelegate );
54 
55  setEditTriggers( QAbstractItemView::AllEditTriggers );
56 
57  setSelectionBehavior( QAbstractItemView::SelectRows );
58  setSelectionMode( QAbstractItemView::ExtendedSelection );
59  setSortingEnabled( true ); // At this point no data is in the model yet, so actually nothing is sorted.
60  horizontalHeader()->setSortIndicatorShown( false ); // So hide the indicator to avoid confusion.
61 
63 
64  connect( verticalHeader(), SIGNAL( sectionPressed( int ) ), this, SLOT( selectRow( int ) ) );
65  connect( verticalHeader(), SIGNAL( sectionEntered( int ) ), this, SLOT( _q_selectRow( int ) ) );
66  connect( horizontalHeader(), SIGNAL( sectionResized( int, int, int ) ), this, SLOT( columnSizeChanged( int, int, int ) ) );
67  connect( horizontalHeader(), SIGNAL( sortIndicatorChanged( int, Qt::SortOrder ) ), this, SLOT( showHorizontalSortIndicator() ) );
68 }
69 
71 {
72  if ( object == verticalHeader()->viewport() )
73  {
74  switch ( event->type() )
75  {
76  case QEvent::MouseButtonPress:
77  mFeatureSelectionModel->enableSync( false );
78  break;
79 
80  case QEvent::MouseButtonRelease:
81  mFeatureSelectionModel->enableSync( true );
82  break;
83 
84  default:
85  break;
86  }
87  }
88  return false;
89 }
90 
92 {
93  int i = 0;
94  Q_FOREACH ( const QgsAttributeTableConfig::ColumnConfig& columnConfig, config.columns() )
95  {
96  if ( columnConfig.hidden )
97  continue;
98 
99  if ( columnConfig.width >= 0 )
100  {
101  setColumnWidth( i, columnConfig.width );
102  }
103  else
104  {
105  setColumnWidth( i, horizontalHeader()->defaultSectionSize() );
106  }
107  i++;
108  }
109 }
110 
112 {
113  mFilterModel = filterModel;
114  QTableView::setModel( filterModel );
115 
116  if ( mFilterModel )
117  {
118  connect( mFilterModel, SIGNAL( destroyed() ), this, SLOT( modelDeleted() ) );
119  connect( mTableDelegate, SIGNAL( actionColumnItemPainted( QModelIndex ) ), this, SLOT( onActionColumnItemPainted( QModelIndex ) ) );
120  }
121 
122  delete mFeatureSelectionModel;
123  mFeatureSelectionModel = nullptr;
124 
125  if ( filterModel )
126  {
127  if ( !mFeatureSelectionManager )
128  {
129  mFeatureSelectionManager = new QgsVectorLayerSelectionManager( mFilterModel->layer(), mFilterModel );
130  }
131 
132  mFeatureSelectionModel = new QgsFeatureSelectionModel( mFilterModel, mFilterModel, mFeatureSelectionManager, mFilterModel );
133  setSelectionModel( mFeatureSelectionModel );
134  mTableDelegate->setFeatureSelectionModel( mFeatureSelectionModel );
135  connect( mFeatureSelectionModel, SIGNAL( requestRepaint( QModelIndexList ) ), this, SLOT( repaintRequested( QModelIndexList ) ) );
136  connect( mFeatureSelectionModel, SIGNAL( requestRepaint() ), this, SLOT( repaintRequested() ) );
137  }
138 }
139 
141 {
142  if ( mFeatureSelectionManager )
143  delete mFeatureSelectionManager;
144 
145  mFeatureSelectionManager = featureSelectionManager;
146 
147  if ( mFeatureSelectionModel )
148  mFeatureSelectionModel->setFeatureSelectionManager( mFeatureSelectionManager );
149 }
150 
151 QWidget* QgsAttributeTableView::createActionWidget( QgsFeatureId fid )
152 {
153  QgsAttributeTableConfig attributeTableConfig = mFilterModel->layer()->attributeTableConfig();
154  QgsActionManager* actions = mFilterModel->layer()->actions();
155 
156  QToolButton* toolButton = nullptr;
157  QWidget* container = nullptr;
158 
159  if ( attributeTableConfig.actionWidgetStyle() == QgsAttributeTableConfig::DropDown )
160  {
161  toolButton = new QToolButton();
162  toolButton->setPopupMode( QToolButton::MenuButtonPopup );
163  container = toolButton;
164  }
165  else
166  {
167  container = new QWidget();
168  container->setLayout( new QHBoxLayout() );
169  container->layout()->setMargin( 0 );
170  }
171 
172  for ( int i = 0; i < actions->size(); ++i )
173  {
174  const QgsAction& action = actions->at( i );
175 
176  if ( !action.showInAttributeTable() )
177  continue;
178 
179  QString actionTitle = !action.shortTitle().isEmpty() ? action.shortTitle() : action.icon().isNull() ? action.name() : "";
180  QAction* act = new QAction( action.icon(), actionTitle, container );
181  act->setToolTip( action.name() );
182  act->setData( i );
183  act->setProperty( "fid", fid );
184 
185  connect( act, SIGNAL( triggered( bool ) ), this, SLOT( actionTriggered() ) );
186 
187  if ( attributeTableConfig.actionWidgetStyle() == QgsAttributeTableConfig::DropDown )
188  {
189  toolButton->addAction( act );
190 
191  if ( actions->defaultAction() == i )
192  toolButton->setDefaultAction( act );
193 
194  container = toolButton;
195  }
196  else
197  {
198  QToolButton* btn = new QToolButton;
199  btn->setDefaultAction( act );
200  container->layout()->addWidget( btn );
201  }
202  }
203 
204  if ( toolButton && !toolButton->actions().isEmpty() && actions->defaultAction() == -1 )
205  toolButton->setDefaultAction( toolButton->actions().at( 0 ) );
206 
207  return container;
208 }
209 
211 {
212  Q_UNUSED( e );
213  QSettings settings;
214  settings.setValue( "/BetterAttributeTable/geometry", QVariant( saveGeometry() ) );
215 }
216 
218 {
219  setSelectionMode( QAbstractItemView::NoSelection );
221  setSelectionMode( QAbstractItemView::ExtendedSelection );
222 }
223 
225 {
226  setSelectionMode( QAbstractItemView::NoSelection );
228  setSelectionMode( QAbstractItemView::ExtendedSelection );
229 }
230 
232 {
233  setSelectionMode( QAbstractItemView::NoSelection );
235  setSelectionMode( QAbstractItemView::ExtendedSelection );
236 }
237 
239 {
240  switch ( event->key() )
241  {
242 
243  // Default Qt behavior would be to change the selection.
244  // We don't make it that easy for the user to trash his selection.
245  case Qt::Key_Up:
246  case Qt::Key_Down:
247  case Qt::Key_Left:
248  case Qt::Key_Right:
249  setSelectionMode( QAbstractItemView::NoSelection );
250  QTableView::keyPressEvent( event );
251  setSelectionMode( QAbstractItemView::ExtendedSelection );
252  break;
253 
254  default:
255  QTableView::keyPressEvent( event );
256  break;
257  }
258 }
259 
260 void QgsAttributeTableView::repaintRequested( const QModelIndexList& indexes )
261 {
262  Q_FOREACH ( const QModelIndex& index, indexes )
263  {
264  update( index );
265  }
266 }
267 
269 {
270  setDirtyRegion( viewport()->rect() );
271 }
272 
274 {
275  QItemSelection selection;
276  selection.append( QItemSelectionRange( mFilterModel->index( 0, 0 ), mFilterModel->index( mFilterModel->rowCount() - 1, 0 ) ) );
277  mFeatureSelectionModel->selectFeatures( selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
278 }
279 
281 {
282  delete mActionPopup;
283  mActionPopup = nullptr;
284 
285  QModelIndex idx = indexAt( event->pos() );
286  if ( !idx.isValid() )
287  {
288  return;
289  }
290 
291  QgsVectorLayer *vlayer = mFilterModel->layer();
292  if ( !vlayer )
293  return;
294 
295  mActionPopup = new QMenu( this );
296 
297  mActionPopup->addAction( tr( "Select All" ), this, SLOT( selectAll() ), QKeySequence::SelectAll );
298 
299  // let some other parts of the application add some actions
300  emit willShowContextMenu( mActionPopup, idx );
301 
302  if ( !mActionPopup->actions().isEmpty() )
303  {
304  mActionPopup->popup( event->globalPos() );
305  }
306 }
307 
309 {
310  selectRow( row, true );
311 }
312 
314 {
315  selectRow( row, false );
316 }
317 
318 void QgsAttributeTableView::modelDeleted()
319 {
320  mFilterModel = nullptr;
321  mFeatureSelectionManager = nullptr;
322  mFeatureSelectionModel = nullptr;
323 }
324 
325 void QgsAttributeTableView::selectRow( int row, bool anchor )
326 {
327  if ( selectionBehavior() == QTableView::SelectColumns
328  || ( selectionMode() == QTableView::SingleSelection
329  && selectionBehavior() == QTableView::SelectItems ) )
330  return;
331 
332  if ( row >= 0 && row < model()->rowCount() )
333  {
334  int column = horizontalHeader()->logicalIndexAt( isRightToLeft() ? viewport()->width() : 0 );
335  QModelIndex index = model()->index( row, column );
337  selectionModel()->setCurrentIndex( index, QItemSelectionModel::NoUpdate );
338  if (( anchor && !( command & QItemSelectionModel::Current ) )
339  || ( selectionMode() == QTableView::SingleSelection ) )
340  mRowSectionAnchor = row;
341 
342  if ( selectionMode() != QTableView::SingleSelection
343  && command.testFlag( QItemSelectionModel::Toggle ) )
344  {
345  if ( anchor )
346  mCtrlDragSelectionFlag = mFeatureSelectionModel->isSelected( index )
347  ? QItemSelectionModel::Deselect : QItemSelectionModel::Select;
348  command &= ~QItemSelectionModel::Toggle;
349  command |= mCtrlDragSelectionFlag;
350  if ( !anchor )
351  command |= QItemSelectionModel::Current;
352  }
353 
354  QModelIndex tl = model()->index( qMin( mRowSectionAnchor, row ), 0 );
355  QModelIndex br = model()->index( qMax( mRowSectionAnchor, row ), model()->columnCount() - 1 );
356  if ( verticalHeader()->sectionsMoved() && tl.row() != br.row() )
357  setSelection( visualRect( tl ) | visualRect( br ), command );
358  else
359  mFeatureSelectionModel->selectFeatures( QItemSelection( tl, br ), command );
360  }
361 }
362 
363 void QgsAttributeTableView::showHorizontalSortIndicator()
364 {
366 }
367 
368 void QgsAttributeTableView::actionTriggered()
369 {
370  QAction* action = qobject_cast<QAction*>( sender() );
371  QgsFeatureId fid = action->property( "fid" ).toLongLong();
372 
373  QgsFeature f;
374  mFilterModel->layerCache()->getFeatures( QgsFeatureRequest( fid ) ).nextFeature( f );
375 
376  mFilterModel->layer()->actions()->doAction( action->data().toInt(), f );
377 }
378 
379 void QgsAttributeTableView::columnSizeChanged( int index, int oldWidth, int newWidth )
380 {
381  Q_UNUSED( oldWidth )
382  emit columnResized( index, newWidth );
383 }
384 
385 void QgsAttributeTableView::onActionColumnItemPainted( const QModelIndex& index )
386 {
387  if ( !indexWidget( index ) )
388  {
389  setIndexWidget( index, createActionWidget( mFilterModel->data( index, QgsAttributeTableModel::FeatureIdRole ).toLongLong() ) );
390  }
391 }
QLayout * layout() const
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const
bool showInAttributeTable() const
Whether 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
void setColumnWidth(int column, int width)
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
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)
QWidget * viewport() const
void setSortIndicatorShown(bool show)
void columnResized(int column, int width)
Emitted when a column in the view has been resized.
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.
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)
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.
bool hidden
Flag that controls if the column is hidden.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
virtual void setFeatureSelectionManager(QgsIFeatureSelectionManager *featureSelectionManager)
void setEditTriggers(QFlags< QAbstractItemView::EditTrigger > triggers)
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.
QVector< ColumnConfig > columns() const
Get the list with all columns and their configuration.
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)
int width
Width of column, or -1 for default width.
virtual void selectFeatures(const QItemSelection &selection, const SelectionFlags &command)
Select features on this table.
const QPoint & pos() 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.
bool isNull() const
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 setAttributeTableConfig(const QgsAttributeTableConfig &config)
Set the attribute table config which should be used to control the appearance of the attribute table...
Defines the configuration of a column in the attribute table.
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)
bool nextFeature(QgsFeature &f)
This is a container for configuration of the attribute table.
QAbstractItemModel * model() const
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
Returns the index of the default action, or -1 if no default action is available. ...
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