QGIS API Documentation  2.9.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
qgsdualview.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsdualview.cpp
3  --------------------------------------
4  Date : 10.2.2013
5  Copyright : (C) 2013 Matthias Kuhn
6  Email : matthias dot kuhn at gmx dot ch
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 "qgsapplication.h"
17 #include "qgsattributeaction.h"
18 #include "qgsattributeform.h"
19 #include "qgsattributetablemodel.h"
20 #include "qgsdualview.h"
22 #include "qgsfeaturelistmodel.h"
24 #include "qgsmapcanvas.h"
26 #include "qgsmessagelog.h"
27 #include "qgsvectordataprovider.h"
28 #include "qgsvectorlayercache.h"
29 
30 #include <QDialog>
31 #include <QMenu>
32 #include <QMessageBox>
33 #include <QProgressDialog>
34 
35 QgsDualView::QgsDualView( QWidget* parent )
36  : QStackedWidget( parent )
37  , mEditorContext()
38  , mMasterModel( 0 )
39  , mFilterModel( 0 )
40  , mFeatureListModel( 0 )
41  , mAttributeForm( 0 )
42  , mLayerCache( 0 )
43  , mProgressDlg( 0 )
44  , mFeatureSelectionManager( 0 )
45 {
46  setupUi( this );
47 
48  mPreviewActionMapper = new QSignalMapper( this );
49 
50  mPreviewColumnsMenu = new QMenu( this );
51  mActionPreviewColumnsMenu->setMenu( mPreviewColumnsMenu );
52 
53  // Set preview icon
54  mActionExpressionPreview->setIcon( QgsApplication::getThemeIcon( "/mIconExpressionPreview.svg" ) );
55 
56  // Connect layer list preview signals
57  connect( mActionExpressionPreview, SIGNAL( triggered() ), SLOT( previewExpressionBuilder() ) );
58  connect( mPreviewActionMapper, SIGNAL( mapped( QObject* ) ), SLOT( previewColumnChanged( QObject* ) ) );
59  connect( mFeatureList, SIGNAL( displayExpressionChanged( QString ) ), this, SLOT( previewExpressionChanged( QString ) ) );
60 }
61 
62 void QgsDualView::init( QgsVectorLayer* layer, QgsMapCanvas* mapCanvas, const QgsFeatureRequest &request, const QgsAttributeEditorContext &context )
63 {
64  mEditorContext = context;
65 
66  connect( mTableView, SIGNAL( willShowContextMenu( QMenu*, QModelIndex ) ), this, SLOT( viewWillShowContextMenu( QMenu*, QModelIndex ) ) );
67 
68  initLayerCache( layer, request.filterType() == QgsFeatureRequest::FilterRect );
69  initModels( mapCanvas, request );
70 
71  mTableView->setModel( mFilterModel );
72  mFeatureList->setModel( mFeatureListModel );
73  mAttributeForm = new QgsAttributeForm( layer, QgsFeature(), mEditorContext );
74  mAttributeEditorScrollArea->setLayout( new QGridLayout() );
75  mAttributeEditorScrollArea->layout()->addWidget( mAttributeForm );
76  mAttributeEditorScrollArea->setWidget( mAttributeForm );
77 
78  mAttributeForm->hideButtonBox();
79 
80  connect( mAttributeForm, SIGNAL( attributeChanged( QString, QVariant ) ), this, SLOT( featureFormAttributeChanged() ) );
81 
82  if ( mFeatureListPreviewButton->defaultAction() )
83  mFeatureList->setDisplayExpression( mDisplayExpression );
84  else
85  columnBoxInit();
86 
87  mFeatureList->setEditSelection( QgsFeatureIds() << mFeatureListModel->idxToFid( mFeatureListModel->index( 0, 0 ) ) );
88 }
89 
91 {
92  // load fields
93  QList<QgsField> fields = mLayerCache->layer()->pendingFields().toList();
94 
95  QString defaultField;
96 
97  // default expression: saved value
98  QString displayExpression = mLayerCache->layer()->displayExpression();
99 
100  // if no display expression is saved: use display field instead
101  if ( displayExpression == "" )
102  {
103  if ( mLayerCache->layer()->displayField() != "" )
104  {
105  defaultField = mLayerCache->layer()->displayField();
106  displayExpression = QString( "COALESCE(\"%1\", '<NULL>')" ).arg( defaultField );
107  }
108  }
109 
110  // if neither diaplay expression nor display field is saved...
111  if ( displayExpression == "" )
112  {
113  QgsAttributeList pkAttrs = mLayerCache->layer()->pendingPkAttributesList();
114 
115  if ( pkAttrs.size() > 0 )
116  {
117  if ( pkAttrs.size() == 1 )
118  defaultField = pkAttrs.at( 0 );
119 
120  // ... If there are primary key(s) defined
121  QStringList pkFields;
122 
123  Q_FOREACH ( int attr, pkAttrs )
124  {
125  pkFields.append( "COALESCE(\"" + fields[attr].name() + "\", '<NULL>')" );
126  }
127 
128  displayExpression = pkFields.join( "||', '||" );
129  }
130  else if ( fields.size() > 0 )
131  {
132  if ( fields.size() == 1 )
133  defaultField = fields.at( 0 ).name();
134 
135  // ... concat all fields
136  QStringList fieldNames;
137  foreach ( QgsField field, fields )
138  {
139  fieldNames.append( "COALESCE(\"" + field.name() + "\", '<NULL>')" );
140  }
141 
142  displayExpression = fieldNames.join( "||', '||" );
143  }
144  else
145  {
146  // ... there isn't really much to display
147  displayExpression = "'[Please define preview text]'";
148  }
149  }
150 
151  mFeatureListPreviewButton->addAction( mActionExpressionPreview );
152  mFeatureListPreviewButton->addAction( mActionPreviewColumnsMenu );
153 
154  Q_FOREACH ( const QgsField& field, fields )
155  {
156  int fieldIndex = mLayerCache->layer()->fieldNameIndex( field.name() );
157  if ( fieldIndex == -1 )
158  continue;
159 
160  if ( mLayerCache->layer()->editorWidgetV2( fieldIndex ) != "Hidden" )
161  {
162  QIcon icon = QgsApplication::getThemeIcon( "/mActionNewAttribute.png" );
163  QString text = field.name();
164 
165  // Generate action for the preview popup button of the feature list
166  QAction* previewAction = new QAction( icon, text, mFeatureListPreviewButton );
167  mPreviewActionMapper->setMapping( previewAction, previewAction );
168  connect( previewAction, SIGNAL( triggered() ), mPreviewActionMapper, SLOT( map() ) );
169  mPreviewColumnsMenu->addAction( previewAction );
170 
171  if ( text == defaultField )
172  {
173  mFeatureListPreviewButton->setDefaultAction( previewAction );
174  }
175  }
176  }
177 
178  // If there is no single field found as preview
179  if ( !mFeatureListPreviewButton->defaultAction() )
180  {
181  mFeatureList->setDisplayExpression( displayExpression );
182  mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
183  mDisplayExpression = mFeatureList->displayExpression();
184  }
185  else
186  {
187  mFeatureListPreviewButton->defaultAction()->trigger();
188  }
189 }
190 
192 {
193  setCurrentIndex( view );
194 }
195 
197 {
198  mFilterModel->setFilterMode( filterMode );
199  emit filterChanged();
200 }
201 
202 void QgsDualView::setSelectedOnTop( bool selectedOnTop )
203 {
204  mFilterModel->setSelectedOnTop( selectedOnTop );
205 }
206 
207 void QgsDualView::initLayerCache( QgsVectorLayer* layer, bool cacheGeometry )
208 {
209  // Initialize the cache
210  QSettings settings;
211  int cacheSize = settings.value( "/qgis/attributeTableRowCache", "10000" ).toInt();
212  mLayerCache = new QgsVectorLayerCache( layer, cacheSize, this );
213  mLayerCache->setCacheGeometry( cacheGeometry );
214  if ( 0 == cacheSize || 0 == ( QgsVectorDataProvider::SelectAtId & mLayerCache->layer()->dataProvider()->capabilities() ) )
215  {
216  connect( mLayerCache, SIGNAL( progress( int, bool & ) ), this, SLOT( progress( int, bool & ) ) );
217  connect( mLayerCache, SIGNAL( finished() ), this, SLOT( finished() ) );
218 
219  mLayerCache->setFullCache( true );
220  }
221 }
222 
223 void QgsDualView::initModels( QgsMapCanvas* mapCanvas, const QgsFeatureRequest& request )
224 {
225  delete mFeatureListModel;
226  delete mFilterModel;
227  delete mMasterModel;
228 
229  mMasterModel = new QgsAttributeTableModel( mLayerCache, this );
230  mMasterModel->setRequest( request );
231  mMasterModel->setEditorContext( mEditorContext );
232 
233  connect( mMasterModel, SIGNAL( progress( int, bool & ) ), this, SLOT( progress( int, bool & ) ) );
234  connect( mMasterModel, SIGNAL( finished() ), this, SLOT( finished() ) );
235 
236  mMasterModel->loadLayer();
237 
238  mFilterModel = new QgsAttributeTableFilterModel( mapCanvas, mMasterModel, mMasterModel );
239 
240  connect( mFeatureList, SIGNAL( displayExpressionChanged( QString ) ), this, SIGNAL( displayExpressionChanged( QString ) ) );
241 
242  mFeatureListModel = new QgsFeatureListModel( mFilterModel, mFilterModel );
243 }
244 
245 void QgsDualView::on_mFeatureList_aboutToChangeEditSelection( bool& ok )
246 {
247  if ( mLayerCache->layer()->isEditable() && !mAttributeForm->save() )
248  ok = false;
249 }
250 
251 void QgsDualView::on_mFeatureList_currentEditSelectionChanged( const QgsFeature &feat )
252 {
253  if ( !mLayerCache->layer()->isEditable() || mAttributeForm->save() )
254  {
255  mAttributeForm->setFeature( feat );
257  }
258  else
259  {
260  // Couldn't save feature
261  }
262 }
263 
265 {
266  mFeatureList->setCurrentFeatureEdited( false );
267  mFeatureList->setEditSelection( fids );
268 }
269 
271 {
272  return mAttributeForm->save();
273 }
274 
275 void QgsDualView::previewExpressionBuilder()
276 {
277  // Show expression builder
278  QgsExpressionBuilderDialog dlg( mLayerCache->layer(), mFeatureList->displayExpression(), this );
279  dlg.setWindowTitle( tr( "Expression based preview" ) );
280  dlg.setExpressionText( mFeatureList->displayExpression() );
281 
282  if ( dlg.exec() == QDialog::Accepted )
283  {
284  mFeatureList->setDisplayExpression( dlg.expressionText() );
285  mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
286  mFeatureListPreviewButton->setPopupMode( QToolButton::MenuButtonPopup );
287  }
288 
289  mDisplayExpression = mFeatureList->displayExpression();
290 }
291 
292 void QgsDualView::previewColumnChanged( QObject* action )
293 {
294  QAction* previewAction = qobject_cast< QAction* >( action );
295 
296  if ( previewAction )
297  {
298  if ( !mFeatureList->setDisplayExpression( QString( "COALESCE( \"%1\", '<NULL>' )" ).arg( previewAction->text() ) ) )
299  {
300  QMessageBox::warning( this,
301  tr( "Could not set preview column" ),
302  tr( "Could not set column '%1' as preview column.\nParser error:\n%2" )
303  .arg( previewAction->text() )
304  .arg( mFeatureList->parserErrorString() )
305  );
306  }
307  else
308  {
309  mFeatureListPreviewButton->setDefaultAction( previewAction );
310  mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup );
311  }
312  }
313 
314  mDisplayExpression = mFeatureList->displayExpression();
315 
316  Q_ASSERT( previewAction );
317 }
318 
320 {
321  return mMasterModel->rowCount();
322 }
323 
325 {
326  return mFilterModel->rowCount();
327 }
328 
329 void QgsDualView::viewWillShowContextMenu( QMenu* menu, QModelIndex atIndex )
330 {
331  QModelIndex sourceIndex = mFilterModel->mapToSource( atIndex );
332 
333  //add user-defined actions to context menu
334  if ( mLayerCache->layer()->actions()->size() != 0 )
335  {
336 
337  QAction *a = menu->addAction( tr( "Run layer action" ) );
338  a->setEnabled( false );
339 
340  for ( int i = 0; i < mLayerCache->layer()->actions()->size(); i++ )
341  {
342  const QgsAction &action = mLayerCache->layer()->actions()->at( i );
343 
344  if ( !action.runable() )
345  continue;
346 
347  QgsAttributeTableAction *a = new QgsAttributeTableAction( action.name(), this, i, sourceIndex );
348  menu->addAction( action.name(), a, SLOT( execute() ) );
349  }
350  }
351 
352  //add actions from QgsMapLayerActionRegistry to context menu
353  QList<QgsMapLayerAction *> registeredActions = QgsMapLayerActionRegistry::instance()->mapLayerActions( mLayerCache->layer() );
354  if ( registeredActions.size() > 0 )
355  {
356  //add a separator between user defined and standard actions
357  menu->addSeparator();
358 
359  QList<QgsMapLayerAction*>::iterator actionIt;
360  for ( actionIt = registeredActions.begin(); actionIt != registeredActions.end(); ++actionIt )
361  {
362  QgsAttributeTableMapLayerAction *a = new QgsAttributeTableMapLayerAction(( *actionIt )->text(), this, ( *actionIt ), sourceIndex );
363  menu->addAction(( *actionIt )->text(), a, SLOT( execute() ) );
364  }
365  }
366 
367  menu->addSeparator();
368  QgsAttributeTableAction *a = new QgsAttributeTableAction( tr( "Open form" ), this, -1, sourceIndex );
369  menu->addAction( tr( "Open form" ), a, SLOT( featureForm() ) );
370 }
371 
372 void QgsDualView::previewExpressionChanged( const QString expression )
373 {
374  mLayerCache->layer()->setDisplayExpression( expression );
375 }
376 
377 void QgsDualView::featureFormAttributeChanged()
378 {
379  mFeatureList->setCurrentFeatureEdited( true );
380 }
381 
383 {
384  mFilterModel->setFilteredFeatures( filteredFeatures );
385 }
386 
388 {
389  mMasterModel->setRequest( request );
390 }
391 
393 {
394  mTableView->setFeatureSelectionManager( featureSelectionManager );
395  // mFeatureList->setFeatureSelectionManager( featureSelectionManager );
396 
397  if ( mFeatureSelectionManager && mFeatureSelectionManager->parent() == this )
398  delete mFeatureSelectionManager;
399 
400  mFeatureSelectionManager = featureSelectionManager;
401 }
402 
403 void QgsDualView::progress( int i, bool& cancel )
404 {
405  if ( !mProgressDlg )
406  {
407  mProgressDlg = new QProgressDialog( tr( "Loading features..." ), tr( "Abort" ), 0, 0, this );
408  mProgressDlg->setWindowTitle( tr( "Attribute table" ) );
409  mProgressDlg->setWindowModality( Qt::WindowModal );
410  mProgressDlg->show();
411  }
412 
413  mProgressDlg->setValue( i );
414  mProgressDlg->setLabelText( tr( "%1 features loaded." ).arg( i ) );
415 
416  QCoreApplication::processEvents();
417 
418  cancel = mProgressDlg->wasCanceled();
419 }
420 
421 void QgsDualView::finished()
422 {
423  delete mProgressDlg;
424  mProgressDlg = 0;
425 }
426 
427 /*
428  * QgsAttributeTableAction
429  */
430 
432 {
433  mDualView->masterModel()->executeAction( mAction, mFieldIdx );
434 }
435 
437 {
438  QgsFeatureIds editedIds;
439  editedIds << mDualView->masterModel()->rowToId( mFieldIdx.row() );
440  mDualView->setCurrentEditSelection( editedIds );
442 }
443 
444 /*
445  * QgsAttributeTableMapLayerAction
446  */
447 
449 {
450  mDualView->masterModel()->executeMapLayerAction( mAction, mFieldIdx );
451 }
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
Definition: qgsfield.cpp:226
QgsFeatureId id() const
Get the feature id for this feature.
Definition: qgsfeature.cpp:100
void setRequest(const QgsFeatureRequest &request)
Set a request that will be used to fill this attribute table model.
const QString & name() const
Gets the name of the field.
Definition: qgsfield.cpp:59
void setFilterMode(QgsAttributeTableFilterModel::FilterMode filterMode)
Set the filter mode.
virtual void loadLayer()
Loads the layer into the model Preferably to be called, before basing any other models on this model...
void setExpressionText(const QString &text)
void setSelectedOnTop(bool selectedOnTop)
Changes the sort order of the features.
void setFilterMode(FilterMode filterMode)
Set the filter mode the filter will use.
QgsAttributeAction * actions()
void setFeatureSelectionManager(QgsIFeatureSelectionManager *featureSelectionManager)
void hideButtonBox()
Hides the button box (Ok/Cancel) and enables auto-commit.
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:317
This class contains context information for attribute editor widgets.
static QIcon getThemeIcon(const QString &theName)
Helper to get a theme icon.
bool save()
Save all the values from the editors to the layer.
ViewMode
The view modes, in which this widget can present information.
Definition: qgsdualview.h:52
QgsVectorLayer * layer()
Returns the layer to which this cache belongs.
int filteredFeatureCount()
Returns the number of features which are currently visible, according to the filter restrictions...
QgsDualView(QWidget *parent=0)
Constructor.
Definition: qgsdualview.cpp:35
void setDisplayExpression(const QString &displayExpression)
Set the preview expression, used to create a human readable preview string.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:113
A model backed by a QgsVectorLayerCache which is able to provide feature/attribute information to a Q...
void setCurrentEditSelection(const QgsFeatureIds &fids)
Set the current edit selection in the AttributeEditor mode.
const QString displayExpression()
Get the preview expression, used to create a human readable preview string.
virtual bool isEditable() const override
Returns true if the provider is in editing mode.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:105
void setView(ViewMode view)
Change the current view mode.
Show a list of the features, where one can be chosen and the according attribute dialog will be prese...
Definition: qgsdualview.h:63
virtual QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
QString name() const
The name of the action.
QList< QgsMapLayerAction * > mapLayerActions(QgsMapLayer *layer, QgsMapLayerAction::Targets targets=QgsMapLayerAction::AllActions)
Returns the map layer actions which can run on the specified layer.
fast access to features using their ID
void executeMapLayerAction(QgsMapLayerAction *action, const QModelIndex &idx) const
Execute a QgsMapLayerAction.
void setFeature(const QgsFeature &feature)
Update all editors to correspond to a different feature.
virtual int capabilities() const
Returns a bitmask containing the supported capabilities Note, some capabilities may change depending ...
void setEditorContext(const QgsAttributeEditorContext &context)
Sets the context in which this table is shown.
const QString editorWidgetV2(int fieldIdx) const
Get the id for the editor widget used to represent the field at the given index.
Utility class that encapsulates an action based on vector attributes.
void setFilteredFeatures(QgsFeatureIds filteredFeatures)
Set a list of currently visible features.
void displayExpressionChanged(const QString expression)
Is emitted, whenever the display expression is successfully changed.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QList< int > QgsAttributeList
void init(QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request=QgsFeatureRequest(), const QgsAttributeEditorContext &context=QgsAttributeEditorContext())
Has to be called to initialize the dual view.
Definition: qgsdualview.cpp:62
const QString displayField() const
Returns the primary display field name used in the identify results dialog.
Filter using a rectangle, no need to set NoGeometry.
void filterChanged()
Is emitted, whenever the filter changes.
FilterType filterType() const
QgsAction & at(int idx)
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:33
void executeAction(int action, const QModelIndex &idx) const
Execute an action.
This class caches features of a given QgsVectorLayer.
QgsAttributeTableModel * masterModel() const
Returns the model which has the information about all features (not only filtered) ...
Definition: qgsdualview.h:137
bool saveEditChanges()
saveEditChanges
void setRequest(const QgsFeatureRequest &request)
void setSelectedOnTop(bool selectedOnTop)
Toggle the selectedOnTop flag.
static QgsMapLayerActionRegistry * instance()
Returns the instance pointer, creating the object on the first call.
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const override
Returns the number of rows.
void setCacheGeometry(bool cacheGeometry)
Enable or disable the caching of geometries.
void setFullCache(bool fullCache)
This enables or disables full caching.
QgsFeatureId idxToFid(const QModelIndex &index) const
bool runable() const
Whether the action is runable on the current platform.
QgsFeatureId rowToId(int row) const
Maps row to feature id.
const QgsFields & pendingFields() const
returns field list in the to-be-committed state
QgsVectorDataProvider * dataProvider()
Returns the data provider.
QgsAttributeList pendingPkAttributesList()
returns list of attribute making up the primary key
Is an interface class to abstract feature selection handling.
Represents a vector layer which manages a vector based data sets.
int fieldNameIndex(const QString &fieldName) const
Returns the index of a field name or -1 if the field does not exist.
virtual void setFilteredFeatures(QgsFeatureIds ids)
Specify a list of features, which the filter will accept.
A generic dialog for building expression strings.
void columnBoxInit()
Initializes widgets which depend on the attributes of this layer.
Definition: qgsdualview.cpp:90
int featureCount()
Returns the number of features on the layer.
#define tr(sourceText)