QGIS API Documentation  2.0.1-Dufour
 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 "qgsdualview.h"
17 #include "qgsmapcanvas.h"
18 #include "qgsvectorlayercache.h"
19 #include "qgsattributetablemodel.h"
20 #include "qgsfeaturelistmodel.h"
21 #include "qgsattributedialog.h"
22 #include "qgsapplication.h"
24 #include "qgsattributeaction.h"
25 #include "qgsvectordataprovider.h"
26 #include "qgsmessagelog.h"
27 
28 #include <QDialog>
29 #include <QMenu>
30 #include <QProgressDialog>
31 #include <QMessageBox>
32 
33 QgsDualView::QgsDualView( QWidget* parent )
34  : QStackedWidget( parent )
35  , mAttributeDialog( NULL )
36  , mProgressDlg( NULL )
37 {
38  setupUi( this );
39 
40  mPreviewActionMapper = new QSignalMapper( this );
41 
42  mPreviewColumnsMenu = new QMenu( this );
43  mActionPreviewColumnsMenu->setMenu( mPreviewColumnsMenu );
44 
45  // Set preview icon
46  mActionExpressionPreview->setIcon( QgsApplication::getThemeIcon( "/mIconExpressionPreview.svg" ) );
47 
48  // Connect layer list preview signals
49  connect( mActionExpressionPreview, SIGNAL( triggered() ), SLOT( previewExpressionBuilder() ) );
50  connect( mPreviewActionMapper, SIGNAL( mapped( QObject* ) ), SLOT( previewColumnChanged( QObject* ) ) );
51  connect( mFeatureList, SIGNAL( displayExpressionChanged( QString ) ), this, SLOT( previewExpressionChanged( QString ) ) );
52  connect( this, SIGNAL( currentChanged( int ) ), this, SLOT( saveEditChanges() ) );
53 }
54 
56 {
57  delete mAttributeDialog;
58 }
59 
61 {
62  mDistanceArea = myDa;
63 
64  connect( mTableView, SIGNAL( willShowContextMenu( QMenu*, QModelIndex ) ), this, SLOT( viewWillShowContextMenu( QMenu*, QModelIndex ) ) );
65 
66  initLayerCache( layer );
67  initModels( mapCanvas );
68 
69  mTableView->setModel( mFilterModel );
70  mFeatureList->setModel( mFeatureListModel );
71 
72  mAttributeDialog = new QgsAttributeDialog( layer, 0, false, myDa );
73  if ( mAttributeDialog->dialog() )
74  mAttributeEditorLayout->addWidget( mAttributeDialog->dialog() );
75 
76  columnBoxInit();
77 }
78 
80 {
81  // load fields
82  QList<QgsField> fields = mLayerCache->layer()->pendingFields().toList();
83 
84  QString defaultField;
85 
86  // default expression: saved value
87  QString displayExpression = mLayerCache->layer()->displayExpression();
88 
89  // if no display expression is saved: use display field instead
90  if ( displayExpression == "" )
91  {
92  if ( mLayerCache->layer()->displayField() != "" )
93  {
94  defaultField = mLayerCache->layer()->displayField();
95  displayExpression = QString( "COALESCE(\"%1\", '<NULL>')" ).arg( defaultField );
96  }
97  }
98 
99  // if neither diaplay expression nor display field is saved...
100  if ( displayExpression == "" )
101  {
103 
104  if ( pkAttrs.size() > 0 )
105  {
106  if ( pkAttrs.size() == 1 )
107  defaultField = pkAttrs.at( 0 );
108 
109  // ... If there are primary key(s) defined
110  QStringList pkFields;
111 
112  Q_FOREACH( int attr, pkAttrs )
113  {
114  pkFields.append( "COALESCE(\"" + fields[attr].name() + "\", '<NULL>')" );
115  }
116 
117  displayExpression = pkFields.join( "||', '||" );
118  }
119  else if ( fields.size() > 0 )
120  {
121  if ( fields.size() == 1 )
122  defaultField = fields.at( 0 ).name();
123 
124  // ... concat all fields
125  QStringList fieldNames;
126  foreach ( QgsField field, fields )
127  {
128  fieldNames.append( "COALESCE(\"" + field.name() + "\", '<NULL>')" );
129  }
130 
131  displayExpression = fieldNames.join( "||', '||" );
132  }
133  else
134  {
135  // ... there isn't really much to display
136  displayExpression = "'[Please define preview text]'";
137  }
138  }
139 
140  // now initialise the menu
141  QList< QAction* > previewActions = mFeatureListPreviewButton->actions();
142  foreach ( QAction* a, previewActions )
143  {
144  if ( a != mActionExpressionPreview )
145  {
146  mPreviewActionMapper->removeMappings( a );
147  delete a;
148  }
149  }
150 
151  mFeatureListPreviewButton->addAction( mActionExpressionPreview );
152  mFeatureListPreviewButton->addAction( mActionPreviewColumnsMenu );
153 
154  foreach ( const QgsField field, fields )
155  {
157  {
158  QIcon icon = QgsApplication::getThemeIcon( "/mActionNewAttribute.png" );
159  QString text = field.name();
160 
161  // Generate action for the preview popup button of the feature list
162  QAction* previewAction = new QAction( icon, text, mFeatureListPreviewButton );
163  mPreviewActionMapper->setMapping( previewAction, previewAction );
164  connect( previewAction, SIGNAL( triggered() ), mPreviewActionMapper, SLOT( map() ) );
165  mPreviewColumnsMenu->addAction( previewAction );
166 
167  if ( text == defaultField )
168  {
169  mFeatureListPreviewButton->setDefaultAction( previewAction );
170  }
171  }
172  }
173 
174  // If there is no single field found as preview
175  if ( !mFeatureListPreviewButton->defaultAction() )
176  {
177  mFeatureList->setDisplayExpression( displayExpression );
178  mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
179  }
180  else
181  {
182  mFeatureListPreviewButton->defaultAction()->trigger();
183  }
184 }
185 
186 void QgsDualView::hideEvent( QHideEvent* event )
187 {
188  saveEditChanges();
189  QStackedWidget::hideEvent( event );
190 }
191 
192 void QgsDualView::focusOutEvent( QFocusEvent* event )
193 {
194  saveEditChanges();
195  QStackedWidget::focusOutEvent( event );
196 }
197 
199 {
200  setCurrentIndex( view );
201 }
202 
204 {
205  mFilterModel->setFilterMode( filterMode );
206 }
207 
208 void QgsDualView::setSelectedOnTop( bool selectedOnTop )
209 {
210  mFilterModel->setSelectedOnTop( selectedOnTop );
211 }
212 
214 {
215  // Initialize the cache
216  QSettings settings;
217  int cacheSize = qMax( 1, settings.value( "/qgis/attributeTableRowCache", "10000" ).toInt() );
218  mLayerCache = new QgsVectorLayerCache( layer, cacheSize, this );
219  mLayerCache->setCacheGeometry( false );
220  if ( 0 == ( QgsVectorDataProvider::SelectAtId & mLayerCache->layer()->dataProvider()->capabilities() ) )
221  {
222  connect( mLayerCache, SIGNAL( progress( int, bool & ) ), this, SLOT( progress( int, bool & ) ) );
223  connect( mLayerCache, SIGNAL( finished() ), this, SLOT( finished() ) );
224 
225  mLayerCache->setFullCache( true );
226  }
227 
228  connect( layer, SIGNAL( editingStarted() ), this, SLOT( editingToggled() ) );
229  connect( layer, SIGNAL( beforeCommitChanges() ), this, SLOT( editingToggled() ) );
230  connect( layer, SIGNAL( editingStopped() ), this, SLOT( editingToggled() ) );
231  connect( layer, SIGNAL( attributeAdded( int ) ), this, SLOT( attributeAdded( int ) ) );
232  connect( layer, SIGNAL( attributeDeleted( int ) ), this, SLOT( attributeDeleted( int ) ) );
233  connect( layer, SIGNAL( featureDeleted( QgsFeatureId ) ), this, SLOT( featureDeleted( QgsFeatureId ) ) );
234 }
235 
237 {
239 
240  connect( mMasterModel, SIGNAL( progress( int, bool & ) ), this, SLOT( progress( int, bool & ) ) );
241  connect( mMasterModel, SIGNAL( finished() ), this, SLOT( finished() ) );
242 
244 
246 
247  connect( mFeatureList, SIGNAL( displayExpressionChanged( QString ) ), this, SIGNAL( displayExpressionChanged( QString ) ) );
248 
250 }
251 
253 {
254  if ( !feat.isValid() )
255  return;
256 
257  // Backup old dialog and delete only after creating the new dialog, so we can "hot-swap" the contained QgsFeature
259 
261  {
262  saveEditChanges();
263  mAttributeEditorLayout->removeWidget( mAttributeDialog->dialog() );
264  }
265 
266  mAttributeDialog = new QgsAttributeDialog( mLayerCache->layer(), new QgsFeature( feat ), true, mDistanceArea, this, false );
267  mAttributeEditorLayout->addWidget( mAttributeDialog->dialog() );
268  mAttributeDialog->dialog()->setVisible( true );
269 
270  delete oldDialog;
271 }
272 
274 {
275  mFeatureList->setEditSelection( fids );
276 }
277 
279 {
281  {
282  if ( mLayerCache->layer() && mLayerCache->layer()->isEditable() )
283  {
284  // Get the current (unedited) feature
285  QgsFeature srcFeat;
287  mLayerCache->featureAtId( fid, srcFeat );
288  QgsAttributes src = srcFeat.attributes();
289 
290  // Let the dialog write the edited widget values to it's feature
292  // Get the edited feature
294 
295  if ( src.count() != dst.count() )
296  {
297  // bail out
298  return;
299  }
300 
301  mLayerCache->layer()->beginEditCommand( tr( "Attributes changed" ) );
302 
303  for ( int i = 0; i < dst.count(); ++i )
304  {
305  if ( dst[i] != src[i] )
306  {
307  mLayerCache->layer()->changeAttributeValue( fid, i, dst[i] );
308  }
309  }
310 
312  }
313  }
314 }
315 
317 {
318  // Show expression builder
319  QgsExpressionBuilderDialog dlg( mLayerCache->layer(), mFeatureList->displayExpression() , this );
320  dlg.setWindowTitle( tr( "Expression based preview" ) );
321  dlg.setExpressionText( mFeatureList->displayExpression() );
322 
323  if ( dlg.exec() == QDialog::Accepted )
324  {
325  mFeatureList->setDisplayExpression( dlg.expressionText() );
326  mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
327  mFeatureListPreviewButton->setPopupMode( QToolButton::MenuButtonPopup );
328  }
329 }
330 
331 void QgsDualView::previewColumnChanged( QObject* action )
332 {
333  QAction* previewAction = qobject_cast< QAction* >( action );
334 
335  if ( previewAction )
336  {
337  if ( !mFeatureList->setDisplayExpression( QString( "COALESCE( \"%1\", '<NULL>' )" ).arg( previewAction->text() ) ) )
338  {
339  QMessageBox::warning( this
340  , tr( "Could not set preview column" )
341  , tr( "Could not set column '%1' as preview column.\nParser error:\n%2" )
342  .arg( previewAction->text() )
343  .arg( mFeatureList->parserErrorString() )
344  );
345  }
346  else
347  {
348  mFeatureListPreviewButton->setDefaultAction( previewAction );
349  mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup );
350  }
351  }
352 
353  Q_ASSERT( previewAction );
354 }
355 
357 {
358  // Reload the attribute dialog widget and commit changes if any
360  {
362  }
363 }
364 
366 {
367  return mMasterModel->rowCount();
368 }
369 
371 {
372  return mFilterModel->rowCount();
373 }
374 
375 void QgsDualView::viewWillShowContextMenu( QMenu* menu, QModelIndex atIndex )
376 {
377  QModelIndex sourceIndex = mFilterModel->mapToSource( atIndex );
378 
379  if ( mLayerCache->layer()->actions()->size() != 0 )
380  {
381 
382  QAction *a = menu->addAction( tr( "Run action" ) );
383  a->setEnabled( false );
384 
385  for ( int i = 0; i < mLayerCache->layer()->actions()->size(); i++ )
386  {
387  const QgsAction &action = mLayerCache->layer()->actions()->at( i );
388 
389  if ( !action.runable() )
390  continue;
391 
392  QgsAttributeTableAction *a = new QgsAttributeTableAction( action.name(), this, i, sourceIndex );
393  menu->addAction( action.name(), a, SLOT( execute() ) );
394  }
395  }
396 
397  QgsAttributeTableAction *a = new QgsAttributeTableAction( tr( "Open form" ), this, -1, sourceIndex );
398  menu->addAction( tr( "Open form" ), a, SLOT( featureForm() ) );
399 }
400 
401 void QgsDualView::previewExpressionChanged( const QString expression )
402 {
403  mLayerCache->layer()->setDisplayExpression( expression );
404 }
405 
406 void QgsDualView::attributeDeleted( int attribute )
407 {
409  {
410  // Let the dialog write the edited widget values to it's feature
412  // Get the edited feature
414  feat->deleteAttribute( attribute );
415 
416  // Backup old dialog and delete only after creating the new dialog, so we can "hot-swap" the contained QgsFeature
418 
419  mAttributeEditorLayout->removeWidget( mAttributeDialog->dialog() );
420 
421  mAttributeDialog = new QgsAttributeDialog( mLayerCache->layer(), new QgsFeature( *feat ), true, mDistanceArea, this, false );
422  mAttributeEditorLayout->addWidget( mAttributeDialog->dialog() );
423 
424  delete oldDialog;
425  }
426 }
427 
428 void QgsDualView::attributeAdded( int attribute )
429 {
431  {
432  // Let the dialog write the edited widget values to it's feature
434  // Get the edited feature
436 
437  // Get the feature including the newly added attribute
438  QgsFeature newFeat;
439  mLayerCache->featureAtId( feat->id(), newFeat );
440 
441  int offset = 0;
442  for ( int idx = 0; idx < newFeat.attributes().count(); ++idx )
443  {
444  if ( idx == attribute )
445  {
446  offset = 1;
447  }
448  else
449  {
450  newFeat.setAttribute( idx, feat->attribute( idx - offset ) );
451  }
452  }
453 
454  *feat = newFeat;
455 
456  // Backup old dialog and delete only after creating the new dialog, so we can "hot-swap" the contained QgsFeature
458 
459  mAttributeEditorLayout->removeWidget( mAttributeDialog->dialog() );
460 
461  mAttributeDialog = new QgsAttributeDialog( mLayerCache->layer(), new QgsFeature( *feat ), true, mDistanceArea, this, false );
462  mAttributeEditorLayout->addWidget( mAttributeDialog->dialog() );
463 
464  delete oldDialog;
465  }
466 }
467 
469 {
471  {
473  if ( feat )
474  {
475  if ( fid == feat->id() )
476  {
477  delete( mAttributeDialog );
478  mAttributeDialog = NULL;
479  }
480  }
481  }
482 }
483 
484 void QgsDualView::reloadAttribute( const int& idx )
485 {
487  {
488  // Let the dialog write the edited widget values to it's feature
490  // Get the edited feature
492 
493  // Get the feature including the changed attribute
494  QgsFeature newFeat;
495  mLayerCache->featureAtId( feat->id(), newFeat );
496 
497  // Update the attribute
498  feat->setAttribute( idx, newFeat.attribute( idx ) );
499 
500  // Backup old dialog and delete only after creating the new dialog, so we can "hot-swap" the contained QgsFeature
502 
503  mAttributeEditorLayout->removeWidget( mAttributeDialog->dialog() );
504 
505  mAttributeDialog = new QgsAttributeDialog( mLayerCache->layer(), new QgsFeature( *feat ), true, mDistanceArea, this, false );
506  mAttributeEditorLayout->addWidget( mAttributeDialog->dialog() );
507 
508  delete oldDialog;
509  }
510 }
511 
513 {
514  mFilterModel->setFilteredFeatures( filteredFeatures );
515 }
516 
517 void QgsDualView::progress( int i, bool& cancel )
518 {
519  if ( !mProgressDlg )
520  {
521  mProgressDlg = new QProgressDialog( tr( "Loading features..." ), tr( "Abort" ), 0, 0, this );
522  mProgressDlg->setWindowTitle( tr( "Attribute table" ) );
523  mProgressDlg->setWindowModality( Qt::WindowModal );
524  mProgressDlg->show();
525  }
526 
527  mProgressDlg->setValue( i );
528  mProgressDlg->setLabelText( tr( "%1 features loaded." ).arg( i ) );
529 
530  QCoreApplication::processEvents();
531 
532  cancel = mProgressDlg->wasCanceled();
533 }
534 
536 {
537  delete mProgressDlg;
538  mProgressDlg = 0;
539 }
540 
541 /*
542  * QgsAttributeTableAction
543  */
544 
546 {
548 }
549 
551 {
552  QgsFeatureIds editedIds;
553  editedIds << mDualView->masterModel()->rowToId( mFieldIdx.row() );
554  mDualView->setCurrentEditSelection( editedIds );
556 }
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
Definition: qgsfield.cpp:164
QgsFeatureId id() const
Get the feature id for this feature.
Definition: qgsfeature.cpp:101
const QString & name() const
Gets the name of the field.
Definition: qgsfield.cpp:55
QgsFeature * feature()
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 initModels(QgsMapCanvas *mapCanvas)
void setExpressionText(const QString &text)
void saveEditChanges()
saveEditChanges
virtual void finished()
Will be called, once all the features are loaded.
void setSelectedOnTop(bool selectedOnTop)
Changes the sort order of the features.
bool isValid() const
Return the validity of this feature.
Definition: qgsfeature.cpp:172
void setFilterMode(FilterMode filterMode)
Set the filter mode the filter will use.
QgsAttributeAction * actions()
virtual void hideEvent(QHideEvent *)
void setDisplayExpression(const QString displayExpression)
Set the preview expression, used to create a human readable preview string.
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const
Returns the number of rows.
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:321
void beginEditCommand(QString text)
Create edit command for undo/redo operations.
void attributeDeleted(int attribute)
If an attribute on this layer is deleted, remove the field also for open attribute dialogs...
ViewMode
The view modes, in which this widget can present information.
Definition: qgsdualview.h:50
void reloadAttribute(const int &attribute)
Update the shown feature if an attribute changed.
bool setAttribute(int field, const QVariant &attr)
Set an attribute by id.
Definition: qgsfeature.cpp:191
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:33
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:114
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:241
const QString displayExpression()
Get the preview expression, used to create a human readable preview string.
static QIcon icon(QString icon)
bool changeAttributeValue(QgsFeatureId fid, int field, QVariant value, bool emitSignal=true)
changed an attribute value (but does not commit it)
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:99
virtual void focusOutEvent(QFocusEvent *)
QSignalMapper * mPreviewActionMapper
Definition: qgsdualview.h:240
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:61
void initLayerCache(QgsVectorLayer *layer)
QString name() const
The name of the action.
void deleteAttribute(int field)
Deletes an attribute and its value.
Definition: qgsfeature.cpp:107
QgsFeatureListModel * mFeatureListModel
Definition: qgsdualview.h:237
void init(QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, QgsDistanceArea myDa)
Has to be called to initialize the dual view.
Definition: qgsdualview.cpp:60
The attribute value should not be shown in the attribute form.
EditType editType(int idx)
get edit type
void on_mFeatureList_currentEditSelectionChanged(const QgsFeature &feat)
Changes the currently visible feature within the attribute editor.
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.
QList< int > QgsAttributeList
const QgsAttributes & attributes() const
Definition: qgsfeature.h:143
const QString displayField() const
Returns the primary display field name used in the identify results dialog.
void featureDeleted(QgsFeatureId fid)
Gets called when a feature is deleted.
QgsAction & at(int idx)
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:29
void previewExpressionBuilder()
fast access to features using their ID
void executeAction(int action, const QModelIndex &idx) const
Execute an action.
void endEditCommand()
Finish edit command and add it to undo/redo stack.
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:131
General purpose distance and area calculator.
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.
void editingToggled()
QgsVectorLayerCache * mLayerCache
Definition: qgsdualview.h:242
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:231
QVector< QVariant > QgsAttributes
Definition: qgsfeature.h:100
bool featureAtId(QgsFeatureId featureId, QgsFeature &feature, bool skipCache=false)
Gets the feature at the given feature id.
QgsAttributeDialog * mAttributeDialog
Definition: qgsdualview.h:238
QgsAttributeTableFilterModel * mFilterModel
Definition: qgsdualview.h:236
void attributeAdded(int attribute)
If an attribute on this layer is added, add the field also for open attribute dialogs.
qint64 QgsFeatureId
Definition: qgsfeature.h:30
QgsDualView * mDualView
Definition: qgsdualview.h:264
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
QProgressDialog * mProgressDlg
Definition: qgsdualview.h:243
virtual bool isEditable() const
Returns true if the provider is in editing mode.
QgsAttributeList pendingPkAttributesList()
returns list of attribute making up the primary key
QgsAttributeTableModel * mMasterModel
Definition: qgsdualview.h:235
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.
virtual ~QgsDualView()
Definition: qgsdualview.cpp:55
void columnBoxInit()
Initializes widgets which depend on the attributes of this layer.
Definition: qgsdualview.cpp:79
int featureCount()
Returns the number of features on the layer.
QgsDistanceArea mDistanceArea
Definition: qgsdualview.h:245
static QIcon getThemeIcon(const QString theName)
Helper to get a theme icon.
#define tr(sourceText)