QGIS API Documentation  2.15.0-Master (af20121)
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  setEditTriggers( QAbstractItemView::AllEditTriggers );
61 
62  setSelectionBehavior( QAbstractItemView::SelectRows );
63  setSelectionMode( QAbstractItemView::ExtendedSelection );
64  setSortingEnabled( true ); // At this point no data is in the model yet, so actually nothing is sorted.
65  horizontalHeader()->setSortIndicatorShown( false ); // So hide the indicator to avoid confusion.
66 
68 
69  connect( verticalHeader(), SIGNAL( sectionPressed( int ) ), this, SLOT( selectRow( int ) ) );
70  connect( verticalHeader(), SIGNAL( sectionEntered( int ) ), this, SLOT( _q_selectRow( int ) ) );
71  connect( horizontalHeader(), SIGNAL( sectionResized( int, int, int ) ), this, SLOT( columnSizeChanged( int, int, int ) ) );
72  connect( horizontalHeader(), SIGNAL( sortIndicatorChanged( int, Qt::SortOrder ) ), this, SLOT( showHorizontalSortIndicator() ) );
73 }
74 
76 {
77  if ( object == verticalHeader()->viewport() )
78  {
79  switch ( event->type() )
80  {
81  case QEvent::MouseButtonPress:
82  mFeatureSelectionModel->enableSync( false );
83  break;
84 
85  case QEvent::MouseButtonRelease:
86  mFeatureSelectionModel->enableSync( true );
87  break;
88 
89  default:
90  break;
91  }
92  }
93  return false;
94 }
95 
97 {
98  if ( mFilterModel )
99  {
100  // Cleanup old model stuff if present
101  disconnect( mFilterModel, SIGNAL( filterAboutToBeInvalidated() ), this, SLOT( onFilterAboutToBeInvalidated() ) );
102  disconnect( mFilterModel, SIGNAL( filterInvalidated() ), this, SLOT( onFilterInvalidated() ) );
103  }
104 
105  mFilterModel = filterModel;
106  QTableView::setModel( filterModel );
107 
108  connect( mFilterModel, SIGNAL( destroyed() ), this, SLOT( modelDeleted() ) );
109 
110  delete mFeatureSelectionModel;
111  mFeatureSelectionModel = nullptr;
112 
113  if ( filterModel )
114  {
115  if ( !mFeatureSelectionManager )
116  {
117  mFeatureSelectionManager = new QgsVectorLayerSelectionManager( mFilterModel->layer(), mFilterModel );
118  }
119 
120  mFeatureSelectionModel = new QgsFeatureSelectionModel( mFilterModel, mFilterModel, mFeatureSelectionManager, mFilterModel );
121  setSelectionModel( mFeatureSelectionModel );
122  mTableDelegate->setFeatureSelectionModel( mFeatureSelectionModel );
123  connect( mFeatureSelectionModel, SIGNAL( requestRepaint( QModelIndexList ) ), this, SLOT( repaintRequested( QModelIndexList ) ) );
124  connect( mFeatureSelectionModel, SIGNAL( requestRepaint() ), this, SLOT( repaintRequested() ) );
125  }
126 
127  mActionWidget = createActionWidget( 0 );
128  mActionWidget->setVisible( false );
129  updateActionImage( mActionWidget );
130 }
131 
133 {
134  if ( mFeatureSelectionManager )
135  delete mFeatureSelectionManager;
136 
137  mFeatureSelectionManager = featureSelectionManager;
138 
139  if ( mFeatureSelectionModel )
140  mFeatureSelectionModel->setFeatureSelectionManager( mFeatureSelectionManager );
141 }
142 
143 QWidget* QgsAttributeTableView::createActionWidget( QgsFeatureId fid )
144 {
145  QgsAttributeTableConfig attributeTableConfig = mFilterModel->layer()->attributeTableConfig();
146  QgsActionManager* actions = mFilterModel->layer()->actions();
147 
148  QToolButton* toolButton = nullptr;
149  QWidget* container = nullptr;
150 
151  if ( attributeTableConfig.actionWidgetStyle() == QgsAttributeTableConfig::DropDown )
152  {
153  toolButton = new QToolButton( this );
154  toolButton->setPopupMode( QToolButton::MenuButtonPopup );
155  container = toolButton;
156  }
157  else
158  {
159  container = new QWidget( this );
160  container->setLayout( new QHBoxLayout() );
161  container->layout()->setMargin( 0 );
162  }
163 
164  for ( int i = 0; i < actions->size(); ++i )
165  {
166  const QgsAction& action = actions->at( i );
167 
168  if ( !action.showInAttributeTable() )
169  continue;
170 
171  QString actionTitle = !action.shortTitle().isEmpty() ? action.shortTitle() : action.icon().isNull() ? action.name() : "";
172  QAction* act = new QAction( action.icon(), actionTitle, container );
173  act->setToolTip( action.name() );
174  act->setData( i );
175  act->setProperty( "fid", fid );
176 
177  connect( act, SIGNAL( triggered( bool ) ), this, SLOT( actionTriggered() ) );
178 
179  if ( attributeTableConfig.actionWidgetStyle() == QgsAttributeTableConfig::DropDown )
180  {
181  toolButton->addAction( act );
182 
183  if ( actions->defaultAction() == i )
184  toolButton->setDefaultAction( act );
185 
186  container = toolButton;
187  }
188  else
189  {
190  QToolButton* btn = new QToolButton;
191  btn->setDefaultAction( act );
192  container->layout()->addWidget( btn );
193  }
194  }
195 
196  if ( toolButton && !toolButton->actions().isEmpty() && actions->defaultAction() == -1 )
197  toolButton->setDefaultAction( toolButton->actions().at( 0 ) );
198 
199  return container;
200 }
201 
203 {
204  Q_UNUSED( e );
205  QSettings settings;
206  settings.setValue( "/BetterAttributeTable/geometry", QVariant( saveGeometry() ) );
207 }
208 
210 {
211  setSelectionMode( QAbstractItemView::NoSelection );
213  setSelectionMode( QAbstractItemView::ExtendedSelection );
214 }
215 
217 {
218  setSelectionMode( QAbstractItemView::NoSelection );
220  setSelectionMode( QAbstractItemView::ExtendedSelection );
221 }
222 
224 {
225  QModelIndex index = indexAt( event->pos() );
227  {
228  Q_ASSERT( index.isValid() );
229 
230  if ( !indexWidget( index ) )
231  setIndexWidget( index, createActionWidget( mFilterModel->data( index, QgsAttributeTableModel::FeatureIdRole ).toLongLong() ) );
232  }
233 
234  setSelectionMode( QAbstractItemView::NoSelection );
236  setSelectionMode( QAbstractItemView::ExtendedSelection );
237 }
238 
240 {
241  switch ( event->key() )
242  {
243 
244  // Default Qt behavior would be to change the selection.
245  // We don't make it that easy for the user to trash his selection.
246  case Qt::Key_Up:
247  case Qt::Key_Down:
248  case Qt::Key_Left:
249  case Qt::Key_Right:
250  setSelectionMode( QAbstractItemView::NoSelection );
251  QTableView::keyPressEvent( event );
252  setSelectionMode( QAbstractItemView::ExtendedSelection );
253  break;
254 
255  default:
256  QTableView::keyPressEvent( event );
257  break;
258  }
259 }
260 
261 void QgsAttributeTableView::repaintRequested( const QModelIndexList& indexes )
262 {
263  Q_FOREACH ( const QModelIndex& index, indexes )
264  {
265  update( index );
266  }
267 }
268 
270 {
271  setDirtyRegion( viewport()->rect() );
272 }
273 
275 {
276  QItemSelection selection;
277  selection.append( QItemSelectionRange( mFilterModel->index( 0, 0 ), mFilterModel->index( mFilterModel->rowCount() - 1, 0 ) ) );
278  mFeatureSelectionModel->selectFeatures( selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
279 }
280 
282 {
283  delete mActionPopup;
284  mActionPopup = nullptr;
285 
286  QModelIndex idx = indexAt( event->pos() );
287  if ( !idx.isValid() )
288  {
289  return;
290  }
291 
292  QgsVectorLayer *vlayer = mFilterModel->layer();
293  if ( !vlayer )
294  return;
295 
296  mActionPopup = new QMenu( this );
297 
298  mActionPopup->addAction( tr( "Select All" ), this, SLOT( selectAll() ), QKeySequence::SelectAll );
299 
300  // let some other parts of the application add some actions
301  emit willShowContextMenu( mActionPopup, idx );
302 
303  if ( !mActionPopup->actions().isEmpty() )
304  {
305  mActionPopup->popup( event->globalPos() );
306  }
307 }
308 
310 {
311  selectRow( row, true );
312 }
313 
315 {
316  selectRow( row, false );
317 }
318 
319 void QgsAttributeTableView::modelDeleted()
320 {
321  mFilterModel = nullptr;
322  mFeatureSelectionManager = nullptr;
323  mFeatureSelectionModel = nullptr;
324 }
325 
326 void QgsAttributeTableView::selectRow( int row, bool anchor )
327 {
328  if ( selectionBehavior() == QTableView::SelectColumns
329  || ( selectionMode() == QTableView::SingleSelection
330  && selectionBehavior() == QTableView::SelectItems ) )
331  return;
332 
333  if ( row >= 0 && row < model()->rowCount() )
334  {
335  int column = horizontalHeader()->logicalIndexAt( isRightToLeft() ? viewport()->width() : 0 );
336  QModelIndex index = model()->index( row, column );
338  selectionModel()->setCurrentIndex( index, QItemSelectionModel::NoUpdate );
339  if (( anchor && !( command & QItemSelectionModel::Current ) )
340  || ( selectionMode() == QTableView::SingleSelection ) )
341  mRowSectionAnchor = row;
342 
343  if ( selectionMode() != QTableView::SingleSelection
344  && command.testFlag( QItemSelectionModel::Toggle ) )
345  {
346  if ( anchor )
347  mCtrlDragSelectionFlag = mFeatureSelectionModel->isSelected( index )
348  ? QItemSelectionModel::Deselect : QItemSelectionModel::Select;
349  command &= ~QItemSelectionModel::Toggle;
350  command |= mCtrlDragSelectionFlag;
351  if ( !anchor )
352  command |= QItemSelectionModel::Current;
353  }
354 
355  QModelIndex tl = model()->index( qMin( mRowSectionAnchor, row ), 0 );
356  QModelIndex br = model()->index( qMax( mRowSectionAnchor, row ), model()->columnCount() - 1 );
357  if ( verticalHeader()->sectionsMoved() && tl.row() != br.row() )
358  setSelection( visualRect( tl ) | visualRect( br ), command );
359  else
360  mFeatureSelectionModel->selectFeatures( QItemSelection( tl, br ), command );
361  }
362 }
363 
364 void QgsAttributeTableView::showHorizontalSortIndicator()
365 {
367 }
368 
369 void QgsAttributeTableView::actionTriggered()
370 {
371  QAction* action = qobject_cast<QAction*>( sender() );
372  QgsFeatureId fid = action->property( "fid" ).toLongLong();
373 
374  QgsFeature f;
375  mFilterModel->layerCache()->getFeatures( QgsFeatureRequest( fid ) ).nextFeature( f );
376 
377  mFilterModel->layer()->actions()->doAction( action->data().toInt(), f );
378 }
379 
380 void QgsAttributeTableView::columnSizeChanged( int index, int oldWidth, int newWidth )
381 {
382  Q_UNUSED( oldWidth )
383  if ( mFilterModel->actionColumnIndex() == index )
384  {
385  mActionWidget->resize( newWidth, mActionWidget->height() );
386  updateActionImage( mActionWidget );
387  }
388 }
389 
390 void QgsAttributeTableView::updateActionImage( QWidget* widget )
391 {
392  QImage image( widget->size(), QImage::Format_ARGB32_Premultiplied );
393  QPainter painter( &image );
394  widget->render( &painter );
395  mTableDelegate->setActionWidgetImage( image );
396 }
QLayout * layout() const
virtual QModelIndex index(int row, int column, const QModelIndex &parent) 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)
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)
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.
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.
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 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)