QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
qgsrelationwidgetwrapper.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrelationwidgetwrapper.cpp
3  --------------------------------------
4  Date : 14.5.2014
5  Copyright : (C) 2014 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 
20 #include "qgsproject.h"
21 #include "qgsrelationmanager.h"
24 #include "qgsgui.h"
25 #include <QWidget>
26 
27 QgsRelationWidgetWrapper::QgsRelationWidgetWrapper( QgsVectorLayer *vl, const QgsRelation &relation, QWidget *editor, QWidget *parent )
28  : QgsRelationWidgetWrapper( QStringLiteral( "relation_editor" ), vl, relation, editor, parent )
29 {
30 }
31 
32 QgsRelationWidgetWrapper::QgsRelationWidgetWrapper( const QString &relationEditorName, QgsVectorLayer *vl, const QgsRelation &relation, QWidget *editor, QWidget *parent )
33  : QgsWidgetWrapper( vl, editor, parent )
34  , mRelation( relation )
35  , mRelationEditorId( relationEditorName )
36 {
37 }
38 
39 QWidget *QgsRelationWidgetWrapper::createWidget( QWidget *parent )
40 {
41  QgsAttributeForm *form = qobject_cast<QgsAttributeForm *>( parent );
42  if ( form )
44 
45  QWidget *widget = QgsGui::instance()->relationWidgetRegistry()->create( mRelationEditorId, widgetConfig(), parent );
46 
47  if ( !widget )
48  {
49  QgsLogger::warning( QStringLiteral( "Failed to create relation widget \"%1\", fallback to \"basic\" relation widget" ).arg( mRelationEditorId ) );
50  widget = QgsGui::instance()->relationWidgetRegistry()->create( QStringLiteral( "relation_editor" ), widgetConfig(), parent );
51  }
52 
53  return widget;
54 }
55 
57 {
58  if ( mWidget && mRelation.isValid() )
59  mWidget->setFeature( feature );
60 }
61 
63 {
64  if ( mWidget )
65  mWidget->setVisible( visible );
66 }
67 
68 void QgsRelationWidgetWrapper::aboutToSave()
69 {
70  if ( !mRelation.isValid() || !widget() || !widget()->isVisible() || mRelation.referencingLayer() == mRelation.referencedLayer() )
71  return;
72 
73  // If the layer is already saved before, return
74  const QgsAttributeEditorContext *ctx = &context();
75  do
76  {
77  if ( ctx->relation().isValid() && ( ctx->relation().referencedLayer() == mRelation.referencingLayer()
78  || ( mNmRelation.isValid() && ctx->relation().referencedLayer() == mNmRelation.referencedLayer() ) )
79  )
80  {
81  return;
82  }
83  ctx = ctx->parentContext();
84  }
85  while ( ctx );
86 
87  // Calling isModified() will emit a beforeModifiedCheck()
88  // signal that will make the embedded form to send any
89  // outstanding widget changes to the edit buffer
90  mRelation.referencingLayer()->isModified();
91 
92  if ( mNmRelation.isValid() )
93  mNmRelation.referencedLayer()->isModified();
94 }
95 
97 {
98  return mRelation;
99 }
100 
101 void QgsRelationWidgetWrapper::widgetValueChanged( const QString &attribute, const QVariant &newValue, bool attributeChanged )
102 {
103  if ( mWidget && attributeChanged )
104  {
105  QgsFeature feature { mWidget->feature() };
106  if ( feature.attribute( attribute ) != newValue )
107  {
108  feature.setAttribute( attribute, newValue );
109  QgsAttributeEditorContext newContext { mWidget->editorContext() };
110  newContext.setParentFormFeature( feature );
111  mWidget->setEditorContext( newContext );
112  mWidget->setFeature( feature, false );
113  mWidget->parentFormValueChanged( attribute, newValue );
114  }
115  }
116 }
117 
119 {
121  return visibleButtons().testFlag( QgsAttributeEditorRelation::Button::Unlink );
123 }
124 
126 {
128  setVisibleButtons( visibleButtons().setFlag( QgsAttributeEditorRelation::Unlink, showUnlinkButton ) );
130 }
131 
132 void QgsRelationWidgetWrapper::setShowSaveChildEditsButton( bool showSaveChildEditsButton )
133 {
135  setVisibleButtons( visibleButtons().setFlag( QgsAttributeEditorRelation::SaveChildEdits, showSaveChildEditsButton ) );
137 }
138 
140 {
141  if ( mWidget )
142  {
143  return mWidget->showLabel();
144  }
145  return false;
146 }
147 
149 {
150  if ( mWidget )
151  mWidget->setShowLabel( showLabel );
152 }
153 
155 {
156  QgsAbstractRelationEditorWidget *w = qobject_cast<QgsAbstractRelationEditorWidget *>( editor );
157 
158  // if the editor cannot be cast to relation editor, insert a new one
159  if ( !w )
160  {
161  w = QgsGui::instance()->relationWidgetRegistry()->create( mRelationEditorId, widgetConfig(), editor );
162  editor->layout()->addWidget( w );
163  }
164 
166 
167  // read the legacy config of force-suppress-popup to support settings made on autoconfigurated forms
168  // it will be overwritten on specific widget configuration
169  if ( config( QStringLiteral( "force-suppress-popup" ), false ).toBool() )
170  {
171  const_cast<QgsVectorLayerTools *>( myContext.vectorLayerTools() )->setForceSuppressFormPopup( true );
172  }
173 
174  /* TODO: this seems to have no effect
175  if ( config( QStringLiteral( "hide-save-child-edits" ), false ).toBool() )
176  {
177  w->setShowSaveChildEditsButton( false );
178  }
179  */
180 
181  // read the legacy config of nm-rel to support settings made on autoconfigurated forms
182  // it will be overwritten on specific widget configuration
183  mNmRelation = QgsProject::instance()->relationManager()->relation( config( QStringLiteral( "nm-rel" ) ).toString() );
184 
185  // If this widget is already embedded by the same relation, reduce functionality
186  const QgsAttributeEditorContext *ctx = &context();
187  do
188  {
189  if ( ( ctx->relation().name() == mRelation.name() && ctx->formMode() == QgsAttributeEditorContext::Embed )
190  || ( mNmRelation.isValid() && ctx->relation().name() == mNmRelation.name() ) )
191  {
192  w->setVisible( false );
193  break;
194  }
195  ctx = ctx->parentContext();
196  }
197  while ( ctx );
198 
199  w->setEditorContext( myContext );
200  w->setRelations( mRelation, mNmRelation );
201 
202  mWidget = w;
203 }
204 
206 {
207  return mWidget;
208 }
209 
211 {
213  return visibleButtons().testFlag( QgsAttributeEditorRelation::Button::Link );
215 }
216 
218 {
220  setVisibleButtons( visibleButtons().setFlag( QgsAttributeEditorRelation::Link, showLinkButton ) );
222 }
223 
225 {
227  return visibleButtons().testFlag( QgsAttributeEditorRelation::Button::SaveChildEdits );
229 }
230 
231 void QgsRelationWidgetWrapper::setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons )
232 {
233  if ( ! mWidget )
234  return;
235  QVariantMap config = mWidget->config();
236  config.insert( "buttons", qgsFlagValueToKeys( buttons ) );
237 
238  mWidget->setConfig( config );
239 }
240 
241 QgsAttributeEditorRelation::Buttons QgsRelationWidgetWrapper::visibleButtons() const
242 {
243  return qgsFlagKeysToValue( mWidget->config().value( QStringLiteral( "buttons" ) ).toString(), QgsAttributeEditorRelation::AllButtons );
244 }
245 
246 void QgsRelationWidgetWrapper::setForceSuppressFormPopup( bool forceSuppressFormPopup )
247 {
248  if ( mWidget )
249  {
251  //it's set to true if one widget is configured like this but the setting is done generally (influencing all widgets).
253  {
254  const_cast<QgsVectorLayerTools *>( mWidget->editorContext().vectorLayerTools() )->setForceSuppressFormPopup( true );
255  }
256  }
257 }
258 
260 {
261  if ( mWidget )
262  return mWidget->forceSuppressFormPopup();
263 
264  return false;
265 }
266 
267 void QgsRelationWidgetWrapper::setNmRelationId( const QVariant &nmRelationId )
268 {
269  if ( mWidget )
270  {
271  mWidget->setNmRelationId( nmRelationId );
272 
273  mNmRelation = QgsProject::instance()->relationManager()->relation( nmRelationId.toString() );
274 
275  // If this widget is already embedded by the same relation, reduce functionality
276  const QgsAttributeEditorContext *ctx = &context();
277  do
278  {
279  if ( ( ctx->relation().name() == mRelation.name() && ctx->formMode() == QgsAttributeEditorContext::Embed )
280  || ( mNmRelation.isValid() && ctx->relation().name() == mNmRelation.name() ) )
281  {
282  mWidget->setVisible( false );
283  break;
284  }
285  ctx = ctx->parentContext();
286  }
287  while ( ctx );
288 
289  mWidget->setRelations( mRelation, mNmRelation );
290  }
291 }
292 
294 {
295  if ( mWidget )
296  return mWidget->nmRelationId();
297  return QVariant();
298 }
299 
300 
301 void QgsRelationWidgetWrapper::setLabel( const QString &label )
302 {
303  if ( mWidget )
304  mWidget->setLabel( label );
305 }
306 
308 {
309  if ( mWidget )
310  return mWidget->label();
311  return QString();
312 }
313 
314 void QgsRelationWidgetWrapper::setWidgetConfig( const QVariantMap &config )
315 {
316  if ( mWidget )
317  mWidget->setConfig( config );
318 }
319 
321 {
322  return mWidget ? mWidget->config() : QVariantMap();
323 }
Base class to build new relation widgets.
virtual void setConfig(const QVariantMap &config)=0
Defines the widget configuration.
bool forceSuppressFormPopup() const
Determines the force suppress form popup status that is configured for this widget.
virtual void setEditorContext(const QgsAttributeEditorContext &context)
Sets the editor context.
QString label() const
Determines the label of this element.
void setLabel(const QString &label=QString())
Sets label for this element If it's empty it takes the relation id as label.
void setRelations(const QgsRelation &relation, const QgsRelation &nmrelation)
Sets the relation(s) for this widget If only one relation is set, it will act as a simple 1:N relatio...
QgsAttributeEditorContext editorContext() const
Returns the attribute editor context.
bool showLabel() const
Defines if a title label should be shown for this widget.
virtual QVariantMap config() const =0
Returns the widget configuration.
virtual void parentFormValueChanged(const QString &attribute, const QVariant &newValue)=0
Called when an attribute value in the parent widget has changed to newValue.
void setFeature(const QgsFeature &feature, bool update=true)
Sets the feature being edited and updates the UI unless update is set to false.
QgsFeature feature() const
Returns the widget's current feature.
QVariant nmRelationId() const
Determines the relation id of the second relation involved in an N:M relation.
void setShowLabel(bool showLabel)
Defines if a title label should be shown for this widget.
void setForceSuppressFormPopup(bool forceSuppressFormPopup)
Sets force suppress form popup status with forceSuppressFormPopup configured for this widget.
void setNmRelationId(const QVariant &nmRelationId=QVariant())
Sets nmRelationId for the relation id of the second relation involved in an N:M relation.
This class contains context information for attribute editor widgets.
const QgsAttributeEditorContext * parentContext() const
FormMode formMode() const
Returns the form mode.
const QgsRelation & relation() const
Returns the attribute relation.
@ Multiple
When showing a list of features (e.g. houses as an embedded form in a district form)
@ Embed
A form was embedded as a widget on another form.
void setParentFormFeature(const QgsFeature &feature)
Sets the feature of the currently edited parent form.
const QgsVectorLayerTools * vectorLayerTools() const
Returns the associated vector layer tools.
void widgetValueChanged(const QString &attribute, const QVariant &value, bool attributeChanged)
Notifies about changes of attributes.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:56
bool setAttribute(int field, const QVariant &attr)
Set an attribute's value by field index.
Definition: qgsfeature.cpp:236
static QgsGui * instance()
Returns a pointer to the singleton instance.
Definition: qgsgui.cpp:65
static QgsRelationWidgetRegistry * relationWidgetRegistry()
Returns the global relation widget registry, used for managing all known relation widget factories.
Definition: qgsgui.cpp:81
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:122
QgsRelationManager * relationManager
Definition: qgsproject.h:109
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:501
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
QgsAbstractRelationEditorWidget * create(const QString &widgetType, const QVariantMap &config, QWidget *parent=nullptr) const
Create a relation widget of a given type for a given field.
bool showLabel() const
Defines if a title label should be shown for this widget.
void setVisible(bool visible)
Sets the visibility of the wrapper's widget.
Q_DECL_DEPRECATED void setVisibleButtons(const QgsAttributeEditorRelation::Buttons &buttons)
Defines the buttons which are shown.
QgsRelation relation() const
The relation for which this wrapper is created.
QVariantMap widgetConfig() const
Returns the whole widget config.
void setShowLabel(bool showLabel)
Defines if a title label should be shown for this widget.
void setLabel(const QString &label=QString())
Sets label for this element If it's empty it takes the relation id as label.
void setWidgetConfig(const QVariantMap &config)
Will set the config of this widget wrapper to the specified config.
void widgetValueChanged(const QString &attribute, const QVariant &newValue, bool attributeChanged)
Will be called when a value in the current edited form or table row changes.
void setForceSuppressFormPopup(bool forceSuppressFormPopup)
Sets force suppress form popup status to forceSuppressFormPopup for this widget and for the vectorLay...
void initWidget(QWidget *editor) override
This method should initialize the editor widget with runtime data.
Q_DECL_DEPRECATED bool showLinkButton() const
Determines if the "link feature" button should be shown.
Q_DECL_DEPRECATED bool showUnlinkButton() const
Determines if the "unlink feature" button should be shown.
bool forceSuppressFormPopup() const
Determines the force suppress form popup status that is configured for this widget.
bool valid() const override
Returns true if the widget has been properly initialized.
QWidget * createWidget(QWidget *parent) override
This method should create a new widget with the provided parent.
Q_DECL_DEPRECATED void setShowUnlinkButton(bool showUnlinkButton)
Determines if the "unlink feature" button should be shown.
Q_DECL_DEPRECATED QgsAttributeEditorRelation::Buttons visibleButtons() const
Returns the buttons which are shown.
QgsRelationWidgetWrapper(QgsVectorLayer *vl, const QgsRelation &relation, QWidget *editor=nullptr, QWidget *parent=nullptr)
Constructor for QgsRelationWidgetWrapper.
QString label() const
Determines the label of this element.
void setNmRelationId(const QVariant &nmRelationId=QVariant())
Sets nmRelationId for the relation id of the second relation involved in an N:M relation.
QVariant nmRelationId() const
Determines the relation id of the second relation involved in an N:M relation.
Q_DECL_DEPRECATED bool showSaveChildEditsButton() const
Determines if the "Save child layer edits" button should be shown.
Q_DECL_DEPRECATED void setShowSaveChildEditsButton(bool showChildEdits)
Determines if the "Save child layer edits" button should be shown.
Q_DECL_DEPRECATED void setShowLinkButton(bool showLinkButton)
Determines if the "link feature" button should be shown.
void setFeature(const QgsFeature &feature) override
QString name
Definition: qgsrelation.h:49
QgsVectorLayer * referencedLayer
Definition: qgsrelation.h:48
QgsVectorLayer * referencingLayer
Definition: qgsrelation.h:47
bool isValid
Definition: qgsrelation.h:50
Methods in this class are used to handle basic operations on vector layers.
Represents a vector layer which manages a vector based data sets.
virtual bool isModified() const
Returns true if the provider has been modified since the last commit.
Manages an editor widget Widget and wrapper share the same parent.
QWidget * widget()
Access the widget managed by this wrapper.
const QgsAttributeEditorContext & context() const
Returns information about the context in which this widget is shown.
QVariantMap config() const
Returns the whole config.
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:798
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:797
T qgsFlagKeysToValue(const QString &keys, const T &defaultValue)
Returns the value corresponding to the given keys of a flag.
Definition: qgis.h:562
QString qgsFlagValueToKeys(const T &value)
Returns the value for the given keys of a flag.
Definition: qgis.h:550