QGIS API Documentation  2.12.0-Lyon
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 at opengis 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 
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  mConditionalFormatWidget->hide();
49 
50  mPreviewActionMapper = new QSignalMapper( this );
51 
52  mPreviewColumnsMenu = new QMenu( this );
53  mActionPreviewColumnsMenu->setMenu( mPreviewColumnsMenu );
54 
55  // Set preview icon
56  mActionExpressionPreview->setIcon( QgsApplication::getThemeIcon( "/mIconExpressionPreview.svg" ) );
57 
58  // Connect layer list preview signals
59  connect( mActionExpressionPreview, SIGNAL( triggered() ), SLOT( previewExpressionBuilder() ) );
60  connect( mPreviewActionMapper, SIGNAL( mapped( QObject* ) ), SLOT( previewColumnChanged( QObject* ) ) );
61  connect( mFeatureList, SIGNAL( displayExpressionChanged( QString ) ), this, SLOT( previewExpressionChanged( QString ) ) );
62 }
63 
64 void QgsDualView::init( QgsVectorLayer* layer, QgsMapCanvas* mapCanvas, const QgsFeatureRequest &request, const QgsAttributeEditorContext &context )
65 {
66  mEditorContext = context;
67 
68  connect( mTableView, SIGNAL( willShowContextMenu( QMenu*, QModelIndex ) ), this, SLOT( viewWillShowContextMenu( QMenu*, QModelIndex ) ) );
69 
70  initLayerCache( layer, !request.filterRect().isNull() );
71  initModels( mapCanvas, request );
72 
73  mConditionalFormatWidget->setLayer( layer );
74 
75  mTableView->setModel( mFilterModel );
76  mFeatureList->setModel( mFeatureListModel );
77  mAttributeForm = new QgsAttributeForm( layer, QgsFeature(), mEditorContext );
78  mAttributeEditorScrollArea->setLayout( new QGridLayout() );
79  mAttributeEditorScrollArea->layout()->addWidget( mAttributeForm );
80  mAttributeEditorScrollArea->setWidget( mAttributeForm );
81 
82  mAttributeForm->hideButtonBox();
83 
84  connect( mAttributeForm, SIGNAL( attributeChanged( QString, QVariant ) ), this, SLOT( featureFormAttributeChanged() ) );
85  connect( mMasterModel, SIGNAL( modelChanged() ), mAttributeForm, SLOT( refreshFeature() ) );
86 
87  if ( mFeatureListPreviewButton->defaultAction() )
88  mFeatureList->setDisplayExpression( mDisplayExpression );
89  else
90  columnBoxInit();
91 
92  // This slows down load of the attribute table heaps and uses loads of memory.
93  //mTableView->resizeColumnsToContents();
94 
95  mFeatureList->setEditSelection( QgsFeatureIds() << mFeatureListModel->idxToFid( mFeatureListModel->index( 0, 0 ) ) );
96 }
97 
99 {
100  // load fields
101  QList<QgsField> fields = mLayerCache->layer()->fields().toList();
102 
103  QString defaultField;
104 
105  // default expression: saved value
106  QString displayExpression = mLayerCache->layer()->displayExpression();
107 
108  // if no display expression is saved: use display field instead
109  if ( displayExpression == "" )
110  {
111  if ( mLayerCache->layer()->displayField() != "" )
112  {
113  defaultField = mLayerCache->layer()->displayField();
114  displayExpression = QString( "COALESCE(\"%1\", '<NULL>')" ).arg( defaultField );
115  }
116  }
117 
118  // if neither diaplay expression nor display field is saved...
119  if ( displayExpression == "" )
120  {
121  QgsAttributeList pkAttrs = mLayerCache->layer()->pkAttributeList();
122 
123  if ( pkAttrs.size() > 0 )
124  {
125  if ( pkAttrs.size() == 1 )
126  defaultField = pkAttrs.at( 0 );
127 
128  // ... If there are primary key(s) defined
129  QStringList pkFields;
130 
131  Q_FOREACH ( int attr, pkAttrs )
132  {
133  pkFields.append( "COALESCE(\"" + fields[attr].name() + "\", '<NULL>')" );
134  }
135 
136  displayExpression = pkFields.join( "||', '||" );
137  }
138  else if ( fields.size() > 0 )
139  {
140  if ( fields.size() == 1 )
141  defaultField = fields.at( 0 ).name();
142 
143  // ... concat all fields
144  QStringList fieldNames;
145  Q_FOREACH ( const QgsField& field, fields )
146  {
147  fieldNames.append( "COALESCE(\"" + field.name() + "\", '<NULL>')" );
148  }
149 
150  displayExpression = fieldNames.join( "||', '||" );
151  }
152  else
153  {
154  // ... there isn't really much to display
155  displayExpression = "'[Please define preview text]'";
156  }
157  }
158 
159  mFeatureListPreviewButton->addAction( mActionExpressionPreview );
160  mFeatureListPreviewButton->addAction( mActionPreviewColumnsMenu );
161 
162  Q_FOREACH ( const QgsField& field, fields )
163  {
164  int fieldIndex = mLayerCache->layer()->fieldNameIndex( field.name() );
165  if ( fieldIndex == -1 )
166  continue;
167 
168  if ( mLayerCache->layer()->editorWidgetV2( fieldIndex ) != "Hidden" )
169  {
170  QIcon icon = QgsApplication::getThemeIcon( "/mActionNewAttribute.png" );
171  QString text = field.name();
172 
173  // Generate action for the preview popup button of the feature list
174  QAction* previewAction = new QAction( icon, text, mFeatureListPreviewButton );
175  mPreviewActionMapper->setMapping( previewAction, previewAction );
176  connect( previewAction, SIGNAL( triggered() ), mPreviewActionMapper, SLOT( map() ) );
177  mPreviewColumnsMenu->addAction( previewAction );
178 
179  if ( text == defaultField )
180  {
181  mFeatureListPreviewButton->setDefaultAction( previewAction );
182  }
183  }
184  }
185 
186  // If there is no single field found as preview
187  if ( !mFeatureListPreviewButton->defaultAction() )
188  {
189  mFeatureList->setDisplayExpression( displayExpression );
190  mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
191  mDisplayExpression = mFeatureList->displayExpression();
192  }
193  else
194  {
195  mFeatureListPreviewButton->defaultAction()->trigger();
196  }
197 }
198 
200 {
201  setCurrentIndex( view );
202 }
203 
205 {
206  mFilterModel->setFilterMode( filterMode );
207  emit filterChanged();
208 }
209 
210 void QgsDualView::setSelectedOnTop( bool selectedOnTop )
211 {
212  mFilterModel->setSelectedOnTop( selectedOnTop );
213 }
214 
215 void QgsDualView::initLayerCache( QgsVectorLayer* layer, bool cacheGeometry )
216 {
217  // Initialize the cache
218  QSettings settings;
219  int cacheSize = settings.value( "/qgis/attributeTableRowCache", "10000" ).toInt();
220  mLayerCache = new QgsVectorLayerCache( layer, cacheSize, this );
221  mLayerCache->setCacheGeometry( cacheGeometry );
222  if ( 0 == cacheSize || 0 == ( QgsVectorDataProvider::SelectAtId & mLayerCache->layer()->dataProvider()->capabilities() ) )
223  {
224  connect( mLayerCache, SIGNAL( progress( int, bool & ) ), this, SLOT( progress( int, bool & ) ) );
225  connect( mLayerCache, SIGNAL( finished() ), this, SLOT( finished() ) );
226 
227  mLayerCache->setFullCache( true );
228  }
229 }
230 
231 void QgsDualView::initModels( QgsMapCanvas* mapCanvas, const QgsFeatureRequest& request )
232 {
233  delete mFeatureListModel;
234  delete mFilterModel;
235  delete mMasterModel;
236 
237  mMasterModel = new QgsAttributeTableModel( mLayerCache, this );
238  mMasterModel->setRequest( request );
239  mMasterModel->setEditorContext( mEditorContext );
240 
241  connect( mMasterModel, SIGNAL( progress( int, bool & ) ), this, SLOT( progress( int, bool & ) ) );
242  connect( mMasterModel, SIGNAL( finished() ), this, SLOT( finished() ) );
243 
244  connect( mConditionalFormatWidget, SIGNAL( rulesUpdated( QString ) ), mMasterModel, SLOT( fieldConditionalStyleChanged( QString ) ) );
245 
246  mMasterModel->loadLayer();
247 
248  mFilterModel = new QgsAttributeTableFilterModel( mapCanvas, mMasterModel, mMasterModel );
249 
250  connect( mFeatureList, SIGNAL( displayExpressionChanged( QString ) ), this, SIGNAL( displayExpressionChanged( QString ) ) );
251 
252  mFeatureListModel = new QgsFeatureListModel( mFilterModel, mFilterModel );
253 }
254 
255 void QgsDualView::on_mFeatureList_aboutToChangeEditSelection( bool& ok )
256 {
257  if ( mLayerCache->layer()->isEditable() && !mAttributeForm->save() )
258  ok = false;
259 }
260 
261 void QgsDualView::on_mFeatureList_currentEditSelectionChanged( const QgsFeature &feat )
262 {
263  if ( !mLayerCache->layer()->isEditable() || mAttributeForm->save() )
264  {
265  mAttributeForm->setFeature( feat );
267  }
268  else
269  {
270  // Couldn't save feature
271  }
272 }
273 
275 {
276  mFeatureList->setCurrentFeatureEdited( false );
277  mFeatureList->setEditSelection( fids );
278 }
279 
281 {
282  return mAttributeForm->save();
283 }
284 
286 {
287  mConditionalFormatWidget->setVisible( !mConditionalFormatWidget->isVisible() );
288  mConditionalFormatWidget->viewRules();
289 }
290 
291 void QgsDualView::previewExpressionBuilder()
292 {
293  // Show expression builder
294  QgsExpressionContext context;
297  << QgsExpressionContextUtils::layerScope( mLayerCache->layer() );
298 
299  QgsExpressionBuilderDialog dlg( mLayerCache->layer(), mFeatureList->displayExpression(), this, "generic", context );
300  dlg.setWindowTitle( tr( "Expression based preview" ) );
301  dlg.setExpressionText( mFeatureList->displayExpression() );
302 
303  if ( dlg.exec() == QDialog::Accepted )
304  {
305  mFeatureList->setDisplayExpression( dlg.expressionText() );
306  mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
307  mFeatureListPreviewButton->setPopupMode( QToolButton::MenuButtonPopup );
308  }
309 
310  mDisplayExpression = mFeatureList->displayExpression();
311 }
312 
313 void QgsDualView::previewColumnChanged( QObject* action )
314 {
315  QAction* previewAction = qobject_cast< QAction* >( action );
316 
317  if ( previewAction )
318  {
319  if ( !mFeatureList->setDisplayExpression( QString( "COALESCE( \"%1\", '<NULL>' )" ).arg( previewAction->text() ) ) )
320  {
321  QMessageBox::warning( this,
322  tr( "Could not set preview column" ),
323  tr( "Could not set column '%1' as preview column.\nParser error:\n%2" )
324  .arg( previewAction->text(), mFeatureList->parserErrorString() )
325  );
326  }
327  else
328  {
329  mFeatureListPreviewButton->setDefaultAction( previewAction );
330  mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup );
331  }
332  }
333 
334  mDisplayExpression = mFeatureList->displayExpression();
335 
336  Q_ASSERT( previewAction );
337 }
338 
340 {
341  return mMasterModel->rowCount();
342 }
343 
345 {
346  return mFilterModel->rowCount();
347 }
348 
349 void QgsDualView::viewWillShowContextMenu( QMenu* menu, const QModelIndex& atIndex )
350 {
351  QModelIndex sourceIndex = mFilterModel->mapToSource( atIndex );
352 
353  //add user-defined actions to context menu
354  if ( mLayerCache->layer()->actions()->size() != 0 )
355  {
356 
357  QAction *a = menu->addAction( tr( "Run layer action" ) );
358  a->setEnabled( false );
359 
360  for ( int i = 0; i < mLayerCache->layer()->actions()->size(); i++ )
361  {
362  const QgsAction &action = mLayerCache->layer()->actions()->at( i );
363 
364  if ( !action.runable() )
365  continue;
366 
367  QgsAttributeTableAction *a = new QgsAttributeTableAction( action.name(), this, i, sourceIndex );
368  menu->addAction( action.name(), a, SLOT( execute() ) );
369  }
370  }
371 
372  //add actions from QgsMapLayerActionRegistry to context menu
374  if ( registeredActions.size() > 0 )
375  {
376  //add a separator between user defined and standard actions
377  menu->addSeparator();
378 
380  for ( actionIt = registeredActions.begin(); actionIt != registeredActions.end(); ++actionIt )
381  {
382  QgsAttributeTableMapLayerAction *a = new QgsAttributeTableMapLayerAction(( *actionIt )->text(), this, ( *actionIt ), sourceIndex );
383  menu->addAction(( *actionIt )->text(), a, SLOT( execute() ) );
384  }
385  }
386 
387  menu->addSeparator();
388  QgsAttributeTableAction *a = new QgsAttributeTableAction( tr( "Open form" ), this, -1, sourceIndex );
389  menu->addAction( tr( "Open form" ), a, SLOT( featureForm() ) );
390 }
391 
392 void QgsDualView::previewExpressionChanged( const QString& expression )
393 {
394  mLayerCache->layer()->setDisplayExpression( expression );
395 }
396 
397 void QgsDualView::featureFormAttributeChanged()
398 {
399  mFeatureList->setCurrentFeatureEdited( true );
400 }
401 
402 void QgsDualView::setFilteredFeatures( const QgsFeatureIds& filteredFeatures )
403 {
404  mFilterModel->setFilteredFeatures( filteredFeatures );
405 }
406 
408 {
409  mMasterModel->setRequest( request );
410 }
411 
413 {
414  mTableView->setFeatureSelectionManager( featureSelectionManager );
415  // mFeatureList->setFeatureSelectionManager( featureSelectionManager );
416 
417  if ( mFeatureSelectionManager && mFeatureSelectionManager->parent() == this )
418  delete mFeatureSelectionManager;
419 
420  mFeatureSelectionManager = featureSelectionManager;
421 }
422 
423 void QgsDualView::progress( int i, bool& cancel )
424 {
425  if ( !mProgressDlg )
426  {
427  mProgressDlg = new QProgressDialog( tr( "Loading features..." ), tr( "Abort" ), 0, 0, this );
428  mProgressDlg->setWindowTitle( tr( "Attribute table" ) );
429  mProgressDlg->setWindowModality( Qt::WindowModal );
430  mProgressDlg->show();
431  }
432 
433  mProgressDlg->setValue( i );
434  mProgressDlg->setLabelText( tr( "%1 features loaded." ).arg( i ) );
435 
437 
438  cancel = mProgressDlg && mProgressDlg->wasCanceled();
439 }
440 
441 void QgsDualView::finished()
442 {
443  delete mProgressDlg;
444  mProgressDlg = 0;
445 }
446 
447 /*
448  * QgsAttributeTableAction
449  */
450 
452 {
453  mDualView->masterModel()->executeAction( mAction, mFieldIdx );
454 }
455 
457 {
458  QgsFeatureIds editedIds;
459  editedIds << mDualView->masterModel()->rowToId( mFieldIdx.row() );
460  mDualView->setCurrentEditSelection( editedIds );
462 }
463 
464 /*
465  * QgsAttributeTableMapLayerAction
466  */
467 
469 {
470  mDualView->masterModel()->executeMapLayerAction( mAction, mFieldIdx );
471 }
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
Definition: qgsfield.cpp:369
QgsFeatureId id() const
Get the feature ID for this feature.
Definition: qgsfeature.cpp:53
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:72
void setFilterMode(QgsAttributeTableFilterModel::FilterMode filterMode)
Set the filter mode.
virtual void loadLayer()
Loads the layer into the model Preferably to be called, before using this model as source for any oth...
void setupUi(QWidget *widget)
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 openConditionalStyles()
void hideButtonBox()
Hides the button box (Ok/Cancel) and enables auto-commit.
const QgsRectangle & filterRect() const
Get the rectangle from which features will be taken.
void setWindowModality(Qt::WindowModality windowModality)
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:422
QgsFields fields() const
Returns the list of fields of this layer.
This class contains context information for attribute editor widgets.
static QIcon getThemeIcon(const QString &theName)
Helper to get a theme icon.
void setLabelText(const QString &text)
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
const T & at(int i) const
void addAction(QAction *action)
bool isNull() const
test if the rectangle is null (all coordinates zero or after call to setMinimal()).
QList< QgsMapLayerAction * > mapLayerActions(QgsMapLayer *layer, const QgsMapLayerAction::Targets &targets=QgsMapLayerAction::AllActions)
Returns the map layer actions which can run on the specified layer.
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...
const QPixmap * icon() const
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.
QString join(const QString &separator) const
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:176
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.
QString tr(const char *sourceText, const char *disambiguation, int n)
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:107
virtual int rowCount(const QModelIndex &parent) const
void setView(ViewMode view)
Change the current view mode.
int size() const
virtual void setFilteredFeatures(const QgsFeatureIds &ids)
Specify a list of features, which the filter will accept.
Show a list of the features, where one can be chosen and the according attribute dialog will be prese...
Definition: qgsdualview.h:64
virtual QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
const char * name() const
void setValue(int progress)
void setMapping(QObject *sender, int id)
QString name() const
The name of the action.
void processEvents(QFlags< QEventLoop::ProcessEventsFlag > flags)
void append(const T &value)
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
Fast access to features using their ID.
void executeMapLayerAction(QgsMapLayerAction *action, const QModelIndex &idx) const
Execute a QgsMapLayerAction.
int toInt(bool *ok) const
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.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
int row() const
void setCurrentIndex(int index)
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)...
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:64
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)
QAction * addSeparator()
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:40
void executeAction(int action, const QModelIndex &idx) const
Execute an action.
iterator end()
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)
QgsAttributeList pkAttributeList() const
Returns list of attributes making up the primary key.
void setSelectedOnTop(bool selectedOnTop)
Toggle the selectedOnTop flag.
QVariant value(const QString &key, const QVariant &defaultValue) const
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const
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.
void setWindowTitle(const QString &)
QgsFeatureId idxToFid(const QModelIndex &index) const
bool runable() const
Whether the action is runable on the current platform.
StandardButton warning(QWidget *parent, const QString &title, const QString &text, QFlags< QMessageBox::StandardButton > buttons, StandardButton defaultButton)
void show()
QgsFeatureId rowToId(int row) const
Maps row to feature id.
static QgsExpressionContextScope * projectScope()
Creates a new scope which contains variables and functions relating to the current QGIS project...
QgsVectorDataProvider * dataProvider()
Returns the data provider.
void setFilteredFeatures(const QgsFeatureIds &filteredFeatures)
Set a list of currently visible features.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
Is an interface class to abstract feature selection handling.
QObject * parent() const
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.
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
iterator begin()
A generic dialog for building expression strings.
void setEnabled(bool)
void columnBoxInit()
Initializes widgets which depend on the attributes of this layer.
Definition: qgsdualview.cpp:98
int featureCount()
Returns the number of features on the layer.