QGIS API Documentation 3.99.0-Master (26c88405ac0)
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
33#include "moc_qgsattributeformeditorwidget.cpp"
34
37 , mWidgetType( widgetType )
38 , mEditorWidget( editorWidget )
39 , mForm( form )
40 , mMultiEditButton( new QgsMultiEditToolButton() )
41 , mBlockValueUpdate( false )
42 , mIsMixed( false )
43 , mIsChanged( false )
44{
45 mRememberLastValueButton = new QToolButton();
46 mRememberLastValueButton->setAutoRaise( true );
47 mRememberLastValueButton->setCheckable( true );
48 mRememberLastValueButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconRememberDisabled.svg" ) ) );
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 ? QStringLiteral( "/mIconRememberEnabled.svg" ) : QStringLiteral( "/mIconRememberDisabled.svg" ) ) );
54 emit rememberLastValueChanged( mEditorWidget->fieldIdx(), checked );
55 } );
56 connect( mForm, &QgsAttributeForm::modeChanged, this, [this]( QgsAttributeEditorContext::Mode ) {
57 updateRememberWidget();
58 } );
59
60 mConstraintResultLabel = new QLabel();
61 mConstraintResultLabel->setObjectName( QStringLiteral( "ConstraintStatus" ) );
62 mConstraintResultLabel->setSizePolicy( QSizePolicy::Fixed, mConstraintResultLabel->sizePolicy().verticalPolicy() );
63 mConstraintResultLabel->setAlignment( Qt::AlignCenter );
64 mConstraintResultLabel->setFixedWidth( 24 );
65
66 mAggregateButton = new QgsAggregateToolButton();
67 mAggregateButton->setType( mEditorWidget->field().type() );
68 connect( mAggregateButton, &QgsAggregateToolButton::aggregateChanged, this, &QgsAttributeFormEditorWidget::onAggregateChanged );
69
70 if ( mEditorWidget->widget() )
71 {
72 mEditorWidget->widget()->setObjectName( mEditorWidget->field().name() );
73 }
74
75 connect( mEditorWidget, &QgsEditorWidgetWrapper::valuesChanged, this, &QgsAttributeFormEditorWidget::editorWidgetValuesChanged );
76
77 mMultiEditButton->setField( mEditorWidget->field() );
78 connect( mMultiEditButton, &QgsMultiEditToolButton::resetFieldValueTriggered, this, &QgsAttributeFormEditorWidget::resetValue );
79 connect( mMultiEditButton, &QgsMultiEditToolButton::setFieldValueTriggered, this, &QgsAttributeFormEditorWidget::setFieldTriggered );
80
81 updateWidgets();
82}
83
85{
86 //there's a chance these widgets are not currently added to the layout, so have no parent set
87 delete mMultiEditButton;
88 delete mRememberLastValueButton;
89 delete mConstraintResultLabel;
90}
91
93{
94 Q_ASSERT( !mWidgetType.isEmpty() );
95 const QVariantMap config = mEditorWidget->config();
96 const int fieldIdx = mEditorWidget->fieldIdx();
97
98 QgsSearchWidgetWrapper *sww = QgsGui::editorWidgetRegistry()->createSearchWidget( mWidgetType, layer(), fieldIdx, config, searchWidgetFrame(), context );
100 searchWidgetFrame()->layout()->addWidget( mAggregateButton );
102 {
103 // create secondary widget for between type searches
104 QgsSearchWidgetWrapper *sww2 = QgsGui::editorWidgetRegistry()->createSearchWidget( mWidgetType, layer(), fieldIdx, config, searchWidgetFrame(), context );
106 }
107}
108
109void QgsAttributeFormEditorWidget::setConstraintStatus( const QString &constraint, const QString &description, const QString &err, QgsEditorWidgetWrapper::ConstraintResult result )
110{
111 switch ( result )
112 {
114 mConstraintResultLabel->setText( QStringLiteral( "<font color=\"#FF9800\">%1</font>" ).arg( QChar( 0x2718 ) ) );
115 mConstraintResultLabel->setToolTip( description.isEmpty() ? QStringLiteral( "<b>%1</b>: %2" ).arg( constraint, err ) : description );
116 break;
117
119 mConstraintResultLabel->setText( QStringLiteral( "<font color=\"#FFC107\">%1</font>" ).arg( QChar( 0x2718 ) ) );
120 mConstraintResultLabel->setToolTip( description.isEmpty() ? QStringLiteral( "<b>%1</b>: %2" ).arg( constraint, err ) : description );
121 break;
122
124 mConstraintResultLabel->setText( QStringLiteral( "<font color=\"#259B24\">%1</font>" ).arg( QChar( 0x2714 ) ) );
125 mConstraintResultLabel->setToolTip( description );
126 break;
127 }
128}
129
131{
132 mIsConstraintResultVisible = editable;
133
134 switch ( mode() )
135 {
136 case SearchMode:
138 return;
139 case DefaultMode:
140 case MultiEditMode:
141 break;
142 }
143
144 if ( !layer() || QgsVectorLayerUtils::attributeHasConstraints( layer(), mEditorWidget->fieldIdx() ) )
145 {
146 const bool hasConstraintResultLabel = ( editPage()->layout()->indexOf( mConstraintResultLabel ) >= 0 );
147 if ( editable && !hasConstraintResultLabel )
148 {
149 editPage()->layout()->addWidget( mConstraintResultLabel );
150 }
151 else if ( !editable && hasConstraintResultLabel )
152 {
153 editPage()->layout()->removeWidget( mConstraintResultLabel );
154 mConstraintResultLabel->setParent( nullptr );
155 }
156 }
157}
158
160{
161 return mEditorWidget;
162}
163
165{
166 if ( mEditorWidget && mixed )
167 mEditorWidget->showIndeterminateState();
168 mMultiEditButton->setIsMixed( mixed );
169 mIsMixed = mixed;
170}
171
173{
174 mRememberLastValueButton->setChecked( remember );
175 mRememberLastValueButton->setIcon( QgsApplication::getThemeIcon( remember ? QStringLiteral( "/mIconRememberEnabled.svg" ) : QStringLiteral( "/mIconRememberDisabled.svg" ) ) );
176}
177
179{
180 if ( mEditorWidget )
181 {
182 mPreviousValue = mEditorWidget->value();
183 mPreviousAdditionalValues = mEditorWidget->additionalFieldValues();
184 }
185
186 setIsMixed( false );
187 mMultiEditButton->changesCommitted();
188 mIsChanged = false;
189}
190
191
192void QgsAttributeFormEditorWidget::initialize( const QVariant &initialValue, bool mixedValues, const QVariantList &additionalFieldValues )
193{
194 if ( mEditorWidget )
195 {
196 mBlockValueUpdate = true;
197 mEditorWidget->setValues( initialValue, additionalFieldValues );
198 mBlockValueUpdate = false;
199 }
200 mPreviousValue = initialValue;
201 mPreviousAdditionalValues = additionalFieldValues;
202 setIsMixed( mixedValues );
203 mMultiEditButton->setIsChanged( false );
204 mIsChanged = false;
205 updateWidgets();
206}
207
209{
210 return mEditorWidget->value();
211}
212
213
214void QgsAttributeFormEditorWidget::editorWidgetValuesChanged( const QVariant &value, const QVariantList &additionalFieldValues )
215{
216 if ( mBlockValueUpdate )
217 return;
218
219 mIsChanged = true;
220
221 switch ( mode() )
222 {
223 case DefaultMode:
224 case SearchMode:
226 break;
227 case MultiEditMode:
228 mMultiEditButton->setIsChanged( true );
229 }
230
232 emit valueChanged( value );
234 emit valuesChanged( value, additionalFieldValues );
235}
236
237void QgsAttributeFormEditorWidget::resetValue()
238{
239 mIsChanged = false;
240 mBlockValueUpdate = true;
241 if ( mEditorWidget )
242 mEditorWidget->setValues( mPreviousValue, mPreviousAdditionalValues );
243 mBlockValueUpdate = false;
244
245 switch ( mode() )
246 {
247 case DefaultMode:
248 case SearchMode:
250 break;
251 case MultiEditMode:
252 {
253 mMultiEditButton->setIsChanged( false );
254 if ( mEditorWidget && mIsMixed )
255 mEditorWidget->showIndeterminateState();
256 break;
257 }
258 }
259}
260
261void QgsAttributeFormEditorWidget::setFieldTriggered()
262{
263 mIsChanged = true;
264}
265
266void QgsAttributeFormEditorWidget::onAggregateChanged()
267{
268 const auto constWigets( searchWidgetWrappers() );
269 for ( QgsSearchWidgetWrapper *searchWidget : constWigets )
270 searchWidget->setAggregate( mAggregateButton->aggregate() );
271}
272
273void QgsAttributeFormEditorWidget::updateRememberWidget()
274{
275 const bool hasRememberButton = ( editPage()->layout()->indexOf( mRememberLastValueButton ) >= 0 );
276 const int idx = mEditorWidget->fieldIdx();
277 if ( !hasRememberButton && form() && form()->mode() == QgsAttributeEditorContext::AddFeatureMode )
278 {
279 if ( layer() && layer()->editFormConfig().reuseLastValuePolicy( idx ) != Qgis::AttributeFormReuseLastValuePolicy::NotAllowed )
280 {
281 editPage()->layout()->addWidget( mRememberLastValueButton );
282 }
283 }
284 else if ( hasRememberButton )
285 {
286 editPage()->layout()->removeWidget( mRememberLastValueButton );
287 mRememberLastValueButton->setParent( nullptr );
288 }
289}
290
291void QgsAttributeFormEditorWidget::updateWidgets()
292{
293 //first update the tool buttons
294 const bool hasMultiEditButton = ( editPage()->layout()->indexOf( mMultiEditButton ) >= 0 );
295
296 bool shouldShowMultiEditButton = false;
297 switch ( mode() )
298 {
302 // in these modes we don't show the multi edit button
303 shouldShowMultiEditButton = false;
304 break;
305
307 {
308 // in multi-edit mode we need to know upfront whether or not to allow add the multiedit buttons
309 // for this field.
310 // if the field is always read only regardless of the feature, no need to dig further. But otherwise
311 // we may need to test editability for the actual selected features...
312 if ( mEditorWidget )
313 {
314 const int fieldIndex = mEditorWidget->fieldIdx();
315 shouldShowMultiEditButton = !QgsVectorLayerUtils::fieldIsReadOnly( layer(), fieldIndex );
316 if ( shouldShowMultiEditButton )
317 {
318 // depending on the field type, the editability of the field may vary feature by feature (e.g. for joined
319 // fields coming from joins without the upsert on edit capabilities).
320 // But this feature-by-feature check is EXPENSIVE!!! (see https://github.com/qgis/QGIS/issues/41366), so
321 // avoid it whenever we can...
322 const bool fieldEditabilityDependsOnFeature = QgsVectorLayerUtils::fieldEditabilityDependsOnFeature( layer(), fieldIndex );
323 if ( fieldEditabilityDependsOnFeature )
324 {
325 QgsFeature feature;
326 QgsFeatureIterator it = layer()->getSelectedFeatures();
327 while ( it.nextFeature( feature ) )
328 {
329 const bool isEditable = QgsVectorLayerUtils::fieldIsEditable( layer(), fieldIndex, feature );
330 if ( !isEditable )
331 {
332 // as soon as we find one read-only feature for the field, we can break early...
333 shouldShowMultiEditButton = false;
334 break;
335 }
336 }
337 }
338 }
339 }
340 }
341 break;
342 }
343
344 if ( hasMultiEditButton && !shouldShowMultiEditButton )
345 {
346 editPage()->layout()->removeWidget( mMultiEditButton );
347 mMultiEditButton->setParent( nullptr );
348 }
349 else if ( !hasMultiEditButton && shouldShowMultiEditButton )
350 {
351 editPage()->layout()->addWidget( mMultiEditButton );
352 }
353
355
356 switch ( mode() )
357 {
358 case DefaultMode:
359 case MultiEditMode:
360 {
361 if ( mIsConstraintResultVisible && editPage()->layout()->indexOf( mConstraintResultLabel ) == -1 )
362 {
363 if ( !layer() || ( mEditorWidget && QgsVectorLayerUtils::attributeHasConstraints( layer(), mEditorWidget->fieldIdx() ) ) )
364 {
365 editPage()->layout()->addWidget( mConstraintResultLabel );
366 }
367 }
368 break;
369 }
370
372 {
373 mAggregateButton->setVisible( true );
374 break;
375 }
376
377 case SearchMode:
378 {
379 mAggregateButton->setVisible( false );
380 break;
381 }
382 }
383}
@ NotAllowed
Reuse of last values not allowed.
Definition qgis.h:5530
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:106
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 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 fieldIsEditable(const QgsVectorLayer *layer, int fieldIndex, const QgsFeature &feature)
Tests whether a field is editable for a particular feature.
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:7170
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7169