QGIS API Documentation  2.12.0-Lyon
qgsrelationeditorwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrelationeditor.cpp
3  --------------------------------------
4  Date : 17.5.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 
17 
18 #include "qgsapplication.h"
19 #include "qgsdistancearea.h"
20 #include "qgsvectordataprovider.h"
21 #include "qgsexpression.h"
22 #include "qgsfeature.h"
23 #include "qgsfeatureselectiondlg.h"
25 #include "qgsrelation.h"
26 #include "qgsvectorlayertools.h"
27 
28 #include <QHBoxLayout>
29 #include <QLabel>
30 
32  : QgsCollapsibleGroupBox( parent )
33  , mViewMode( QgsDualView::AttributeEditor )
34  , mEditorContext( QgsAttributeEditorContext() )
35  , mRelation( QgsRelation() )
36  , mFeature( QgsFeature() )
37  , mInitialized( false )
38 {
39  QVBoxLayout* topLayout = new QVBoxLayout( this );
40  topLayout->setContentsMargins( 0, 9, 0, 0 );
41  setLayout( topLayout );
42 
43  // buttons
44  QHBoxLayout* buttonLayout = new QHBoxLayout();
45  buttonLayout->setContentsMargins( 0, 0, 0, 0 );
46  // toogle editing
47  mToggleEditingButton = new QToolButton( this );
48  mToggleEditingButton->setIcon( QgsApplication::getThemeIcon( "/mActionToggleEditing.svg" ) );
49  mToggleEditingButton->setText( tr( "Toggle editing" ) );
50  mToggleEditingButton->setEnabled( false );
51  mToggleEditingButton->setCheckable( true );
52  buttonLayout->addWidget( mToggleEditingButton );
53  // save Edits
54  mSaveEditsButton = new QToolButton( this );
55  mSaveEditsButton->setIcon( QgsApplication::getThemeIcon( "/mActionSaveEdits.svg" ) );
56  mSaveEditsButton->setText( tr( "Save layer edits" ) );
57  mSaveEditsButton->setEnabled( true );
58  buttonLayout->addWidget( mSaveEditsButton );
59  // add feature
60  mAddFeatureButton = new QToolButton( this );
61  mAddFeatureButton->setIcon( QgsApplication::getThemeIcon( "/mActionAdd.svg" ) );
62  mAddFeatureButton->setText( tr( "Add feature" ) );
63  buttonLayout->addWidget( mAddFeatureButton );
64  // delete feature
65  mDeleteFeatureButton = new QToolButton( this );
66  mDeleteFeatureButton->setIcon( QgsApplication::getThemeIcon( "/mActionRemove.svg" ) );
67  mDeleteFeatureButton->setText( tr( "Delete feature" ) );
68  buttonLayout->addWidget( mDeleteFeatureButton );
69  // link feature
70  mLinkFeatureButton = new QToolButton( this );
71  mLinkFeatureButton->setIcon( QgsApplication::getThemeIcon( "/mActionLink.svg" ) );
72  mLinkFeatureButton->setText( tr( "Link feature" ) );
73  buttonLayout->addWidget( mLinkFeatureButton );
74  // unlink feature
75  mUnlinkFeatureButton = new QToolButton( this );
76  mUnlinkFeatureButton->setIcon( QgsApplication::getThemeIcon( "/mActionUnlink.svg" ) );
77  mUnlinkFeatureButton->setText( tr( "Unlink feature" ) );
78  buttonLayout->addWidget( mUnlinkFeatureButton );
79  // spacer
80  buttonLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding ) );
81  // form view
82  mFormViewButton = new QToolButton( this );
83  mFormViewButton->setText( tr( "Form view" ) );
84  mFormViewButton->setIcon( QgsApplication::getThemeIcon( "/mActionPropertyItem.png" ) );
85  mFormViewButton->setCheckable( true );
86  mFormViewButton->setChecked( mViewMode == QgsDualView::AttributeEditor );
87  buttonLayout->addWidget( mFormViewButton );
88  // table view
89  mTableViewButton = new QToolButton( this );
90  mTableViewButton->setText( tr( "Table view" ) );
91  mTableViewButton->setIcon( QgsApplication::getThemeIcon( "/mActionOpenTable.png" ) );
92  mTableViewButton->setCheckable( true );
93  mTableViewButton->setChecked( mViewMode == QgsDualView::AttributeTable );
94  buttonLayout->addWidget( mTableViewButton );
95  // button group
96  mViewModeButtonGroup = new QButtonGroup( this );
97  mViewModeButtonGroup->addButton( mFormViewButton, QgsDualView::AttributeEditor );
98  mViewModeButtonGroup->addButton( mTableViewButton, QgsDualView::AttributeTable );
99 
100  // add buttons layout
101  topLayout->addLayout( buttonLayout );
102 
103  // Set initial state for add/remove etc. buttons
104  referencingLayerEditingToggled();
105 
106  mRelationLayout = new QGridLayout();
107  mRelationLayout->setContentsMargins( 0, 0, 0, 0 );
108  topLayout->addLayout( mRelationLayout );
109 
110  mDualView = new QgsDualView( this );
111  mDualView->setView( mViewMode );
112  mFeatureSelectionMgr = new QgsGenericFeatureSelectionManager( mDualView );
113  mDualView->setFeatureSelectionManager( mFeatureSelectionMgr );
114 
115  mRelationLayout->addWidget( mDualView );
116 
117  connect( this, SIGNAL( collapsedStateChanged( bool ) ), this, SLOT( onCollapsedStateChanged( bool ) ) );
118  connect( mViewModeButtonGroup, SIGNAL( buttonClicked( int ) ), this, SLOT( setViewMode( int ) ) );
119  connect( mToggleEditingButton, SIGNAL( clicked( bool ) ), this, SLOT( toggleEditing( bool ) ) );
120  connect( mSaveEditsButton, SIGNAL( clicked() ), this, SLOT( saveEdits() ) );
121  connect( mAddFeatureButton, SIGNAL( clicked() ), this, SLOT( addFeature() ) );
122  connect( mDeleteFeatureButton, SIGNAL( clicked() ), this, SLOT( deleteFeature() ) );
123  connect( mLinkFeatureButton, SIGNAL( clicked() ), this, SLOT( linkFeature() ) );
124  connect( mUnlinkFeatureButton, SIGNAL( clicked() ), this, SLOT( unlinkFeature() ) );
125 }
126 
128 {
129  if ( mRelation.isValid() )
130  {
131  disconnect( mRelation.referencingLayer(), SIGNAL( editingStarted() ), this, SLOT( referencingLayerEditingToggled() ) );
132  disconnect( mRelation.referencingLayer(), SIGNAL( editingStopped() ), this, SLOT( referencingLayerEditingToggled() ) );
133  }
134 
135  mRelation = relation;
136  mFeature = feature;
137 
138  connect( mRelation.referencingLayer(), SIGNAL( editingStarted() ), this, SLOT( referencingLayerEditingToggled() ) );
139  connect( mRelation.referencingLayer(), SIGNAL( editingStopped() ), this, SLOT( referencingLayerEditingToggled() ) );
140 
141  setTitle( relation.name() );
142 
143  QgsVectorLayer* lyr = relation.referencingLayer();
144 
145  bool canChangeAttributes = lyr->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues;
146  if ( canChangeAttributes && !lyr->isReadOnly() )
147  {
148  mToggleEditingButton->setEnabled( true );
149  referencingLayerEditingToggled();
150  }
151  else
152  {
153  mToggleEditingButton->setEnabled( false );
154  }
155 
156  setObjectName( mRelation.name() );
157  loadState();
158 
159  // If not yet initialized, it is not (yet) visible, so we don't load it to be faster (lazy loading)
160  // If it is already initialized, it has been set visible before and the currently shown feature is changing
161  // and the widget needs updating
162 
163  if ( mInitialized )
164  {
165  QgsFeatureRequest myRequest = mRelation.getRelatedFeaturesRequest( mFeature );
166 
167  mDualView->init( mRelation.referencingLayer(), 0, myRequest, mEditorContext );
168  }
169 }
170 
172 {
173  mEditorContext = context;
174 }
175 
177 {
178  mDualView->setView( mode );
179  mViewMode = mode;
180 }
181 
183 {
184  mRelationId = qgisRelationId;
185  // by setting the object name appropriately we can properly save the collapsed state
186  setObjectName( qgisRelationId );
187  loadState();
188 }
189 
190 void QgsRelationEditorWidget::referencingLayerEditingToggled()
191 {
192  bool editable = false;
193  if ( mRelation.isValid() )
194  {
195  editable = mRelation.referencingLayer()->isEditable();
196  }
197 
198  mAddFeatureButton->setEnabled( editable );
199  mLinkFeatureButton->setEnabled( editable );
200  mDeleteFeatureButton->setEnabled( editable );
201  mUnlinkFeatureButton->setEnabled( editable );
202  mToggleEditingButton->setChecked( editable );
203  mSaveEditsButton->setEnabled( editable );
204 }
205 
206 void QgsRelationEditorWidget::addFeature()
207 {
208  QgsAttributeMap keyAttrs;
209 
210  QgsFields fields = mRelation.referencingLayer()->fields();
211 
212  Q_FOREACH ( const QgsRelation::FieldPair& fieldPair, mRelation.fieldPairs() )
213  {
214  keyAttrs.insert( fields.indexFromName( fieldPair.referencingField() ), mFeature.attribute( fieldPair.referencedField() ) );
215  }
216 
217  mEditorContext.vectorLayerTools()->addFeature( mDualView->masterModel()->layer(), keyAttrs );
218 }
219 
220 void QgsRelationEditorWidget::linkFeature()
221 {
222  QgsFeatureSelectionDlg selectionDlg( mRelation.referencingLayer(), this );
223 
224  if ( selectionDlg.exec() )
225  {
226  QMap<int, QVariant> keys;
227  Q_FOREACH ( const QgsRelation::FieldPair& fieldPair, mRelation.fieldPairs() )
228  {
229  int idx = mRelation.referencingLayer()->fieldNameIndex( fieldPair.referencingField() );
230  QVariant val = mFeature.attribute( fieldPair.referencedField() );
231  keys.insert( idx, val );
232  }
233 
234  Q_FOREACH ( QgsFeatureId fid, selectionDlg.selectedFeatures() )
235  {
236  QMapIterator<int, QVariant> it( keys );
237  while ( it.hasNext() )
238  {
239  it.next();
240  mRelation.referencingLayer()->changeAttributeValue( fid, it.key(), it.value() );
241  }
242  }
243  }
244 }
245 
246 void QgsRelationEditorWidget::deleteFeature()
247 {
248  Q_FOREACH ( QgsFeatureId fid, mFeatureSelectionMgr->selectedFeaturesIds() )
249  {
250  mRelation.referencingLayer()->deleteFeature( fid );
251  }
252 }
253 
254 void QgsRelationEditorWidget::unlinkFeature()
255 {
256  QMap<int, QgsField> keyFields;
257  Q_FOREACH ( const QgsRelation::FieldPair& fieldPair, mRelation.fieldPairs() )
258  {
259  int idx = mRelation.referencingLayer()->fieldNameIndex( fieldPair.referencingField() );
260  if ( idx < 0 )
261  {
262  QgsDebugMsg( QString( "referencing field %1 not found" ).arg( fieldPair.referencingField() ) );
263  return;
264  }
265  QgsField fld = mRelation.referencingLayer()->fields().at( idx );
266  keyFields.insert( idx, fld );
267  }
268 
269  Q_FOREACH ( QgsFeatureId fid, mFeatureSelectionMgr->selectedFeaturesIds() )
270  {
271  QMapIterator<int, QgsField> it( keyFields );
272  while ( it.hasNext() )
273  {
274  it.next();
275  mRelation.referencingLayer()->changeAttributeValue( fid, it.key(), QVariant( it.value().type() ) );
276  }
277  }
278 }
279 
280 void QgsRelationEditorWidget::toggleEditing( bool state )
281 {
282  if ( state )
283  {
284  mEditorContext.vectorLayerTools()->startEditing( mRelation.referencingLayer() );
285  }
286  else
287  {
288  mEditorContext.vectorLayerTools()->stopEditing( mRelation.referencingLayer() );
289  }
290 }
291 
292 void QgsRelationEditorWidget::saveEdits()
293 {
294  mEditorContext.vectorLayerTools()->saveEdits( mRelation.referencingLayer() );
295 }
296 
297 void QgsRelationEditorWidget::onCollapsedStateChanged( bool collapsed )
298 {
299  if ( !mInitialized && !collapsed && mRelation.isValid() )
300  {
301  mInitialized = true;
302 
303  QgsFeatureRequest myRequest = mRelation.getRelatedFeaturesRequest( mFeature );
304 
305  mDualView->init( mRelation.referencingLayer(), 0, myRequest, mEditorContext );
306  }
307 }
bool isValid() const
Returns the validity of this relation.
Allows modification of attribute values.
virtual bool saveEdits(QgsVectorLayer *layer) const =0
Should be called, when the features should be commited but the editing session is not ended...
void setContentsMargins(int left, int top, int right, int bottom)
QString referencedField() const
Get the name of the referenced field.
Definition: qgsrelation.h:52
virtual bool startEditing(QgsVectorLayer *layer) const =0
This will be called, whenever a vector layer should be switched to edit mode.
void setFeatureSelectionManager(QgsIFeatureSelectionManager *featureSelectionManager)
void addWidget(QWidget *widget, int row, int column, QFlags< Qt::AlignmentFlag > alignment)
A groupbox that collapses/expands when toggled and can save its collapsed and checked states...
void addButton(QAbstractButton *button)
void clicked(bool checked)
bool deleteFeature(QgsFeatureId fid)
Delete a feature from the layer (but does not commit it)
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
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.
ViewMode
The view modes, in which this widget can present information.
Definition: qgsdualview.h:53
void collapsedStateChanged(bool collapsed)
Signal emitted when groupbox collapsed/expanded state is changed, and when first shown.
Container of fields for a vector layer.
Definition: qgsfield.h:177
virtual bool stopEditing(QgsVectorLayer *layer, bool allowCancel=true) const =0
Will be called, when an editing session is ended and the features should be commited.
virtual const QgsFeatureIds & selectedFeaturesIds() const override
Return reference to identifiers of selected features.
QString name() const
Returns a human readable name for this relation.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:176
void setViewMode(QgsDualView::ViewMode mode)
Define the view mode for the dual view.
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
virtual bool isEditable() const override
Returns true if the provider is in editing mode.
void setIcon(const QIcon &icon)
QString tr(const char *sourceText, const char *disambiguation, int n)
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:64
void setEnabled(bool)
void addWidget(QWidget *widget, int stretch, QFlags< Qt::AlignmentFlag > alignment)
Defines a relation between matchin fields of the two involved tables of a relation.
Definition: qgsrelation.h:38
void setLayout(QLayout *layout)
Shows the features and attributes in a table layout.
Definition: qgsdualview.h:58
QString referencingField() const
Get the name of the referencing field.
Definition: qgsrelation.h:50
virtual int capabilities() const
Returns a bitmask containing the supported capabilities Note, some capabilities may change depending ...
QgsVectorLayer * referencingLayer() const
Access the referencing (child) layer This is the layer which has the field(s) which point to another ...
virtual void addItem(QLayoutItem *item)
void setObjectName(const QString &name)
This class wraps a request for features to a vector layer (or directly its vector data provider)...
void setQgisRelation(const QString &qgisRelationId)
Defines the relation ID (from project relations)
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
void setCheckable(bool)
virtual bool addFeature(QgsVectorLayer *layer, const QgsAttributeMap &defaultValues=QgsAttributeMap(), const QgsGeometry &defaultGeometry=QgsGeometry()) const =0
This method should/will be called, whenever a new feature will be added to the layer.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:40
QgsFeatureRequest getRelatedFeaturesRequest(const QgsFeature &feature) const
Creates a request to return all the features on the referencing (child) layer which have a foreign ke...
const QgsField & at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfield.cpp:331
Q_DECL_DEPRECATED bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &value, bool emitSignal)
Changes an attribute value (but does not commit it)
int indexFromName(const QString &name) const
Look up field's index from name. Returns -1 on error.
Definition: qgsfield.cpp:364
This selection manager synchronizes a local set of selected features with an attribute table...
QgsAttributeTableModel * masterModel() const
Returns the model which has the information about all features (not only filtered) ...
Definition: qgsdualview.h:138
QgsRelationEditorWidget(QWidget *parent=NULL)
void setChecked(bool)
void setRelationFeature(const QgsRelation &relation, const QgsFeature &feature)
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:238
void setTitle(const QString &title)
QgsVectorLayer * layer() const
Returns the layer this model uses as backend.
void setEditorContext(const QgsAttributeEditorContext &context)
const QgsVectorLayerTools * vectorLayerTools() const
void loadState()
Will load the collapsed and checked state.
QList< FieldPair > fieldPairs() const
Returns the field pairs which form this relation The first element of each pair are the field names f...
qint64 QgsFeatureId
Definition: qgsfeature.h:31
void setText(const QString &text)
iterator insert(const Key &key, const T &value)
QgsVectorDataProvider * dataProvider()
Returns the data provider.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
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.
This widget is used to show the attributes of a set of features of a QgsVectorLayer.
Definition: qgsdualview.h:41
void addLayout(QLayout *layout, int stretch)