QGIS API Documentation 3.27.0-Master (c6eca784ad)
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#include "qgsattributeform.h"
25#include "qgsgui.h"
27#include "qgsvectorlayerutils.h"
28
29#include <QLayout>
30#include <QLabel>
31#include <QStackedWidget>
32
34 : QgsAttributeFormWidget( editorWidget, form )
35 , mWidgetType( widgetType )
36 , mEditorWidget( editorWidget )
37 , mForm( form )
38 , mMultiEditButton( new QgsMultiEditToolButton() )
39 , mBlockValueUpdate( false )
40 , mIsMixed( false )
41 , mIsChanged( false )
42{
43 mConstraintResultLabel = new QLabel( this );
44 mConstraintResultLabel->setObjectName( QStringLiteral( "ConstraintStatus" ) );
45 mConstraintResultLabel->setSizePolicy( QSizePolicy::Fixed, mConstraintResultLabel->sizePolicy().verticalPolicy() );
46
47 mMultiEditButton->setField( mEditorWidget->field() );
48 mAggregateButton = new QgsAggregateToolButton();
49 mAggregateButton->setType( mEditorWidget->field().type() );
50 connect( mAggregateButton, &QgsAggregateToolButton::aggregateChanged, this, &QgsAttributeFormEditorWidget::onAggregateChanged );
51
52 if ( mEditorWidget->widget() )
53 {
54 mEditorWidget->widget()->setObjectName( mEditorWidget->field().name() );
55 }
56
57 connect( mEditorWidget, &QgsEditorWidgetWrapper::valuesChanged, this, &QgsAttributeFormEditorWidget::editorWidgetValuesChanged );
58
59 connect( mMultiEditButton, &QgsMultiEditToolButton::resetFieldValueTriggered, this, &QgsAttributeFormEditorWidget::resetValue );
60 connect( mMultiEditButton, &QgsMultiEditToolButton::setFieldValueTriggered, this, &QgsAttributeFormEditorWidget::setFieldTriggered );
61
62 mMultiEditButton->setField( mEditorWidget->field() );
63
64 updateWidgets();
65}
66
68{
69 //there's a chance these widgets are not currently added to the layout, so have no parent set
70 delete mMultiEditButton;
71}
72
74{
75 Q_ASSERT( !mWidgetType.isEmpty() );
76 const QVariantMap config = mEditorWidget->config();
77 const int fieldIdx = mEditorWidget->fieldIdx();
78
79 QgsSearchWidgetWrapper *sww = QgsGui::editorWidgetRegistry()->createSearchWidget( mWidgetType, layer(), fieldIdx, config,
80 searchWidgetFrame(), context );
82 searchWidgetFrame()->layout()->addWidget( mAggregateButton );
85 {
86 // create secondary widget for between type searches
87 QgsSearchWidgetWrapper *sww2 = QgsGui::editorWidgetRegistry()->createSearchWidget( mWidgetType, layer(), fieldIdx, config,
88 searchWidgetFrame(), context );
90 }
91}
92
93void QgsAttributeFormEditorWidget::setConstraintStatus( const QString &constraint, const QString &description, const QString &err, QgsEditorWidgetWrapper::ConstraintResult result )
94{
95 switch ( result )
96 {
98 mConstraintResultLabel->setText( QStringLiteral( "<font color=\"#FF9800\">%1</font>" ).arg( QChar( 0x2718 ) ) );
99 mConstraintResultLabel->setToolTip( description.isEmpty() ? QStringLiteral( "<b>%1</b>: %2" ).arg( constraint, err ) : description );
100 break;
101
103 mConstraintResultLabel->setText( QStringLiteral( "<font color=\"#FFC107\">%1</font>" ).arg( QChar( 0x2718 ) ) );
104 mConstraintResultLabel->setToolTip( description.isEmpty() ? QStringLiteral( "<b>%1</b>: %2" ).arg( constraint, err ) : description );
105 break;
106
108 mConstraintResultLabel->setText( QStringLiteral( "<font color=\"#259B24\">%1</font>" ).arg( QChar( 0x2714 ) ) );
109 mConstraintResultLabel->setToolTip( description );
110 break;
111 }
112}
113
115{
116 mConstraintResultLabel->setHidden( !editable );
117}
118
120{
121 return mEditorWidget;
122}
123
125{
126 if ( mEditorWidget && mixed )
127 mEditorWidget->showIndeterminateState();
128 mMultiEditButton->setIsMixed( mixed );
129 mIsMixed = mixed;
130}
131
133{
134 if ( mEditorWidget )
135 {
136 mPreviousValue = mEditorWidget->value();
137 mPreviousAdditionalValues = mEditorWidget->additionalFieldValues();
138 }
139
140 setIsMixed( false );
141 mMultiEditButton->changesCommitted();
142 mIsChanged = false;
143}
144
145
146
147void QgsAttributeFormEditorWidget::initialize( const QVariant &initialValue, bool mixedValues, const QVariantList &additionalFieldValues )
148{
149 if ( mEditorWidget )
150 {
151 mBlockValueUpdate = true;
152 mEditorWidget->setValues( initialValue, additionalFieldValues );
153 mBlockValueUpdate = false;
154 }
155 mPreviousValue = initialValue;
156 mPreviousAdditionalValues = additionalFieldValues;
157 setIsMixed( mixedValues );
158 mMultiEditButton->setIsChanged( false );
159 mIsChanged = false;
160 updateWidgets();
161}
162
164{
165 return mEditorWidget->value();
166}
167
168
169
170void QgsAttributeFormEditorWidget::editorWidgetValuesChanged( const QVariant &value, const QVariantList &additionalFieldValues )
171{
172 if ( mBlockValueUpdate )
173 return;
174
175 mIsChanged = true;
176
177 switch ( mode() )
178 {
179 case DefaultMode:
180 case SearchMode:
182 break;
183 case MultiEditMode:
184 mMultiEditButton->setIsChanged( true );
185 }
186
188 emit valueChanged( value );
190 emit valuesChanged( value, additionalFieldValues );
191}
192
193void QgsAttributeFormEditorWidget::resetValue()
194{
195 mIsChanged = false;
196 mBlockValueUpdate = true;
197 if ( mEditorWidget )
198 mEditorWidget->setValues( mPreviousValue, mPreviousAdditionalValues );
199 mBlockValueUpdate = false;
200
201 switch ( mode() )
202 {
203 case DefaultMode:
204 case SearchMode:
206 break;
207 case MultiEditMode:
208 {
209 mMultiEditButton->setIsChanged( false );
210 if ( mEditorWidget && mIsMixed )
211 mEditorWidget->showIndeterminateState();
212 break;
213 }
214 }
215}
216
217void QgsAttributeFormEditorWidget::setFieldTriggered()
218{
219 mIsChanged = true;
220}
221
222void QgsAttributeFormEditorWidget::onAggregateChanged()
223{
224 const auto constWigets( searchWidgetWrappers() );
225 for ( QgsSearchWidgetWrapper *searchWidget : constWigets )
226 searchWidget->setAggregate( mAggregateButton->aggregate() );
227}
228
229void QgsAttributeFormEditorWidget::updateWidgets()
230{
231 //first update the tool buttons
232 const bool hasMultiEditButton = ( editPage()->layout()->indexOf( mMultiEditButton ) >= 0 );
233
234 bool shouldShowMultiEditButton = false;
235 switch ( mode() )
236 {
240 // in these modes we don't show the multi edit button
241 shouldShowMultiEditButton = false;
242 break;
243
245 {
246 // in multi-edit mode we need to know upfront whether or not to allow add the multiedit buttons
247 // for this field.
248 // if the field is always read only regardless of the feature, no need to dig further. But otherwise
249 // we may need to test editability for the actual selected features...
250 const int fieldIndex = mEditorWidget->fieldIdx();
251 shouldShowMultiEditButton = !QgsVectorLayerUtils::fieldIsReadOnly( layer(), fieldIndex );
252 if ( shouldShowMultiEditButton )
253 {
254 // depending on the field type, the editability of the field may vary feature by feature (e.g. for joined
255 // fields coming from joins without the upsert on edit capabilities).
256 // But this feature-by-feature check is EXPENSIVE!!! (see https://github.com/qgis/QGIS/issues/41366), so
257 // avoid it whenever we can...
258 const bool fieldEditabilityDependsOnFeature = QgsVectorLayerUtils::fieldEditabilityDependsOnFeature( layer(), fieldIndex );
259 if ( fieldEditabilityDependsOnFeature )
260 {
261 QgsFeature feature;
263 while ( it.nextFeature( feature ) )
264 {
265 const bool isEditable = QgsVectorLayerUtils::fieldIsEditable( layer(), fieldIndex, feature );
266 if ( !isEditable )
267 {
268 // as soon as we find one read-only feature for the field, we can break early...
269 shouldShowMultiEditButton = false;
270 break;
271 }
272 }
273 }
274 }
275 }
276 break;
277 }
278
279 if ( hasMultiEditButton && !shouldShowMultiEditButton )
280 {
281 editPage()->layout()->removeWidget( mMultiEditButton );
282 mMultiEditButton->setParent( nullptr );
283 }
284 else if ( !hasMultiEditButton && shouldShowMultiEditButton )
285 {
286 editPage()->layout()->addWidget( mMultiEditButton );
287 }
288
289 switch ( mode() )
290 {
291 case DefaultMode:
292 case MultiEditMode:
293 {
294 stack()->setCurrentWidget( editPage() );
295 editPage()->layout()->addWidget( mConstraintResultLabel );
296 break;
297 }
298
300 {
301 mAggregateButton->setVisible( true );
302 stack()->setCurrentWidget( searchPage() );
303 break;
304 }
305
306 case SearchMode:
307 {
308 mAggregateButton->setVisible( false );
309 stack()->setCurrentWidget( searchPage() );
310 break;
311 }
312 }
313}
Offers a toolbutton to choose between different aggregate functions.
void setType(QVariant::Type type)
Based on the type of underlying data, some aggregates will be available or not.
QString aggregate() const
The function name of the selected aggregate or a Null String if none is chosen.
void aggregateChanged()
The function name of the selected aggregate has changed.
This class 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;.
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.
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.
Base class for all widgets shown on a QgsAttributeForm.
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.
QWidget * searchPage() const
Returns a pointer to the search page widget.
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.
QStackedWidget * stack() const
Returns a pointer to the stacked widget managing edit and search page.
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.
QgsSearchWidgetWrapper * createSearchWidget(const QString &widgetId, QgsVectorLayer *vl, int fieldIdx, const QVariantMap &config, QWidget *parent, const QgsAttributeEditorContext &context=QgsAttributeEditorContext())
Manages an editor widget Widget and wrapper share the same parent.
virtual QVariant value() const =0
Will be used to access the widget's value.
virtual QVariantList additionalFieldValues() const
Will be used to access the widget's values for potential additional fields handled by the widget.
int fieldIdx() const
Access the field index.
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.
void setValues(const QVariant &value, const QVariantList &additionalValues)
Is called when the value of the widget or additional field values needs to be changed.
QgsField field() const
Access the field.
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.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QString name
Definition: qgsfield.h:60
QVariant::Type type
Definition: qgsfield.h:58
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories.
Definition: qgsgui.cpp:83
A tool button widget which is displayed next to editor widgets in attribute forms,...
void setIsMixed(bool mixed)
Sets whether the associated field contains mixed values.
void changesCommitted()
Called when field values have been changed and field now contains all the same values.
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.
void setField(const QgsField &field)
Sets the field associated with this button.
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 fieldIsReadOnly(const QgsVectorLayer *layer, int fieldIndex)
QgsFeatureIterator getSelectedFeatures(QgsFeatureRequest request=QgsFeatureRequest()) const
Returns an iterator of the selected features.
QWidget * widget()
Access the widget managed by this wrapper.
QVariant config(const QString &key, const QVariant &defaultVal=QVariant()) const
Use this inside your overridden classes to access the configuration.
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:3041
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:3040