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 
90 {
91  // load fields
92  QList<QgsField> fields = mLayerCache->layer()->pendingFields().toList();
93 
94  QString defaultField;
95 
96  // default expression: saved value
97  QString displayExpression = mLayerCache->layer()->displayExpression();
98 
99  // if no display expression is saved: use display field instead
100  if ( displayExpression == "" )
101  {
102  if ( mLayerCache->layer()->displayField() != "" )
103  {
104  defaultField = mLayerCache->layer()->displayField();
105  displayExpression = QString( "COALESCE(\"%1\", '<NULL>')" ).arg( defaultField );
106  }
107  }
108 
109  // if neither diaplay expression nor display field is saved...
110  if ( displayExpression == "" )
111  {
112  QgsAttributeList pkAttrs = mLayerCache->layer()->pendingPkAttributesList();
113 
114  if ( pkAttrs.size() > 0 )
115  {
116  if ( pkAttrs.size() == 1 )
117  defaultField = pkAttrs.at( 0 );
118 
119  // ... If there are primary key(s) defined
120  QStringList pkFields;
121 
122  Q_FOREACH ( int attr, pkAttrs )
123  {
124  pkFields.append( "COALESCE(\"" + fields[attr].name() + "\", '<NULL>')" );
125  }
126 
127  displayExpression = pkFields.join( "||', '||" );
128  }
129  else if ( fields.size() > 0 )
130  {
131  if ( fields.size() == 1 )
132  defaultField = fields.at( 0 ).name();
133 
134  // ... concat all fields
135  QStringList fieldNames;
136  foreach ( QgsField field, fields )
137  {
138  fieldNames.append( "COALESCE(\"" + field.name() + "\", '<NULL>')" );
139  }
140 
141  displayExpression = fieldNames.join( "||', '||" );
142  }
143  else
144  {
145  // ... there isn't really much to display
146  displayExpression = "'[Please define preview text]'";
147  }
148  }
149 
150  mFeatureListPreviewButton->addAction( mActionExpressionPreview );
151  mFeatureListPreviewButton->addAction( mActionPreviewColumnsMenu );
152 
153  Q_FOREACH ( const QgsField& field, fields )
154  {
155  if ( mLayerCache->layer()->editorWidgetV2( mLayerCache->layer()->fieldNameIndex( field.name() ) ) != "Hidden" )
156  {
157  QIcon icon = QgsApplication::getThemeIcon( "/mActionNewAttribute.png" );
158  QString text = field.name();
159 
160  // Generate action for the preview popup button of the feature list
161  QAction* previewAction = new QAction( icon, text, mFeatureListPreviewButton );
162  mPreviewActionMapper->setMapping( previewAction, previewAction );
163  connect( previewAction, SIGNAL( triggered() ), mPreviewActionMapper, SLOT( map() ) );
164  mPreviewColumnsMenu->addAction( previewAction );
165 
166  if ( text == defaultField )
167  {
168  mFeatureListPreviewButton->setDefaultAction( previewAction );
169  }
170  }
171  }
172 
173  // If there is no single field found as preview
174  if ( !mFeatureListPreviewButton->defaultAction() )
175  {
176  mFeatureList->setDisplayExpression( displayExpression );
177  mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
178  mDisplayExpression = mFeatureList->displayExpression();
179  }
180  else
181  {
182  mFeatureListPreviewButton->defaultAction()->trigger();
183  }
184 }
185 
187 {
188  setCurrentIndex( view );
189 }
190 
192 {
193  mFilterModel->setFilterMode( filterMode );
194  emit filterChanged();
195 }
196 
197 void QgsDualView::setSelectedOnTop( bool selectedOnTop )
198 {
199  mFilterModel->setSelectedOnTop( selectedOnTop );
200 }
201 
202 void QgsDualView::initLayerCache( QgsVectorLayer* layer )
203 {
204  // Initialize the cache
205  QSettings settings;
206  int cacheSize = settings.value( "/qgis/attributeTableRowCache", "10000" ).toInt();
207  mLayerCache = new QgsVectorLayerCache( layer, cacheSize, this );
208  mLayerCache->setCacheGeometry( false );
209  if ( 0 == cacheSize || 0 == ( QgsVectorDataProvider::SelectAtId & mLayerCache->layer()->dataProvider()->capabilities() ) )
210  {
211  connect( mLayerCache, SIGNAL( progress( int, bool & ) ), this, SLOT( progress( int, bool & ) ) );
212  connect( mLayerCache, SIGNAL( finished() ), this, SLOT( finished() ) );
213 
214  mLayerCache->setFullCache( true );
215  }
216 }
217 
218 void QgsDualView::initModels( QgsMapCanvas* mapCanvas, const QgsFeatureRequest& request )
219 {
220  delete mFeatureListModel;
221  delete mFilterModel;
222  delete mMasterModel;
223 
224  mMasterModel = new QgsAttributeTableModel( mLayerCache, this );
225  mMasterModel->setRequest( request );
226  mMasterModel->setEditorContext( mEditorContext );
227 
228  connect( mMasterModel, SIGNAL( progress( int, bool & ) ), this, SLOT( progress( int, bool & ) ) );
229  connect( mMasterModel, SIGNAL( finished() ), this, SLOT( finished() ) );
230 
231  mMasterModel->loadLayer();
232 
233  mFilterModel = new QgsAttributeTableFilterModel( mapCanvas, mMasterModel, mMasterModel );
234 
235  connect( mFeatureList, SIGNAL( displayExpressionChanged( QString ) ), this, SIGNAL( displayExpressionChanged( QString ) ) );
236 
237  mFeatureListModel = new QgsFeatureListModel( mFilterModel, mFilterModel );
238 }
239 
240 void QgsDualView::on_mFeatureList_aboutToChangeEditSelection( bool& ok )
241 {
242  if ( mLayerCache->layer()->isEditable() && !mAttributeForm->save() )
243  ok = false;
244 }
245 
246 void QgsDualView::on_mFeatureList_currentEditSelectionChanged( const QgsFeature &feat )
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 
270 void QgsDualView::previewExpressionBuilder()
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  mDisplayExpression = mFeatureList->displayExpression();
285 }
286 
287 void QgsDualView::previewColumnChanged( QObject* action )
288 {
289  QAction* previewAction = qobject_cast< QAction* >( action );
290 
291  if ( previewAction )
292  {
293  if ( !mFeatureList->setDisplayExpression( QString( "COALESCE( \"%1\", '<NULL>' )" ).arg( previewAction->text() ) ) )
294  {
295  QMessageBox::warning( this,
296  tr( "Could not set preview column" ),
297  tr( "Could not set column '%1' as preview column.\nParser error:\n%2" )
298  .arg( previewAction->text() )
299  .arg( mFeatureList->parserErrorString() )
300  );
301  }
302  else
303  {
304  mFeatureListPreviewButton->setDefaultAction( previewAction );
305  mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup );
306  }
307  }
308 
309  mDisplayExpression = mFeatureList->displayExpression();
310 
311  Q_ASSERT( previewAction );
312 }
313 
315 {
316  return mMasterModel->rowCount();
317 }
318 
320 {
321  return mFilterModel->rowCount();
322 }
323 
324 void QgsDualView::viewWillShowContextMenu( QMenu* menu, QModelIndex atIndex )
325 {
326  QModelIndex sourceIndex = mFilterModel->mapToSource( atIndex );
327 
328  //add user-defined actions to context menu
329  if ( mLayerCache->layer()->actions()->size() != 0 )
330  {
331 
332  QAction *a = menu->addAction( tr( "Run layer action" ) );
333  a->setEnabled( false );
334 
335  for ( int i = 0; i < mLayerCache->layer()->actions()->size(); i++ )
336  {
337  const QgsAction &action = mLayerCache->layer()->actions()->at( i );
338 
339  if ( !action.runable() )
340  continue;
341 
342  QgsAttributeTableAction *a = new QgsAttributeTableAction( action.name(), this, i, sourceIndex );
343  menu->addAction( action.name(), a, SLOT( execute() ) );
344  }
345  }
346 
347  //add actions from QgsMapLayerActionRegistry to context menu
348  QList<QgsMapLayerAction *> registeredActions = QgsMapLayerActionRegistry::instance()->mapLayerActions( mLayerCache->layer() );
349  if ( registeredActions.size() > 0 )
350  {
351  //add a separator between user defined and standard actions
352  menu->addSeparator();
353 
354  QList<QgsMapLayerAction*>::iterator actionIt;
355  for ( actionIt = registeredActions.begin(); actionIt != registeredActions.end(); ++actionIt )
356  {
357  QgsAttributeTableMapLayerAction *a = new QgsAttributeTableMapLayerAction(( *actionIt )->text(), this, ( *actionIt ), sourceIndex );
358  menu->addAction(( *actionIt )->text(), a, SLOT( execute() ) );
359  }
360  }
361 
362  menu->addSeparator();
363  QgsAttributeTableAction *a = new QgsAttributeTableAction( tr( "Open form" ), this, -1, sourceIndex );
364  menu->addAction( tr( "Open form" ), a, SLOT( featureForm() ) );
365 }
366 
367 void QgsDualView::previewExpressionChanged( const QString expression )
368 {
369  mLayerCache->layer()->setDisplayExpression( expression );
370 }
371 
372 void QgsDualView::featureFormAttributeChanged()
373 {
374  mFeatureList->setCurrentFeatureEdited( true );
375 }
376 
378 {
379  mFilterModel->setFilteredFeatures( filteredFeatures );
380 }
381 
383 {
384  mMasterModel->setRequest( request );
385 }
386 
388 {
389  mTableView->setFeatureSelectionManager( featureSelectionManager );
390  // mFeatureList->setFeatureSelectionManager( featureSelectionManager );
391 
392  if ( mFeatureSelectionManager && mFeatureSelectionManager->parent() == this )
393  delete mFeatureSelectionManager;
394 
395  mFeatureSelectionManager = featureSelectionManager;
396 }
397 
398 void QgsDualView::progress( int i, bool& cancel )
399 {
400  if ( !mProgressDlg )
401  {
402  mProgressDlg = new QProgressDialog( tr( "Loading features..." ), tr( "Abort" ), 0, 0, this );
403  mProgressDlg->setWindowTitle( tr( "Attribute table" ) );
404  mProgressDlg->setWindowModality( Qt::WindowModal );
405  mProgressDlg->show();
406  }
407 
408  mProgressDlg->setValue( i );
409  mProgressDlg->setLabelText( tr( "%1 features loaded." ).arg( i ) );
410 
411  QCoreApplication::processEvents();
412 
413  cancel = mProgressDlg->wasCanceled();
414 }
415 
416 void QgsDualView::finished()
417 {
418  delete mProgressDlg;
419  mProgressDlg = 0;
420 }
421 
422 /*
423  * QgsAttributeTableAction
424  */
425 
427 {
428  mDualView->masterModel()->executeAction( mAction, mFieldIdx );
429 }
430 
432 {
433  QgsFeatureIds editedIds;
434  editedIds << mDualView->masterModel()->rowToId( mFieldIdx.row() );
435  mDualView->setCurrentEditSelection( editedIds );
437 }
438 
439 /*
440  * QgsAttributeTableMapLayerAction
441  */
442 
444 {
445  mDualView->masterModel()->executeMapLayerAction( mAction, mFieldIdx );
446 }
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.
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.
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:89
int featureCount()
Returns the number of features on the layer.
#define tr(sourceText)