QGIS API Documentation  2.11.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties 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 "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  foreach ( 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() )
325  .arg( mFeatureList->parserErrorString() )
326  );
327  }
328  else
329  {
330  mFeatureListPreviewButton->setDefaultAction( previewAction );
331  mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup );
332  }
333  }
334 
335  mDisplayExpression = mFeatureList->displayExpression();
336 
337  Q_ASSERT( previewAction );
338 }
339 
341 {
342  return mMasterModel->rowCount();
343 }
344 
346 {
347  return mFilterModel->rowCount();
348 }
349 
350 void QgsDualView::viewWillShowContextMenu( QMenu* menu, QModelIndex atIndex )
351 {
352  QModelIndex sourceIndex = mFilterModel->mapToSource( atIndex );
353 
354  //add user-defined actions to context menu
355  if ( mLayerCache->layer()->actions()->size() != 0 )
356  {
357 
358  QAction *a = menu->addAction( tr( "Run layer action" ) );
359  a->setEnabled( false );
360 
361  for ( int i = 0; i < mLayerCache->layer()->actions()->size(); i++ )
362  {
363  const QgsAction &action = mLayerCache->layer()->actions()->at( i );
364 
365  if ( !action.runable() )
366  continue;
367 
368  QgsAttributeTableAction *a = new QgsAttributeTableAction( action.name(), this, i, sourceIndex );
369  menu->addAction( action.name(), a, SLOT( execute() ) );
370  }
371  }
372 
373  //add actions from QgsMapLayerActionRegistry to context menu
375  if ( registeredActions.size() > 0 )
376  {
377  //add a separator between user defined and standard actions
378  menu->addSeparator();
379 
381  for ( actionIt = registeredActions.begin(); actionIt != registeredActions.end(); ++actionIt )
382  {
383  QgsAttributeTableMapLayerAction *a = new QgsAttributeTableMapLayerAction(( *actionIt )->text(), this, ( *actionIt ), sourceIndex );
384  menu->addAction(( *actionIt )->text(), a, SLOT( execute() ) );
385  }
386  }
387 
388  menu->addSeparator();
389  QgsAttributeTableAction *a = new QgsAttributeTableAction( tr( "Open form" ), this, -1, sourceIndex );
390  menu->addAction( tr( "Open form" ), a, SLOT( featureForm() ) );
391 }
392 
393 void QgsDualView::previewExpressionChanged( const QString expression )
394 {
395  mLayerCache->layer()->setDisplayExpression( expression );
396 }
397 
398 void QgsDualView::featureFormAttributeChanged()
399 {
400  mFeatureList->setCurrentFeatureEdited( true );
401 }
402 
404 {
405  mFilterModel->setFilteredFeatures( filteredFeatures );
406 }
407 
409 {
410  mMasterModel->setRequest( request );
411 }
412 
414 {
415  mTableView->setFeatureSelectionManager( featureSelectionManager );
416  // mFeatureList->setFeatureSelectionManager( featureSelectionManager );
417 
418  if ( mFeatureSelectionManager && mFeatureSelectionManager->parent() == this )
419  delete mFeatureSelectionManager;
420 
421  mFeatureSelectionManager = featureSelectionManager;
422 }
423 
424 void QgsDualView::progress( int i, bool& cancel )
425 {
426  if ( !mProgressDlg )
427  {
428  mProgressDlg = new QProgressDialog( tr( "Loading features..." ), tr( "Abort" ), 0, 0, this );
429  mProgressDlg->setWindowTitle( tr( "Attribute table" ) );
430  mProgressDlg->setWindowModality( Qt::WindowModal );
431  mProgressDlg->show();
432  }
433 
434  mProgressDlg->setValue( i );
435  mProgressDlg->setLabelText( tr( "%1 features loaded." ).arg( i ) );
436 
438 
439  cancel = mProgressDlg && mProgressDlg->wasCanceled();
440 }
441 
442 void QgsDualView::finished()
443 {
444  delete mProgressDlg;
445  mProgressDlg = 0;
446 }
447 
448 /*
449  * QgsAttributeTableAction
450  */
451 
453 {
454  mDualView->masterModel()->executeAction( mAction, mFieldIdx );
455 }
456 
458 {
459  QgsFeatureIds editedIds;
460  editedIds << mDualView->masterModel()->rowToId( mFieldIdx.row() );
461  mDualView->setCurrentEditSelection( editedIds );
463 }
464 
465 /*
466  * QgsAttributeTableMapLayerAction
467  */
468 
470 {
471  mDualView->masterModel()->executeMapLayerAction( mAction, mFieldIdx );
472 }
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
Definition: qgsfield.cpp:343
QgsFeatureId id() const
Get the feature ID for this feature.
Definition: qgsfeature.cpp:51
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:70
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
void setWindowModality(Qt::WindowModality windowModality)
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:408
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()).
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:162
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:105
virtual int rowCount(const QModelIndex &parent) const
void setView(ViewMode view)
Change the current view mode.
int size() const
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...
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.
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 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)...
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.
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
virtual void setFilteredFeatures(QgsFeatureIds ids)
Specify a list of features, which the filter will accept.
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.