QGIS API Documentation  2.10.1-Pisa
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
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  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()->pendingFields();
211 
212  Q_FOREACH ( 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()->pendingFields().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.
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...
void setContentsMargins(int left, int top, int right, int bottom)
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
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:173
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.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:162
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:63
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: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 ...
virtual void addItem(QLayoutItem *item)
void setObjectName(const QString &name)
void setQgisRelation(QString qgisRelationId)
Defines the relation ID (from project relations)
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:62
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.
void setCheckable(bool)
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:38
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:303
int indexFromName(const QString &name) const
Look up field's index from name. Returns -1 on error.
Definition: qgsfield.cpp:336
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 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:236
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)
const QgsFields & pendingFields() const
returns field list in the to-be-committed state
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.
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
void addLayout(QLayout *layout, int stretch)