QGIS API Documentation  2.7.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 
64 {
65  mEditorContext = context;
66 
67  connect( mTableView, SIGNAL( willShowContextMenu( QMenu*, QModelIndex ) ), this, SLOT( viewWillShowContextMenu( QMenu*, QModelIndex ) ) );
68 
69  initLayerCache( layer );
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  if ( mLayerCache->layer()->editorWidgetV2( mLayerCache->layer()->fieldNameIndex( field.name() ) ) != "Hidden" )
158  {
159  QIcon icon = QgsApplication::getThemeIcon( "/mActionNewAttribute.png" );
160  QString text = field.name();
161 
162  // Generate action for the preview popup button of the feature list
163  QAction* previewAction = new QAction( icon, text, mFeatureListPreviewButton );
164  mPreviewActionMapper->setMapping( previewAction, previewAction );
165  connect( previewAction, SIGNAL( triggered() ), mPreviewActionMapper, SLOT( map() ) );
166  mPreviewColumnsMenu->addAction( previewAction );
167 
168  if ( text == defaultField )
169  {
170  mFeatureListPreviewButton->setDefaultAction( previewAction );
171  }
172  }
173  }
174 
175  // If there is no single field found as preview
176  if ( !mFeatureListPreviewButton->defaultAction() )
177  {
178  mFeatureList->setDisplayExpression( displayExpression );
179  mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
180  mDisplayExpression = mFeatureList->displayExpression();
181  }
182  else
183  {
184  mFeatureListPreviewButton->defaultAction()->trigger();
185  }
186 }
187 
189 {
190  setCurrentIndex( view );
191 }
192 
194 {
195  mFilterModel->setFilterMode( filterMode );
196  emit filterChanged();
197 }
198 
199 void QgsDualView::setSelectedOnTop( bool selectedOnTop )
200 {
201  mFilterModel->setSelectedOnTop( selectedOnTop );
202 }
203 
204 void QgsDualView::initLayerCache( QgsVectorLayer* layer )
205 {
206  // Initialize the cache
207  QSettings settings;
208  int cacheSize = settings.value( "/qgis/attributeTableRowCache", "10000" ).toInt();
209  mLayerCache = new QgsVectorLayerCache( layer, cacheSize, this );
210  mLayerCache->setCacheGeometry( false );
211  if ( 0 == cacheSize || 0 == ( QgsVectorDataProvider::SelectAtId & mLayerCache->layer()->dataProvider()->capabilities() ) )
212  {
213  connect( mLayerCache, SIGNAL( progress( int, bool & ) ), this, SLOT( progress( int, bool & ) ) );
214  connect( mLayerCache, SIGNAL( finished() ), this, SLOT( finished() ) );
215 
216  mLayerCache->setFullCache( true );
217  }
218 }
219 
220 void QgsDualView::initModels( QgsMapCanvas* mapCanvas, const QgsFeatureRequest& request )
221 {
222  delete mFeatureListModel;
223  delete mFilterModel;
224  delete mMasterModel;
225 
226  mMasterModel = new QgsAttributeTableModel( mLayerCache, this );
227  mMasterModel->setRequest( request );
228  mMasterModel->setEditorContext( mEditorContext );
229 
230  connect( mMasterModel, SIGNAL( progress( int, bool & ) ), this, SLOT( progress( int, bool & ) ) );
231  connect( mMasterModel, SIGNAL( finished() ), this, SLOT( finished() ) );
232 
233  mMasterModel->loadLayer();
234 
235  mFilterModel = new QgsAttributeTableFilterModel( mapCanvas, mMasterModel, mMasterModel );
236 
237  connect( mFeatureList, SIGNAL( displayExpressionChanged( QString ) ), this, SIGNAL( displayExpressionChanged( QString ) ) );
238 
239  mFeatureListModel = new QgsFeatureListModel( mFilterModel, mFilterModel );
240 }
241 
242 void QgsDualView::on_mFeatureList_aboutToChangeEditSelection( bool& ok )
243 {
244  if ( mLayerCache->layer()->isEditable() && !mAttributeForm->save() )
245  ok = false;
246 }
247 
248 void QgsDualView::on_mFeatureList_currentEditSelectionChanged( const QgsFeature &feat )
249 {
250  if ( !mLayerCache->layer()->isEditable() || mAttributeForm->save() )
251  {
252  mAttributeForm->setFeature( feat );
254  }
255  else
256  {
257  // Couldn't save feature
258  }
259 }
260 
262 {
263  mFeatureList->setCurrentFeatureEdited( false );
264  mFeatureList->setEditSelection( fids );
265 }
266 
268 {
269  return mAttributeForm->save();
270 }
271 
272 void QgsDualView::previewExpressionBuilder()
273 {
274  // Show expression builder
275  QgsExpressionBuilderDialog dlg( mLayerCache->layer(), mFeatureList->displayExpression(), this );
276  dlg.setWindowTitle( tr( "Expression based preview" ) );
277  dlg.setExpressionText( mFeatureList->displayExpression() );
278 
279  if ( dlg.exec() == QDialog::Accepted )
280  {
281  mFeatureList->setDisplayExpression( dlg.expressionText() );
282  mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
283  mFeatureListPreviewButton->setPopupMode( QToolButton::MenuButtonPopup );
284  }
285 
286  mDisplayExpression = mFeatureList->displayExpression();
287 }
288 
289 void QgsDualView::previewColumnChanged( QObject* action )
290 {
291  QAction* previewAction = qobject_cast< QAction* >( action );
292 
293  if ( previewAction )
294  {
295  if ( !mFeatureList->setDisplayExpression( QString( "COALESCE( \"%1\", '<NULL>' )" ).arg( previewAction->text() ) ) )
296  {
297  QMessageBox::warning( this,
298  tr( "Could not set preview column" ),
299  tr( "Could not set column '%1' as preview column.\nParser error:\n%2" )
300  .arg( previewAction->text() )
301  .arg( mFeatureList->parserErrorString() )
302  );
303  }
304  else
305  {
306  mFeatureListPreviewButton->setDefaultAction( previewAction );
307  mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup );
308  }
309  }
310 
311  mDisplayExpression = mFeatureList->displayExpression();
312 
313  Q_ASSERT( previewAction );
314 }
315 
317 {
318  return mMasterModel->rowCount();
319 }
320 
322 {
323  return mFilterModel->rowCount();
324 }
325 
326 void QgsDualView::viewWillShowContextMenu( QMenu* menu, QModelIndex atIndex )
327 {
328  QModelIndex sourceIndex = mFilterModel->mapToSource( atIndex );
329 
330  //add user-defined actions to context menu
331  if ( mLayerCache->layer()->actions()->size() != 0 )
332  {
333 
334  QAction *a = menu->addAction( tr( "Run layer action" ) );
335  a->setEnabled( false );
336 
337  for ( int i = 0; i < mLayerCache->layer()->actions()->size(); i++ )
338  {
339  const QgsAction &action = mLayerCache->layer()->actions()->at( i );
340 
341  if ( !action.runable() )
342  continue;
343 
344  QgsAttributeTableAction *a = new QgsAttributeTableAction( action.name(), this, i, sourceIndex );
345  menu->addAction( action.name(), a, SLOT( execute() ) );
346  }
347  }
348 
349  //add actions from QgsMapLayerActionRegistry to context menu
350  QList<QgsMapLayerAction *> registeredActions = QgsMapLayerActionRegistry::instance()->mapLayerActions( mLayerCache->layer() );
351  if ( registeredActions.size() > 0 )
352  {
353  //add a separator between user defined and standard actions
354  menu->addSeparator();
355 
356  QList<QgsMapLayerAction*>::iterator actionIt;
357  for ( actionIt = registeredActions.begin(); actionIt != registeredActions.end(); ++actionIt )
358  {
359  QgsAttributeTableMapLayerAction *a = new QgsAttributeTableMapLayerAction(( *actionIt )->text(), this, ( *actionIt ), sourceIndex );
360  menu->addAction(( *actionIt )->text(), a, SLOT( execute() ) );
361  }
362  }
363 
364  menu->addSeparator();
365  QgsAttributeTableAction *a = new QgsAttributeTableAction( tr( "Open form" ), this, -1, sourceIndex );
366  menu->addAction( tr( "Open form" ), a, SLOT( featureForm() ) );
367 }
368 
369 void QgsDualView::previewExpressionChanged( const QString expression )
370 {
371  mLayerCache->layer()->setDisplayExpression( expression );
372 }
373 
374 void QgsDualView::featureFormAttributeChanged()
375 {
376  mFeatureList->setCurrentFeatureEdited( true );
377 }
378 
380 {
381  mFilterModel->setFilteredFeatures( filteredFeatures );
382 }
383 
385 {
386  mMasterModel->setRequest( request );
387 }
388 
390 {
391  mTableView->setFeatureSelectionManager( featureSelectionManager );
392  // mFeatureList->setFeatureSelectionManager( featureSelectionManager );
393 
394  if ( mFeatureSelectionManager && mFeatureSelectionManager->parent() == this )
395  delete mFeatureSelectionManager;
396 
397  mFeatureSelectionManager = featureSelectionManager;
398 }
399 
400 void QgsDualView::progress( int i, bool& cancel )
401 {
402  if ( !mProgressDlg )
403  {
404  mProgressDlg = new QProgressDialog( tr( "Loading features..." ), tr( "Abort" ), 0, 0, this );
405  mProgressDlg->setWindowTitle( tr( "Attribute table" ) );
406  mProgressDlg->setWindowModality( Qt::WindowModal );
407  mProgressDlg->show();
408  }
409 
410  mProgressDlg->setValue( i );
411  mProgressDlg->setLabelText( tr( "%1 features loaded." ).arg( i ) );
412 
413  QCoreApplication::processEvents();
414 
415  cancel = mProgressDlg->wasCanceled();
416 }
417 
418 void QgsDualView::finished()
419 {
420  delete mProgressDlg;
421  mProgressDlg = 0;
422 }
423 
424 /*
425  * QgsAttributeTableAction
426  */
427 
429 {
430  mDualView->masterModel()->executeAction( mAction, mFieldIdx );
431 }
432 
434 {
435  QgsFeatureIds editedIds;
436  editedIds << mDualView->masterModel()->rowToId( mFieldIdx.row() );
437  mDualView->setCurrentEditSelection( editedIds );
439 }
440 
441 /*
442  * QgsAttributeTableMapLayerAction
443  */
444 
446 {
447  mDualView->masterModel()->executeMapLayerAction( mAction, mFieldIdx );
448 }
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
Definition: qgsfield.cpp:200
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.
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const
Returns the number of rows.
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.
virtual QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const
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.
static QIcon icon(QString icon)
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:104
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
void init(QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request=QgsFeatureRequest(), QgsAttributeEditorContext context=QgsAttributeEditorContext())
Has to be called to initialize the dual view.
Definition: qgsdualview.cpp:63
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
const QString displayField() const
Returns the primary display field name used in the identify results dialog.
void filterChanged()
Is emitted, whenever the filter changes.
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.
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.
virtual bool isEditable() const
Returns true if the provider is in editing mode.
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)