QGIS API Documentation  2.4.0-Chugiak
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups 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  , mAttributeForm( 0 )
41  , mLayerCache( 0 )
42  , mProgressDlg( 0 )
43  , mFeatureSelectionManager( 0 )
44 {
45  setupUi( this );
46 
47  mPreviewActionMapper = new QSignalMapper( this );
48 
49  mPreviewColumnsMenu = new QMenu( this );
50  mActionPreviewColumnsMenu->setMenu( mPreviewColumnsMenu );
51 
52  // Set preview icon
53  mActionExpressionPreview->setIcon( QgsApplication::getThemeIcon( "/mIconExpressionPreview.svg" ) );
54 
55  // Connect layer list preview signals
56  connect( mActionExpressionPreview, SIGNAL( triggered() ), SLOT( previewExpressionBuilder() ) );
57  connect( mPreviewActionMapper, SIGNAL( mapped( QObject* ) ), SLOT( previewColumnChanged( QObject* ) ) );
58  connect( mFeatureList, SIGNAL( displayExpressionChanged( QString ) ), this, SLOT( previewExpressionChanged( QString ) ) );
59 }
60 
62 {
63  mEditorContext = context;
64 
65  connect( mTableView, SIGNAL( willShowContextMenu( QMenu*, QModelIndex ) ), this, SLOT( viewWillShowContextMenu( QMenu*, QModelIndex ) ) );
66 
67  initLayerCache( layer );
68  initModels( mapCanvas, request );
69 
70  mTableView->setModel( mFilterModel );
71  mFeatureList->setModel( mFeatureListModel );
73  mAttributeEditorScrollArea->setLayout( new QGridLayout() );
74  mAttributeEditorScrollArea->layout()->addWidget( mAttributeForm );
75  mAttributeEditorScrollArea->setWidget( mAttributeForm );
76 
78 
79  connect( mAttributeForm, SIGNAL( attributeChanged( QString, QVariant ) ), this, SLOT( featureFormAttributeChanged() ) );
80 
81  columnBoxInit();
82 }
83 
85 {
86  // load fields
87  QList<QgsField> fields = mLayerCache->layer()->pendingFields().toList();
88 
89  QString defaultField;
90 
91  // default expression: saved value
92  QString displayExpression = mLayerCache->layer()->displayExpression();
93 
94  // if no display expression is saved: use display field instead
95  if ( displayExpression == "" )
96  {
97  if ( mLayerCache->layer()->displayField() != "" )
98  {
99  defaultField = mLayerCache->layer()->displayField();
100  displayExpression = QString( "COALESCE(\"%1\", '<NULL>')" ).arg( defaultField );
101  }
102  }
103 
104  // if neither diaplay expression nor display field is saved...
105  if ( displayExpression == "" )
106  {
108 
109  if ( pkAttrs.size() > 0 )
110  {
111  if ( pkAttrs.size() == 1 )
112  defaultField = pkAttrs.at( 0 );
113 
114  // ... If there are primary key(s) defined
115  QStringList pkFields;
116 
117  Q_FOREACH( int attr, pkAttrs )
118  {
119  pkFields.append( "COALESCE(\"" + fields[attr].name() + "\", '<NULL>')" );
120  }
121 
122  displayExpression = pkFields.join( "||', '||" );
123  }
124  else if ( fields.size() > 0 )
125  {
126  if ( fields.size() == 1 )
127  defaultField = fields.at( 0 ).name();
128 
129  // ... concat all fields
130  QStringList fieldNames;
131  foreach ( QgsField field, fields )
132  {
133  fieldNames.append( "COALESCE(\"" + field.name() + "\", '<NULL>')" );
134  }
135 
136  displayExpression = fieldNames.join( "||', '||" );
137  }
138  else
139  {
140  // ... there isn't really much to display
141  displayExpression = "'[Please define preview text]'";
142  }
143  }
144 
145  // now initialise the menu
146  QList< QAction* > previewActions = mFeatureListPreviewButton->actions();
147  foreach ( QAction* a, previewActions )
148  {
149  if ( a != mActionExpressionPreview )
150  {
151  mPreviewActionMapper->removeMappings( a );
152  delete a;
153  }
154  }
155 
156  mFeatureListPreviewButton->addAction( mActionExpressionPreview );
157  mFeatureListPreviewButton->addAction( mActionPreviewColumnsMenu );
158 
159  foreach ( const QgsField& field, fields )
160  {
161  if ( mLayerCache->layer()->editorWidgetV2( mLayerCache->layer()->fieldNameIndex( field.name() ) ) != "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  }
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 
208 {
209  // Initialize the cache
210  QSettings settings;
211  int cacheSize = qMax( 1, settings.value( "/qgis/attributeTableRowCache", "10000" ).toInt() );
212  mLayerCache = new QgsVectorLayerCache( layer, cacheSize, this );
213  mLayerCache->setCacheGeometry( false );
214  if ( 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 {
226  mMasterModel->setRequest( request );
227 
228  connect( mMasterModel, SIGNAL( progress( int, bool & ) ), this, SLOT( progress( int, bool & ) ) );
229  connect( mMasterModel, SIGNAL( finished() ), this, SLOT( finished() ) );
230 
232 
234 
235  connect( mFeatureList, SIGNAL( displayExpressionChanged( QString ) ), this, SIGNAL( displayExpressionChanged( QString ) ) );
236 
238 }
239 
241 {
242  if ( mLayerCache->layer()->isEditable() && !mAttributeForm->save() )
243  ok = false;
244 }
245 
247 {
248  if ( !mLayerCache->layer()->isEditable() || mAttributeForm->save() )
249  {
250  mAttributeForm->setFeature( feat );
252  }
253  else
254  {
255  // Couldn't save feature
256  }
257 }
258 
260 {
261  mFeatureList->setCurrentFeatureEdited( false );
262  mFeatureList->setEditSelection( fids );
263 }
264 
266 {
267  return mAttributeForm->save();
268 }
269 
271 {
272  // Show expression builder
273  QgsExpressionBuilderDialog dlg( mLayerCache->layer(), mFeatureList->displayExpression() , this );
274  dlg.setWindowTitle( tr( "Expression based preview" ) );
275  dlg.setExpressionText( mFeatureList->displayExpression() );
276 
277  if ( dlg.exec() == QDialog::Accepted )
278  {
279  mFeatureList->setDisplayExpression( dlg.expressionText() );
280  mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
281  mFeatureListPreviewButton->setPopupMode( QToolButton::MenuButtonPopup );
282  }
283 }
284 
285 void QgsDualView::previewColumnChanged( QObject* action )
286 {
287  QAction* previewAction = qobject_cast< QAction* >( action );
288 
289  if ( previewAction )
290  {
291  if ( !mFeatureList->setDisplayExpression( QString( "COALESCE( \"%1\", '<NULL>' )" ).arg( previewAction->text() ) ) )
292  {
293  QMessageBox::warning( this
294  , tr( "Could not set preview column" )
295  , tr( "Could not set column '%1' as preview column.\nParser error:\n%2" )
296  .arg( previewAction->text() )
297  .arg( mFeatureList->parserErrorString() )
298  );
299  }
300  else
301  {
302  mFeatureListPreviewButton->setDefaultAction( previewAction );
303  mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup );
304  }
305  }
306 
307  Q_ASSERT( previewAction );
308 }
309 
311 {
312  return mMasterModel->rowCount();
313 }
314 
316 {
317  return mFilterModel->rowCount();
318 }
319 
320 void QgsDualView::viewWillShowContextMenu( QMenu* menu, QModelIndex atIndex )
321 {
322  QModelIndex sourceIndex = mFilterModel->mapToSource( atIndex );
323 
324  //add user-defined actions to context menu
325  if ( mLayerCache->layer()->actions()->size() != 0 )
326  {
327 
328  QAction *a = menu->addAction( tr( "Run layer action" ) );
329  a->setEnabled( false );
330 
331  for ( int i = 0; i < mLayerCache->layer()->actions()->size(); i++ )
332  {
333  const QgsAction &action = mLayerCache->layer()->actions()->at( i );
334 
335  if ( !action.runable() )
336  continue;
337 
338  QgsAttributeTableAction *a = new QgsAttributeTableAction( action.name(), this, i, sourceIndex );
339  menu->addAction( action.name(), a, SLOT( execute() ) );
340  }
341  }
342 
343  //add actions from QgsMapLayerActionRegistry to context menu
344  QList<QgsMapLayerAction *> registeredActions = QgsMapLayerActionRegistry::instance()->mapLayerActions( mLayerCache->layer() );
345  if ( registeredActions.size() > 0 )
346  {
347  //add a separator between user defined and standard actions
348  menu->addSeparator();
349 
350  QList<QgsMapLayerAction*>::iterator actionIt;
351  for ( actionIt = registeredActions.begin(); actionIt != registeredActions.end(); ++actionIt )
352  {
353  QgsAttributeTableMapLayerAction *a = new QgsAttributeTableMapLayerAction(( *actionIt )->text(), this, ( *actionIt ), sourceIndex );
354  menu->addAction(( *actionIt )->text(), a, SLOT( execute() ) );
355  }
356  }
357 
358  menu->addSeparator();
359  QgsAttributeTableAction *a = new QgsAttributeTableAction( tr( "Open form" ), this, -1, sourceIndex );
360  menu->addAction( tr( "Open form" ), a, SLOT( featureForm() ) );
361 }
362 
363 void QgsDualView::previewExpressionChanged( const QString expression )
364 {
365  mLayerCache->layer()->setDisplayExpression( expression );
366 }
367 
369 {
370  mFeatureList->setCurrentFeatureEdited( true );
371 }
372 
374 {
375  mFilterModel->setFilteredFeatures( filteredFeatures );
376 }
377 
379 {
380  mMasterModel->setRequest( request );
381 }
382 
384 {
385  mTableView->setFeatureSelectionManager( featureSelectionManager );
386  // mFeatureList->setFeatureSelectionManager( featureSelectionManager );
387 
388  if ( mFeatureSelectionManager && mFeatureSelectionManager->parent() == this )
390 
391  mFeatureSelectionManager = featureSelectionManager;
392 }
393 
394 void QgsDualView::progress( int i, bool& cancel )
395 {
396  if ( !mProgressDlg )
397  {
398  mProgressDlg = new QProgressDialog( tr( "Loading features..." ), tr( "Abort" ), 0, 0, this );
399  mProgressDlg->setWindowTitle( tr( "Attribute table" ) );
400  mProgressDlg->setWindowModality( Qt::WindowModal );
401  mProgressDlg->show();
402  }
403 
404  mProgressDlg->setValue( i );
405  mProgressDlg->setLabelText( tr( "%1 features loaded." ).arg( i ) );
406 
407  QCoreApplication::processEvents();
408 
409  cancel = mProgressDlg->wasCanceled();
410 }
411 
413 {
414  delete mProgressDlg;
415  mProgressDlg = 0;
416 }
417 
418 /*
419  * QgsAttributeTableAction
420  */
421 
423 {
425 }
426 
428 {
429  QgsFeatureIds editedIds;
430  editedIds << mDualView->masterModel()->rowToId( mFieldIdx.row() );
431  mDualView->setCurrentEditSelection( editedIds );
433 }
434 
435 /*
436  * QgsAttributeTableMapLayerAction
437  */
438 
440 {
442 }
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
Definition: qgsfield.cpp:167
QgsFeatureId id() const
Get the feature id for this feature.
Definition: qgsfeature.cpp:100
void setRequest(const QgsFeatureRequest &request)
const QString & name() const
Gets the name of the field.
Definition: qgsfield.cpp:58
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)
virtual void finished()
Will be called, once all the features are loaded.
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:325
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:53
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...
QgsAttributeForm * mAttributeForm
Definition: qgsdualview.h:226
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.
QMenu * mPreviewColumnsMenu
Definition: qgsdualview.h:229
const QString displayExpression()
Get the preview expression, used to create a human readable preview string.
static QIcon icon(QString icon)
void on_mFeatureList_aboutToChangeEditSelection(bool &ok)
QgsAttributeEditorContext mEditorContext
Definition: qgsdualview.h:222
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:104
QSignalMapper * mPreviewActionMapper
Definition: qgsdualview.h:228
void setView(ViewMode view)
Change the current view mode.
void previewColumnChanged(QObject *previewAction)
Show a list of the features, where one can be chosen and the according attribute dialog will be prese...
Definition: qgsdualview.h:64
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:61
void initLayerCache(QgsVectorLayer *layer)
QString name() const
The name of the action.
QgsMapLayerAction * mAction
Definition: qgsdualview.h:271
QgsFeatureListModel * mFeatureListModel
Definition: qgsdualview.h:225
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.
QgsIFeatureSelectionManager * mFeatureSelectionManager
Definition: qgsdualview.h:232
QList< QgsMapLayerAction * > mapLayerActions(QgsMapLayer *layer)
Returns the map layer actions which can run on the specified layer.
void on_mFeatureList_currentEditSelectionChanged(const QgsFeature &feat)
Changes the currently visible feature within the attribute editor.
const QString editorWidgetV2(int fieldIdx) const
Get the id for the editor widget used to represent the field at the given index.
void viewWillShowContextMenu(QMenu *menu, QModelIndex atIndex)
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:31
void previewExpressionBuilder()
fast access to features using their ID
void executeAction(int action, const QModelIndex &idx) const
Execute an action.
void featureFormAttributeChanged()
Will be called whenever the currently shown feature form changes.
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:138
bool saveEditChanges()
saveEditChanges
void setRequest(const QgsFeatureRequest &request)
virtual void progress(int i, bool &cancel)
Will be called periodically, when loading layers from slow data providers.
void setSelectedOnTop(bool selectedOnTop)
Toggle the selectedOnTop flag.
QgsVectorLayerCache * mLayerCache
Definition: qgsdualview.h:230
static QgsMapLayerActionRegistry * instance()
Returns the instance pointer, creating the object on the first call.
QgsAttributeTableFilterModel * mFilterModel
Definition: qgsdualview.h:224
QgsDualView * mDualView
Definition: qgsdualview.h:252
bool runable() const
Whether the action is runable on the current platform.
void initModels(QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request)
QgsFeatureId rowToId(int row) const
Maps row to feature id.
const QgsFields & pendingFields() const
returns field list in the to-be-committed state
QProgressDialog * mProgressDlg
Definition: qgsdualview.h:231
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.
QgsAttributeTableModel * mMasterModel
Definition: qgsdualview.h:223
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.
void previewExpressionChanged(const QString expression)
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:84
int featureCount()
Returns the number of features on the layer.
#define tr(sourceText)