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 "qgsvectordataprovider.h"
29 #include "qgsvectorlayercache.h"
30 
31 #include <QDialog>
32 #include <QMenu>
33 #include <QMessageBox>
34 #include <QProgressDialog>
35 
36 QgsDualView::QgsDualView( QWidget* parent )
37  : QStackedWidget( parent )
38  , mEditorContext()
39  , mMasterModel( 0 )
40  , mFilterModel( 0 )
41  , mFeatureListModel( 0 )
42  , mAttributeForm( 0 )
43  , mLayerCache( 0 )
44  , mProgressDlg( 0 )
45  , mFeatureSelectionManager( 0 )
46 {
47  setupUi( this );
48 
49  mPreviewActionMapper = new QSignalMapper( this );
50 
51  mPreviewColumnsMenu = new QMenu( this );
52  mActionPreviewColumnsMenu->setMenu( mPreviewColumnsMenu );
53 
54  // Set preview icon
55  mActionExpressionPreview->setIcon( QgsApplication::getThemeIcon( "/mIconExpressionPreview.svg" ) );
56 
57  // Connect layer list preview signals
58  connect( mActionExpressionPreview, SIGNAL( triggered() ), SLOT( previewExpressionBuilder() ) );
59  connect( mPreviewActionMapper, SIGNAL( mapped( QObject* ) ), SLOT( previewColumnChanged( QObject* ) ) );
60  connect( mFeatureList, SIGNAL( displayExpressionChanged( QString ) ), this, SLOT( previewExpressionChanged( QString ) ) );
61 }
62 
63 void QgsDualView::init( QgsVectorLayer* layer, QgsMapCanvas* mapCanvas, const QgsFeatureRequest &request, const QgsAttributeEditorContext &context )
64 {
65  mEditorContext = context;
66 
67  connect( mTableView, SIGNAL( willShowContextMenu( QMenu*, QModelIndex ) ), this, SLOT( viewWillShowContextMenu( QMenu*, QModelIndex ) ) );
68 
69  initLayerCache( layer, request.filterType() == QgsFeatureRequest::FilterRect );
70  initModels( mapCanvas, request );
71 
72  mTableView->setModel( mFilterModel );
73  mFeatureList->setModel( mFeatureListModel );
74  mAttributeForm = new QgsAttributeForm( layer, QgsFeature(), mEditorContext );
75  mAttributeEditorScrollArea->setLayout( new QGridLayout() );
76  mAttributeEditorScrollArea->layout()->addWidget( mAttributeForm );
77  mAttributeEditorScrollArea->setWidget( mAttributeForm );
78 
79  mAttributeForm->hideButtonBox();
80 
81  connect( mAttributeForm, SIGNAL( attributeChanged( QString, QVariant ) ), this, SLOT( featureFormAttributeChanged() ) );
82 
83  if ( mFeatureListPreviewButton->defaultAction() )
84  mFeatureList->setDisplayExpression( mDisplayExpression );
85  else
86  columnBoxInit();
87 
88  mFeatureList->setEditSelection( QgsFeatureIds() << mFeatureListModel->idxToFid( mFeatureListModel->index( 0, 0 ) ) );
89 }
90 
92 {
93  // load fields
94  QList<QgsField> fields = mLayerCache->layer()->pendingFields().toList();
95 
96  QString defaultField;
97 
98  // default expression: saved value
99  QString displayExpression = mLayerCache->layer()->displayExpression();
100 
101  // if no display expression is saved: use display field instead
102  if ( displayExpression == "" )
103  {
104  if ( mLayerCache->layer()->displayField() != "" )
105  {
106  defaultField = mLayerCache->layer()->displayField();
107  displayExpression = QString( "COALESCE(\"%1\", '<NULL>')" ).arg( defaultField );
108  }
109  }
110 
111  // if neither diaplay expression nor display field is saved...
112  if ( displayExpression == "" )
113  {
114  QgsAttributeList pkAttrs = mLayerCache->layer()->pendingPkAttributesList();
115 
116  if ( pkAttrs.size() > 0 )
117  {
118  if ( pkAttrs.size() == 1 )
119  defaultField = pkAttrs.at( 0 );
120 
121  // ... If there are primary key(s) defined
122  QStringList pkFields;
123 
124  Q_FOREACH ( int attr, pkAttrs )
125  {
126  pkFields.append( "COALESCE(\"" + fields[attr].name() + "\", '<NULL>')" );
127  }
128 
129  displayExpression = pkFields.join( "||', '||" );
130  }
131  else if ( fields.size() > 0 )
132  {
133  if ( fields.size() == 1 )
134  defaultField = fields.at( 0 ).name();
135 
136  // ... concat all fields
137  QStringList fieldNames;
138  foreach ( QgsField field, fields )
139  {
140  fieldNames.append( "COALESCE(\"" + field.name() + "\", '<NULL>')" );
141  }
142 
143  displayExpression = fieldNames.join( "||', '||" );
144  }
145  else
146  {
147  // ... there isn't really much to display
148  displayExpression = "'[Please define preview text]'";
149  }
150  }
151 
152  mFeatureListPreviewButton->addAction( mActionExpressionPreview );
153  mFeatureListPreviewButton->addAction( mActionPreviewColumnsMenu );
154 
155  Q_FOREACH ( const QgsField& field, fields )
156  {
157  int fieldIndex = mLayerCache->layer()->fieldNameIndex( field.name() );
158  if ( fieldIndex == -1 )
159  continue;
160 
161  if ( mLayerCache->layer()->editorWidgetV2( fieldIndex ) != "Hidden" )
162  {
163  QIcon icon = QgsApplication::getThemeIcon( "/mActionNewAttribute.png" );
164  QString text = field.name();
165 
166  // Generate action for the preview popup button of the feature list
167  QAction* previewAction = new QAction( icon, text, mFeatureListPreviewButton );
168  mPreviewActionMapper->setMapping( previewAction, previewAction );
169  connect( previewAction, SIGNAL( triggered() ), mPreviewActionMapper, SLOT( map() ) );
170  mPreviewColumnsMenu->addAction( previewAction );
171 
172  if ( text == defaultField )
173  {
174  mFeatureListPreviewButton->setDefaultAction( previewAction );
175  }
176  }
177  }
178 
179  // If there is no single field found as preview
180  if ( !mFeatureListPreviewButton->defaultAction() )
181  {
182  mFeatureList->setDisplayExpression( displayExpression );
183  mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
184  mDisplayExpression = mFeatureList->displayExpression();
185  }
186  else
187  {
188  mFeatureListPreviewButton->defaultAction()->trigger();
189  }
190 }
191 
193 {
194  setCurrentIndex( view );
195 }
196 
198 {
199  mFilterModel->setFilterMode( filterMode );
200  emit filterChanged();
201 }
202 
203 void QgsDualView::setSelectedOnTop( bool selectedOnTop )
204 {
205  mFilterModel->setSelectedOnTop( selectedOnTop );
206 }
207 
208 void QgsDualView::initLayerCache( QgsVectorLayer* layer, bool cacheGeometry )
209 {
210  // Initialize the cache
211  QSettings settings;
212  int cacheSize = settings.value( "/qgis/attributeTableRowCache", "10000" ).toInt();
213  mLayerCache = new QgsVectorLayerCache( layer, cacheSize, this );
214  mLayerCache->setCacheGeometry( cacheGeometry );
215  if ( 0 == cacheSize || 0 == ( QgsVectorDataProvider::SelectAtId & mLayerCache->layer()->dataProvider()->capabilities() ) )
216  {
217  connect( mLayerCache, SIGNAL( progress( int, bool & ) ), this, SLOT( progress( int, bool & ) ) );
218  connect( mLayerCache, SIGNAL( finished() ), this, SLOT( finished() ) );
219 
220  mLayerCache->setFullCache( true );
221  }
222 }
223 
224 void QgsDualView::initModels( QgsMapCanvas* mapCanvas, const QgsFeatureRequest& request )
225 {
226  delete mFeatureListModel;
227  delete mFilterModel;
228  delete mMasterModel;
229 
230  mMasterModel = new QgsAttributeTableModel( mLayerCache, this );
231  mMasterModel->setRequest( request );
232  mMasterModel->setEditorContext( mEditorContext );
233 
234  connect( mMasterModel, SIGNAL( progress( int, bool & ) ), this, SLOT( progress( int, bool & ) ) );
235  connect( mMasterModel, SIGNAL( finished() ), this, SLOT( finished() ) );
236 
237  mMasterModel->loadLayer();
238 
239  mFilterModel = new QgsAttributeTableFilterModel( mapCanvas, mMasterModel, mMasterModel );
240 
241  connect( mFeatureList, SIGNAL( displayExpressionChanged( QString ) ), this, SIGNAL( displayExpressionChanged( QString ) ) );
242 
243  mFeatureListModel = new QgsFeatureListModel( mFilterModel, mFilterModel );
244 }
245 
246 void QgsDualView::on_mFeatureList_aboutToChangeEditSelection( bool& ok )
247 {
248  if ( mLayerCache->layer()->isEditable() && !mAttributeForm->save() )
249  ok = false;
250 }
251 
252 void QgsDualView::on_mFeatureList_currentEditSelectionChanged( const QgsFeature &feat )
253 {
254  if ( !mLayerCache->layer()->isEditable() || mAttributeForm->save() )
255  {
256  mAttributeForm->setFeature( feat );
258  }
259  else
260  {
261  // Couldn't save feature
262  }
263 }
264 
266 {
267  mFeatureList->setCurrentFeatureEdited( false );
268  mFeatureList->setEditSelection( fids );
269 }
270 
272 {
273  return mAttributeForm->save();
274 }
275 
276 void QgsDualView::previewExpressionBuilder()
277 {
278  // Show expression builder
279  QgsExpressionBuilderDialog dlg( mLayerCache->layer(), mFeatureList->displayExpression(), this );
280  dlg.setWindowTitle( tr( "Expression based preview" ) );
281  dlg.setExpressionText( mFeatureList->displayExpression() );
282 
283  if ( dlg.exec() == QDialog::Accepted )
284  {
285  mFeatureList->setDisplayExpression( dlg.expressionText() );
286  mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
287  mFeatureListPreviewButton->setPopupMode( QToolButton::MenuButtonPopup );
288  }
289 
290  mDisplayExpression = mFeatureList->displayExpression();
291 }
292 
293 void QgsDualView::previewColumnChanged( QObject* action )
294 {
295  QAction* previewAction = qobject_cast< QAction* >( action );
296 
297  if ( previewAction )
298  {
299  if ( !mFeatureList->setDisplayExpression( QString( "COALESCE( \"%1\", '<NULL>' )" ).arg( previewAction->text() ) ) )
300  {
301  QMessageBox::warning( this,
302  tr( "Could not set preview column" ),
303  tr( "Could not set column '%1' as preview column.\nParser error:\n%2" )
304  .arg( previewAction->text() )
305  .arg( mFeatureList->parserErrorString() )
306  );
307  }
308  else
309  {
310  mFeatureListPreviewButton->setDefaultAction( previewAction );
311  mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup );
312  }
313  }
314 
315  mDisplayExpression = mFeatureList->displayExpression();
316 
317  Q_ASSERT( previewAction );
318 }
319 
321 {
322  return mMasterModel->rowCount();
323 }
324 
326 {
327  return mFilterModel->rowCount();
328 }
329 
330 void QgsDualView::viewWillShowContextMenu( QMenu* menu, QModelIndex atIndex )
331 {
332  QModelIndex sourceIndex = mFilterModel->mapToSource( atIndex );
333 
334  //add user-defined actions to context menu
335  if ( mLayerCache->layer()->actions()->size() != 0 )
336  {
337 
338  QAction *a = menu->addAction( tr( "Run layer action" ) );
339  a->setEnabled( false );
340 
341  for ( int i = 0; i < mLayerCache->layer()->actions()->size(); i++ )
342  {
343  const QgsAction &action = mLayerCache->layer()->actions()->at( i );
344 
345  if ( !action.runable() )
346  continue;
347 
348  QgsAttributeTableAction *a = new QgsAttributeTableAction( action.name(), this, i, sourceIndex );
349  menu->addAction( action.name(), a, SLOT( execute() ) );
350  }
351  }
352 
353  //add actions from QgsMapLayerActionRegistry to context menu
354  QList<QgsMapLayerAction *> registeredActions = QgsMapLayerActionRegistry::instance()->mapLayerActions( mLayerCache->layer() );
355  if ( registeredActions.size() > 0 )
356  {
357  //add a separator between user defined and standard actions
358  menu->addSeparator();
359 
360  QList<QgsMapLayerAction*>::iterator actionIt;
361  for ( actionIt = registeredActions.begin(); actionIt != registeredActions.end(); ++actionIt )
362  {
363  QgsAttributeTableMapLayerAction *a = new QgsAttributeTableMapLayerAction(( *actionIt )->text(), this, ( *actionIt ), sourceIndex );
364  menu->addAction(( *actionIt )->text(), a, SLOT( execute() ) );
365  }
366  }
367 
368  menu->addSeparator();
369  QgsAttributeTableAction *a = new QgsAttributeTableAction( tr( "Open form" ), this, -1, sourceIndex );
370  menu->addAction( tr( "Open form" ), a, SLOT( featureForm() ) );
371 }
372 
373 void QgsDualView::previewExpressionChanged( const QString expression )
374 {
375  mLayerCache->layer()->setDisplayExpression( expression );
376 }
377 
378 void QgsDualView::featureFormAttributeChanged()
379 {
380  mFeatureList->setCurrentFeatureEdited( true );
381 }
382 
384 {
385  mFilterModel->setFilteredFeatures( filteredFeatures );
386 }
387 
389 {
390  mMasterModel->setRequest( request );
391 }
392 
394 {
395  mTableView->setFeatureSelectionManager( featureSelectionManager );
396  // mFeatureList->setFeatureSelectionManager( featureSelectionManager );
397 
398  if ( mFeatureSelectionManager && mFeatureSelectionManager->parent() == this )
399  delete mFeatureSelectionManager;
400 
401  mFeatureSelectionManager = featureSelectionManager;
402 }
403 
404 void QgsDualView::progress( int i, bool& cancel )
405 {
406  if ( !mProgressDlg )
407  {
408  mProgressDlg = new QProgressDialog( tr( "Loading features..." ), tr( "Abort" ), 0, 0, this );
409  mProgressDlg->setWindowTitle( tr( "Attribute table" ) );
410  mProgressDlg->setWindowModality( Qt::WindowModal );
411  mProgressDlg->show();
412  }
413 
414  mProgressDlg->setValue( i );
415  mProgressDlg->setLabelText( tr( "%1 features loaded." ).arg( i ) );
416 
417  QCoreApplication::processEvents();
418 
419  cancel = mProgressDlg->wasCanceled();
420 }
421 
422 void QgsDualView::finished()
423 {
424  delete mProgressDlg;
425  mProgressDlg = 0;
426 }
427 
428 /*
429  * QgsAttributeTableAction
430  */
431 
433 {
434  mDualView->masterModel()->executeAction( mAction, mFieldIdx );
435 }
436 
438 {
439  QgsFeatureIds editedIds;
440  editedIds << mDualView->masterModel()->rowToId( mFieldIdx.row() );
441  mDualView->setCurrentEditSelection( editedIds );
443 }
444 
445 /*
446  * QgsAttributeTableMapLayerAction
447  */
448 
450 {
451  mDualView->masterModel()->executeMapLayerAction( mAction, mFieldIdx );
452 }
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:36
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:63
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:91
int featureCount()
Returns the number of features on the layer.
#define tr(sourceText)