QGIS API Documentation  2.8.6-Wien
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 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 
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  // If not yet initialized, it is not (yet) visible, so we don't load it to be faster (lazy loading)
157  // If it is already initialized, it has been set visible before and the currently shown feature is changing
158  // and the widget needs updating
159 
160  if ( mInitialized )
161  {
162  QgsFeatureRequest myRequest = mRelation.getRelatedFeaturesRequest( mFeature );
163 
164  mDualView->init( mRelation.referencingLayer(), 0, myRequest, mEditorContext );
165  }
166 }
167 
169 {
170  mEditorContext = context;
171 }
172 
174 {
175  mDualView->setView( mode );
176  mViewMode = mode;
177 }
178 
179 void QgsRelationEditorWidget::referencingLayerEditingToggled()
180 {
181  bool editable = false;
182  if ( mRelation.isValid() )
183  {
184  editable = mRelation.referencingLayer()->isEditable();
185  }
186 
187  mAddFeatureButton->setEnabled( editable );
188  mLinkFeatureButton->setEnabled( editable );
189  mDeleteFeatureButton->setEnabled( editable );
190  mUnlinkFeatureButton->setEnabled( editable );
191  mToggleEditingButton->setChecked( editable );
192  mSaveEditsButton->setEnabled( editable );
193 }
194 
195 void QgsRelationEditorWidget::addFeature()
196 {
197  QgsAttributeMap keyAttrs;
198 
199  QgsFields fields = mRelation.referencingLayer()->pendingFields();
200 
201  Q_FOREACH ( QgsRelation::FieldPair fieldPair, mRelation.fieldPairs() )
202  {
203  keyAttrs.insert( fields.indexFromName( fieldPair.referencingField() ), mFeature.attribute( fieldPair.referencedField() ) );
204  }
205 
206  mEditorContext.vectorLayerTools()->addFeature( mDualView->masterModel()->layer(), keyAttrs );
207 }
208 
209 void QgsRelationEditorWidget::linkFeature()
210 {
211  QgsFeatureSelectionDlg selectionDlg( mRelation.referencingLayer(), this );
212 
213  if ( selectionDlg.exec() )
214  {
215  QMap<int, QVariant> keys;
216  Q_FOREACH ( const QgsRelation::FieldPair fieldPair, mRelation.fieldPairs() )
217  {
218  int idx = mRelation.referencingLayer()->fieldNameIndex( fieldPair.referencingField() );
219  QVariant val = mFeature.attribute( fieldPair.referencedField() );
220  keys.insert( idx, val );
221  }
222 
223  Q_FOREACH ( QgsFeatureId fid, selectionDlg.selectedFeatures() )
224  {
225  QMapIterator<int, QVariant> it( keys );
226  while ( it.hasNext() )
227  {
228  it.next();
229  mRelation.referencingLayer()->changeAttributeValue( fid, it.key(), it.value() );
230  }
231  }
232  }
233 }
234 
235 void QgsRelationEditorWidget::deleteFeature()
236 {
237  Q_FOREACH ( QgsFeatureId fid, mFeatureSelectionMgr->selectedFeaturesIds() )
238  {
239  mRelation.referencingLayer()->deleteFeature( fid );
240  }
241 }
242 
243 void QgsRelationEditorWidget::unlinkFeature()
244 {
245  QMap<int, QgsField> keyFields;
246  Q_FOREACH ( const QgsRelation::FieldPair fieldPair, mRelation.fieldPairs() )
247  {
248  int idx = mRelation.referencingLayer()->fieldNameIndex( fieldPair.referencingField() );
249  if ( idx < 0 )
250  {
251  QgsDebugMsg( QString( "referencing field %1 not found" ).arg( fieldPair.referencingField() ) );
252  return;
253  }
254  QgsField fld = mRelation.referencingLayer()->pendingFields().at( idx );
255  keyFields.insert( idx, fld );
256  }
257 
258  Q_FOREACH ( QgsFeatureId fid, mFeatureSelectionMgr->selectedFeaturesIds() )
259  {
260  QMapIterator<int, QgsField> it( keyFields );
261  while ( it.hasNext() )
262  {
263  it.next();
264  mRelation.referencingLayer()->changeAttributeValue( fid, it.key(), QVariant( it.value().type() ) );
265  }
266  }
267 }
268 
269 void QgsRelationEditorWidget::toggleEditing( bool state )
270 {
271  if ( state )
272  {
273  mEditorContext.vectorLayerTools()->startEditing( mRelation.referencingLayer() );
274  }
275  else
276  {
277  mEditorContext.vectorLayerTools()->stopEditing( mRelation.referencingLayer() );
278  }
279 }
280 
281 void QgsRelationEditorWidget::saveEdits()
282 {
283  mEditorContext.vectorLayerTools()->saveEdits( mRelation.referencingLayer() );
284 }
285 
286 void QgsRelationEditorWidget::onCollapsedStateChanged( bool collapsed )
287 {
288  if ( !mInitialized && !collapsed && mRelation.isValid() )
289  {
290  mInitialized = true;
291 
292  QgsFeatureRequest myRequest = mRelation.getRelatedFeaturesRequest( mFeature );
293 
294  mDualView->init( mRelation.referencingLayer(), 0, myRequest, mEditorContext );
295  }
296 }
bool isValid() const
Returns the validity of this relation.
const QString name() const
virtual bool saveEdits(QgsVectorLayer *layer) const =0
Should be called, when the features should be commited but the editing session is not ended...
virtual bool startEditing(QgsVectorLayer *layer) const =0
This will be called, whenever a vector layer should be switched to edit mode.
QMap< int, QVariant > QgsAttributeMap
Definition: qgsfeature.h:98
void setFeatureSelectionManager(QgsIFeatureSelectionManager *featureSelectionManager)
A groupbox that collapses/expands when toggled and can save its collapsed and checked states...
bool deleteFeature(QgsFeatureId fid)
delete a feature from the layer (but does not commit it)
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
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:52
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:172
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.
const QgsField & at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfield.h:227
virtual const QgsFeatureIds & selectedFeaturesIds() const override
Return reference to identifiers of selected features.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:156
void setViewMode(QgsDualView::ViewMode mode)
Define the view mode for the dual view.
virtual bool isEditable() const override
Returns true if the provider is in editing mode.
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
Defines a relation between matchin fields of the two involved tables of a relation.
Definition: qgsrelation.h:38
Shows the features and attributes in a table layout.
Definition: qgsdualview.h:57
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 ...
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:63
Q_DECL_DEPRECATED bool changeAttributeValue(QgsFeatureId fid, int field, QVariant value, bool emitSignal)
Changes an attribute value (but does not commit it)
virtual bool addFeature(QgsVectorLayer *layer, 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:33
QgsFeatureRequest getRelatedFeaturesRequest(const QgsFeature &feature) const
Creates a request to return all the features on the referencing (child) layer which have a foreign ke...
int indexFromName(const QString &name) const
Look up field&#39;s index from name. Returns -1 on error.
Definition: qgsfield.h:239
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:137
QgsRelationEditorWidget(QWidget *parent=NULL)
const QString & referencingField() const
Get the name of the referencing field.
Definition: qgsrelation.h:50
void setRelationFeature(const QgsRelation &relation, const QgsFeature &feature)
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:230
QgsVectorLayer * layer() const
Returns the layer this model uses as backend.
void setEditorContext(const QgsAttributeEditorContext &context)
const QgsVectorLayerTools * vectorLayerTools() const
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:30
const QgsFields & pendingFields() const
returns field list in the to-be-committed state
QgsVectorDataProvider * dataProvider()
Returns the data provider.
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.
allows modification of attribute values
This widget is used to show the attributes of a set of features of a QgsVectorLayer.
Definition: qgsdualview.h:40
const QString & referencedField() const
Get the name of the referenced field.
Definition: qgsrelation.h:52
#define tr(sourceText)