QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
Loading...
Searching...
No Matches
qgsattributeformeditorwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsattributeformeditorwidget.cpp
3 -------------------------------
4 Date : March 2016
5 Copyright : (C) 2016 Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
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
19#include "qgsapplication.h"
21#include "qgsattributeform.h"
24#include "qgsgui.h"
27#include "qgsvectorlayerutils.h"
28
29#include <QLabel>
30#include <QLayout>
31#include <QStackedWidget>
32#include <QString>
33
34#include "moc_qgsattributeformeditorwidget.cpp"
35
36using namespace Qt::StringLiterals;
37
40 , mWidgetType( widgetType )
41 , mEditorWidget( editorWidget )
42 , mForm( form )
43 , mMultiEditButton( new QgsMultiEditToolButton() )
44{
45 mRememberLastValueButton = new QToolButton();
46 mRememberLastValueButton->setAutoRaise( true );
47 mRememberLastValueButton->setCheckable( true );
48 mRememberLastValueButton->setIcon( QgsApplication::getThemeIcon( u"/mIconRememberDisabled.svg"_s ) );
49 mRememberLastValueButton->setToolTip( tr( "When enabled, the entered value will be remembered and reused for the next feature additions" ) );
50 updateRememberWidget();
51
52 connect( mRememberLastValueButton, &QAbstractButton::toggled, this, [this]( bool checked ) {
53 mRememberLastValueButton->setIcon( QgsApplication::getThemeIcon( checked ? u"/mIconRememberEnabled.svg"_s : u"/mIconRememberDisabled.svg"_s ) );
54 emit rememberLastValueChanged( mEditorWidget->fieldIdx(), checked );
55 } );
56 connect( mForm, &QgsAttributeForm::modeChanged, this, [this]( QgsAttributeEditorContext::Mode ) { updateRememberWidget(); } );
57
58 mConstraintResultLabel = new QLabel();
59 mConstraintResultLabel->setObjectName( u"ConstraintStatus"_s );
60 mConstraintResultLabel->setSizePolicy( QSizePolicy::Fixed, mConstraintResultLabel->sizePolicy().verticalPolicy() );
61 mConstraintResultLabel->setAlignment( Qt::AlignCenter );
62 mConstraintResultLabel->setFixedWidth( 24 );
63
64 mAggregateButton = new QgsAggregateToolButton();
65 mAggregateButton->setType( mEditorWidget->field().type() );
66 connect( mAggregateButton, &QgsAggregateToolButton::aggregateChanged, this, &QgsAttributeFormEditorWidget::onAggregateChanged );
67
68 if ( mEditorWidget->widget() )
69 {
70 mEditorWidget->widget()->setObjectName( mEditorWidget->field().name() );
71 }
72
73 connect( mEditorWidget, &QgsEditorWidgetWrapper::valuesChanged, this, &QgsAttributeFormEditorWidget::editorWidgetValuesChanged );
74
75 mMultiEditButton->setField( mEditorWidget->field() );
76 connect( mMultiEditButton, &QgsMultiEditToolButton::resetFieldValueTriggered, this, &QgsAttributeFormEditorWidget::resetValue );
77 connect( mMultiEditButton, &QgsMultiEditToolButton::setFieldValueTriggered, this, &QgsAttributeFormEditorWidget::setFieldTriggered );
78
79 updateWidgets();
80}
81
83{
84 //there's a chance these widgets are not currently added to the layout, so have no parent set
85 delete mMultiEditButton;
86 delete mRememberLastValueButton;
87 delete mConstraintResultLabel;
88}
89
91{
92 Q_ASSERT( !mWidgetType.isEmpty() );
93 const QVariantMap config = mEditorWidget->config();
94 const int fieldIdx = mEditorWidget->fieldIdx();
95
96 QgsSearchWidgetWrapper *sww = QgsGui::editorWidgetRegistry()->createSearchWidget( mWidgetType, layer(), fieldIdx, config, searchWidgetFrame(), context );
98 searchWidgetFrame()->layout()->addWidget( mAggregateButton );
100 {
101 // create secondary widget for between type searches
102 QgsSearchWidgetWrapper *sww2 = QgsGui::editorWidgetRegistry()->createSearchWidget( mWidgetType, layer(), fieldIdx, config, searchWidgetFrame(), context );
104 }
105}
106
107void QgsAttributeFormEditorWidget::setConstraintStatus( const QString &constraint, const QString &description, const QString &err, QgsEditorWidgetWrapper::ConstraintResult result )
108{
109 switch ( result )
110 {
112 mConstraintResultLabel->setText( u"<font color=\"#FF9800\">%1</font>"_s.arg( QChar( 0x2718 ) ) );
113 mConstraintResultLabel->setToolTip( description.isEmpty() ? u"<b>%1</b>: %2"_s.arg( constraint, err ) : description );
114 break;
115
117 mConstraintResultLabel->setText( u"<font color=\"#FFC107\">%1</font>"_s.arg( QChar( 0x2718 ) ) );
118 mConstraintResultLabel->setToolTip( description.isEmpty() ? u"<b>%1</b>: %2"_s.arg( constraint, err ) : description );
119 break;
120
122 mConstraintResultLabel->setText( u"<font color=\"#259B24\">%1</font>"_s.arg( QChar( 0x2714 ) ) );
123 mConstraintResultLabel->setToolTip( description );
124 break;
125 }
126}
127
129{
130 mIsConstraintResultVisible = editable;
131
132 switch ( mode() )
133 {
134 case SearchMode:
136 return;
137 case DefaultMode:
138 case MultiEditMode:
139 break;
140 }
141
142 if ( !layer() || QgsVectorLayerUtils::attributeHasConstraints( layer(), mEditorWidget->fieldIdx() ) )
143 {
144 const bool hasConstraintResultLabel = ( editPage()->layout()->indexOf( mConstraintResultLabel ) >= 0 );
145 if ( editable && !hasConstraintResultLabel )
146 {
147 editPage()->layout()->addWidget( mConstraintResultLabel );
148 }
149 else if ( !editable && hasConstraintResultLabel )
150 {
151 editPage()->layout()->removeWidget( mConstraintResultLabel );
152 mConstraintResultLabel->setParent( nullptr );
153 }
154 }
155}
156
158{
159 return mEditorWidget;
160}
161
163{
164 if ( mEditorWidget && mixed )
165 mEditorWidget->showIndeterminateState();
166 mMultiEditButton->setIsMixed( mixed );
167 mIsMixed = mixed;
168}
169
171{
172 mRememberLastValueButton->setChecked( remember );
173 mRememberLastValueButton->setIcon( QgsApplication::getThemeIcon( remember ? u"/mIconRememberEnabled.svg"_s : u"/mIconRememberDisabled.svg"_s ) );
174}
175
177{
178 if ( mEditorWidget )
179 {
180 mPreviousValue = mEditorWidget->value();
181 mPreviousAdditionalValues = mEditorWidget->additionalFieldValues();
182 }
183
184 setIsMixed( false );
185 mMultiEditButton->changesCommitted();
186 mIsChanged = false;
187}
188
189
190void QgsAttributeFormEditorWidget::initialize( const QVariant &initialValue, bool mixedValues, const QVariantList &additionalFieldValues )
191{
192 if ( mEditorWidget )
193 {
194 mBlockValueUpdate = true;
195 mEditorWidget->setValues( initialValue, additionalFieldValues );
196 mBlockValueUpdate = false;
197 }
198 mPreviousValue = initialValue;
199 mPreviousAdditionalValues = additionalFieldValues;
200 setIsMixed( mixedValues );
201 mMultiEditButton->setIsChanged( false );
202 mIsChanged = false;
203 updateWidgets();
204}
205
207{
208 return mEditorWidget->value();
209}
210
211
212void QgsAttributeFormEditorWidget::editorWidgetValuesChanged( const QVariant &value, const QVariantList &additionalFieldValues )
213{
214 if ( mBlockValueUpdate )
215 return;
216
217 mIsChanged = true;
218
219 switch ( mode() )
220 {
221 case DefaultMode:
222 case SearchMode:
224 break;
225 case MultiEditMode:
226 mMultiEditButton->setIsChanged( true );
227 }
228
230 emit valueChanged( value );
232 emit valuesChanged( value, additionalFieldValues );
233}
234
235void QgsAttributeFormEditorWidget::resetValue()
236{
237 mIsChanged = false;
238 mBlockValueUpdate = true;
239 if ( mEditorWidget )
240 mEditorWidget->setValues( mPreviousValue, mPreviousAdditionalValues );
241 mBlockValueUpdate = false;
242
243 switch ( mode() )
244 {
245 case DefaultMode:
246 case SearchMode:
248 break;
249 case MultiEditMode:
250 {
251 mMultiEditButton->setIsChanged( false );
252 if ( mEditorWidget && mIsMixed )
253 mEditorWidget->showIndeterminateState();
254 break;
255 }
256 }
257}
258
259void QgsAttributeFormEditorWidget::setFieldTriggered()
260{
261 mIsChanged = true;
262}
263
264void QgsAttributeFormEditorWidget::onAggregateChanged()
265{
266 const auto constWigets( searchWidgetWrappers() );
267 for ( QgsSearchWidgetWrapper *searchWidget : constWigets )
268 searchWidget->setAggregate( mAggregateButton->aggregate() );
269}
270
271void QgsAttributeFormEditorWidget::updateRememberWidget()
272{
273 const bool hasRememberButton = ( editPage()->layout()->indexOf( mRememberLastValueButton ) >= 0 );
274 const int idx = mEditorWidget->fieldIdx();
275 if ( !hasRememberButton && form() && form()->mode() == QgsAttributeEditorContext::AddFeatureMode )
276 {
277 if ( layer() && layer()->editFormConfig().reuseLastValuePolicy( idx ) != Qgis::AttributeFormReuseLastValuePolicy::NotAllowed )
278 {
279 editPage()->layout()->addWidget( mRememberLastValueButton );
280 }
281 }
282 else if ( hasRememberButton )
283 {
284 editPage()->layout()->removeWidget( mRememberLastValueButton );
285 mRememberLastValueButton->setParent( nullptr );
286 }
287}
288
289void QgsAttributeFormEditorWidget::updateWidgets()
290{
291 //first update the tool buttons
292 const bool hasMultiEditButton = ( editPage()->layout()->indexOf( mMultiEditButton ) >= 0 );
293
294 bool shouldShowMultiEditButton = false;
295 switch ( mode() )
296 {
300 // in these modes we don't show the multi edit button
301 shouldShowMultiEditButton = false;
302 break;
303
305 {
306 // in multi-edit mode we need to know upfront whether or not to allow add the multiedit buttons
307 // for this field.
308 // if the field is always read only regardless of the feature, no need to dig further. But otherwise
309 // we may need to test editability for the actual selected features...
310 if ( mEditorWidget )
311 {
312 const int fieldIndex = mEditorWidget->fieldIdx();
313 shouldShowMultiEditButton = !QgsVectorLayerUtils::fieldIsReadOnly( layer(), fieldIndex );
314 if ( shouldShowMultiEditButton )
315 {
316 // depending on the field type, the editability of the field may vary feature by feature (e.g. for joined
317 // fields coming from joins without the upsert on edit capabilities).
318 // But this feature-by-feature check is EXPENSIVE!!! (see https://github.com/qgis/QGIS/issues/41366), so
319 // avoid it whenever we can...
320 const bool fieldEditabilityDependsOnFeature = QgsVectorLayerUtils::fieldEditabilityDependsOnFeature( layer(), fieldIndex );
321 if ( fieldEditabilityDependsOnFeature )
322 {
323 QgsFeature feature;
324 QgsFeatureIterator it = layer()->getSelectedFeatures();
325 while ( it.nextFeature( feature ) )
326 {
327 const bool isEditable = QgsVectorLayerUtils::fieldIsEditable( layer(), fieldIndex, feature );
328 if ( !isEditable )
329 {
330 // as soon as we find one read-only feature for the field, we can break early...
331 shouldShowMultiEditButton = false;
332 break;
333 }
334 }
335 }
336 }
337 }
338 }
339 break;
340 }
341
342 if ( hasMultiEditButton && !shouldShowMultiEditButton )
343 {
344 editPage()->layout()->removeWidget( mMultiEditButton );
345 mMultiEditButton->setParent( nullptr );
346 }
347 else if ( !hasMultiEditButton && shouldShowMultiEditButton )
348 {
349 editPage()->layout()->addWidget( mMultiEditButton );
350 }
351
353
354 switch ( mode() )
355 {
356 case DefaultMode:
357 case MultiEditMode:
358 {
359 if ( mIsConstraintResultVisible && editPage()->layout()->indexOf( mConstraintResultLabel ) == -1 )
360 {
361 if ( !layer() || ( mEditorWidget && QgsVectorLayerUtils::attributeHasConstraints( layer(), mEditorWidget->fieldIdx() ) ) )
362 {
363 editPage()->layout()->addWidget( mConstraintResultLabel );
364 }
365 }
366 break;
367 }
368
370 {
371 mAggregateButton->setVisible( true );
372 break;
373 }
374
375 case SearchMode:
376 {
377 mAggregateButton->setVisible( false );
378 break;
379 }
380 }
381}
@ NotAllowed
Reuse of last values not allowed.
Definition qgis.h:5886
Offers a toolbutton to choose between different aggregate functions.
void aggregateChanged()
The function name of the selected aggregate has changed.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Contains context information for attribute editor widgets.
QgsAttributeFormEditorWidget(QgsEditorWidgetWrapper *editorWidget, const QString &widgetType, QgsAttributeForm *form)
Constructor for QgsAttributeFormEditorWidget.
void initialize(const QVariant &initialValue, bool mixedValues=false, const QVariantList &additionalFieldValues=QVariantList())
Resets the widget to an initial value.
void valuesChanged(const QVariant &value, const QVariantList &additionalFieldValues)
Emitted when the widget's value changes.
void changesCommitted()
Called when field values have been committed;.
void setRememberLastValue(bool remember)
Sets whether the widget value will be remembered for potential reuse when creating new features.
Q_DECL_DEPRECATED void valueChanged(const QVariant &value)
Emitted when the widget's value changes.
QVariant currentValue() const
Returns the current value of the attached editor widget.
void rememberLastValueChanged(int index, bool remember)
Emitted when the widget's remember last value toggle changes.
QgsEditorWidgetWrapper * editorWidget() const
Returns the editor widget wrapper.
void setConstraintStatus(const QString &constraint, const QString &description, const QString &err, QgsEditorWidgetWrapper::ConstraintResult result)
Set the constraint status for this widget.
void setIsMixed(bool mixed)
Sets whether the widget should be displayed in a "mixed values" mode.
void createSearchWidgetWrappers(const QgsAttributeEditorContext &context=QgsAttributeEditorContext()) override
Creates the search widget wrappers for the widget used when the form is in search mode.
void setConstraintResultVisible(bool editable)
Set the constraint result label visible or invisible according to the layer editable status.
void setSearchWidgetWrapper(QgsSearchWidgetWrapper *wrapper)
Sets the search widget wrapper for the widget used when the form is in search mode.
QList< QgsSearchWidgetWrapper * > searchWidgetWrappers()
Returns the search widget wrapper used in this widget.
QgsAttributeForm * form() const
The form on which this widget is shown.
void setVisiblePageForMode(QgsAttributeFormWidget::Mode mode)
Sets the visible page in the widget to the page matching the specified mode.
void addAdditionalSearchWidgetWrapper(QgsSearchWidgetWrapper *wrapper)
Adds an additional search widget wrapper.
QWidget * searchWidgetFrame()
Returns the widget which should be used as a parent during construction of the search widget wrapper.
QWidget * editPage() const
Returns a pointer to the EDIT page widget.
QgsVectorLayer * layer()
The layer for which this widget and its form is shown.
Mode mode() const
Returns the current mode for the widget.
@ SearchMode
Layer search/filter mode.
@ MultiEditMode
Multi edit mode, both the editor widget and a QgsMultiEditToolButton is shown.
@ DefaultMode
Default mode, only the editor widget is shown.
@ AggregateSearchMode
Embedded in a search form, show additional aggregate function toolbutton.
QgsAttributeFormWidget(QgsWidgetWrapper *widget, QgsAttributeForm *form)
A new form widget for the wrapper widget on form.
The attribute form widget for vector layer features.
void modeChanged(QgsAttributeEditorContext::Mode mode)
Emitted when the form changes mode.
QgsSearchWidgetWrapper * createSearchWidget(const QString &widgetId, QgsVectorLayer *vl, int fieldIdx, const QVariantMap &config, QWidget *parent, const QgsAttributeEditorContext &context=QgsAttributeEditorContext())
Manages an editor widget.
virtual void showIndeterminateState()
Sets the widget to display in an indeterminate "mixed value" state.
void valuesChanged(const QVariant &value, const QVariantList &additionalFieldValues=QVariantList())
Emit this signal, whenever the value changed.
ConstraintResult
Result of constraint checks.
@ ConstraintResultFailSoft
Widget failed at least one soft (non-enforced) constraint.
@ ConstraintResultPass
Widget passed constraints successfully.
@ ConstraintResultFailHard
Widget failed at least one hard (enforced) constraint.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories.
Definition qgsgui.cpp:109
A tool button for controlling how edits to multiple features are applied.
void setIsChanged(bool changed)
Sets whether the associated field has changed.
void resetFieldValueTriggered()
Emitted when the "reset to original values" option is selected.
void setFieldValueTriggered()
Emitted when the "set field value for all features" option is selected.
Shows a search widget on a filter form.
@ IsNotBetween
Supports searching for values outside of a set range.
@ Between
Supports searches between two values.
virtual FilterFlags supportedFlags() const
Returns filter flags supported by the search widget.
static bool fieldIsEditable(const QgsVectorLayer *layer, int fieldIndex, const QgsFeature &feature, QgsVectorLayerUtils::FieldIsEditableFlags flags=QgsVectorLayerUtils::FieldIsEditableFlags())
Tests whether a field is editable for a particular feature.
static bool fieldEditabilityDependsOnFeature(const QgsVectorLayer *layer, int fieldIndex)
Returns true if the editability of the field at index fieldIndex from layer may vary feature by featu...
static bool attributeHasConstraints(const QgsVectorLayer *layer, int attributeIndex)
Returns true if a feature attribute has active constraints.
static bool fieldIsReadOnly(const QgsVectorLayer *layer, int fieldIndex)
Returns true if the field at index fieldIndex from layer is editable, false if the field is read only...
QgsFeatureIterator getSelectedFeatures(QgsFeatureRequest request=QgsFeatureRequest()) const
Returns an iterator of the selected features.
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:7504
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7503