QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
qgsrangewidgetwrapper.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrangewidgetwrapper.cpp
3 --------------------------------------
4 Date : 5.1.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
18#include "qgsapplication.h"
19#include "qgsdial.h"
20#include "qgsdoublespinbox.h"
21#include "qgsslider.h"
22#include "qgsspinbox.h"
23#include "qgsvectorlayer.h"
24
25#include <QSettings>
26#include <QString>
27
28#include "moc_qgsrangewidgetwrapper.cpp"
29
30using namespace Qt::StringLiterals;
31
33 : QgsEditorWidgetWrapper( layer, fieldIdx, editor, parent )
34
35{
36}
37
39{
40 constexpr int DEFAULT_PRECISION_DOUBLE = 4;
41 const int fieldPrecision = field.precision();
42 switch ( field.type() )
43 {
44 case QMetaType::Type::Float:
45 case QMetaType::Type::Double:
46 return fieldPrecision > 0 ? fieldPrecision : DEFAULT_PRECISION_DOUBLE;
47
48 // we use the double spin box for long long fields in order to get sufficient range of min/max values
49 case QMetaType::Type::LongLong:
50 return 0;
51
52 default:
53 break;
54 }
55
56 return fieldPrecision;
57}
58
59QWidget *QgsRangeWidgetWrapper::createWidget( QWidget *parent )
60{
61 QWidget *editor = nullptr;
62
63 if ( config( u"Style"_s ).toString() == "Dial"_L1 )
64 {
65 editor = new QgsDial( parent );
66 }
67 else if ( config( u"Style"_s ).toString() == "Slider"_L1 )
68 {
69 editor = new QgsSlider( Qt::Horizontal, parent );
70 }
71 else
72 {
73 switch ( layer()->fields().at( fieldIdx() ).type() )
74 {
75 case QMetaType::Type::Double:
76 // for long long field types we have to use a double spin box with 0 decimal places,
77 // as the min/max value supported by QSpinBox is not large enough
78 case QMetaType::Type::LongLong:
79 {
80 editor = new QgsDoubleSpinBox( parent );
81 static_cast<QgsDoubleSpinBox *>( editor )->setLineEditAlignment( Qt::AlignRight );
82 break;
83 }
84
85 case QMetaType::Type::Int:
86 default:
87 editor = new QgsSpinBox( parent );
88 static_cast<QgsSpinBox *>( editor )->setLineEditAlignment( Qt::AlignRight );
89 break;
90 }
91 }
92
93 return editor;
94}
95
96template<class T>
97static void setupIntEditor( const QVariant &min, const QVariant &max, const QVariant &step, T *slider, QgsRangeWidgetWrapper *wrapper )
98{
99 // must use a template function because those methods are overloaded and not inherited by some classes
100 slider->setMinimum( min.isValid() ? min.toInt() : std::numeric_limits<int>::lowest() );
101 slider->setMaximum( max.isValid() ? max.toInt() : std::numeric_limits<int>::max() );
102 slider->setSingleStep( step.isValid() ? step.toInt() : 1 );
103 QObject::connect( slider, qOverload<int>( &T::valueChanged ), wrapper, &QgsRangeWidgetWrapper::emitValueChanged );
104}
105
106template<class T>
107static void setupVariantEditor( const QVariant &min, const QVariant &max, const QVariant &step, T *slider, QgsRangeWidgetWrapper *wrapper )
108{
109 // must use a template function because those methods are overloaded and not inherited by some classes
110 slider->setMinimum( min.isValid() ? min.toInt() : std::numeric_limits<int>::lowest() );
111 slider->setMaximum( max.isValid() ? max.toInt() : std::numeric_limits<int>::max() );
112 slider->setSingleStep( step.isValid() ? step.toInt() : 1 );
113 QObject::connect( slider, qOverload<const QVariant &>( &T::valueChanged ), wrapper, &QgsRangeWidgetWrapper::emitValueChanged );
114}
115
117{
118 mDoubleSpinBox = qobject_cast<QDoubleSpinBox *>( editor );
119 mIntSpinBox = qobject_cast<QSpinBox *>( editor );
120
121 mDial = qobject_cast<QDial *>( editor );
122 mSlider = qobject_cast<QSlider *>( editor );
123 mQgsDial = qobject_cast<QgsDial *>( editor );
124 mQgsSlider = qobject_cast<QgsSlider *>( editor );
125
126 const bool allowNull = config( u"AllowNull"_s, true ).toBool();
127
128 QVariant min( config( u"Min"_s ) );
129 QVariant max( config( u"Max"_s ) );
130 QVariant step( config( u"Step"_s ) );
131 const QVariant precision( config( u"Precision"_s ) );
132
133 if ( mDoubleSpinBox )
134 {
135 const double stepval = step.isValid() ? step.toDouble() : 1.0;
136 double minval = min.isValid() ? min.toDouble() : std::numeric_limits<double>::lowest();
137 const double maxval = max.isValid() ? max.toDouble() : std::numeric_limits<double>::max();
138
139 const QgsField field = layer()->fields().at( fieldIdx() );
140 const int precisionval = precision.isValid() ? precision.toInt() : defaultFieldPrecision( field );
141 mDoubleSpinBox->setDecimals( precisionval );
142
143 QgsDoubleSpinBox *qgsWidget = qobject_cast<QgsDoubleSpinBox *>( mDoubleSpinBox );
144
145
146 if ( qgsWidget )
147 qgsWidget->setShowClearButton( allowNull );
148 // Make room for null value: lower the minimum to allow for NULL special values
149 if ( allowNull )
150 {
151 double decr;
152 if ( precisionval > 0 )
153 {
154 decr = std::pow( 10, -precisionval );
155 }
156 else
157 {
158 decr = stepval;
159 }
160 minval -= decr;
161 // Note: call setMinimum here or setValue won't work
162 mDoubleSpinBox->setMinimum( minval );
163 mDoubleSpinBox->setValue( minval );
164 QgsDoubleSpinBox *doubleSpinBox( qobject_cast<QgsDoubleSpinBox *>( mDoubleSpinBox ) );
165 if ( doubleSpinBox )
167 else
168 mDoubleSpinBox->setSpecialValueText( QgsApplication::nullRepresentation() );
169 }
170 mDoubleSpinBox->setMinimum( minval );
171 mDoubleSpinBox->setMaximum( maxval );
172 mDoubleSpinBox->setSingleStep( stepval );
173 if ( config( u"Suffix"_s ).isValid() )
174 mDoubleSpinBox->setSuffix( config( u"Suffix"_s ).toString() );
175
176 connect( mDoubleSpinBox, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, [this]( double ) { emitValueChanged(); } );
177 }
178 else if ( mIntSpinBox )
179 {
180 QgsSpinBox *qgsWidget = qobject_cast<QgsSpinBox *>( mIntSpinBox );
181 if ( qgsWidget )
182 qgsWidget->setShowClearButton( allowNull );
183 int minval = min.isValid() ? min.toInt() : std::numeric_limits<int>::lowest();
184 const int maxval = max.isValid() ? max.toInt() : std::numeric_limits<int>::max();
185 const uint stepval = step.isValid() ? step.toUInt() : 1;
186 if ( allowNull )
187 {
188 // make sure there is room for a new value (i.e. signed integer does not overflow)
189 const int minvalOverflow = uint( minval ) - stepval;
190 if ( minvalOverflow < minval )
191 {
192 minval = minvalOverflow;
193 }
194 mIntSpinBox->setValue( minval );
195 QgsSpinBox *intSpinBox( qobject_cast<QgsSpinBox *>( mIntSpinBox ) );
196 if ( intSpinBox )
198 else
199 mIntSpinBox->setSpecialValueText( QgsApplication::nullRepresentation() );
200 }
201 setupIntEditor( minval, maxval, stepval, mIntSpinBox, this );
202 if ( config( u"Suffix"_s ).isValid() )
203 mIntSpinBox->setSuffix( config( u"Suffix"_s ).toString() );
204 }
205 else
206 {
207 ( void ) field().convertCompatible( min );
208 ( void ) field().convertCompatible( max );
209 ( void ) field().convertCompatible( step );
210 if ( mQgsDial )
211 setupVariantEditor( min, max, step, mQgsDial, this );
212 else if ( mQgsSlider )
213 setupVariantEditor( min, max, step, mQgsSlider, this );
214 else if ( mDial )
215 setupIntEditor( min, max, step, mDial, this );
216 else if ( mSlider )
217 setupIntEditor( min, max, step, mSlider, this );
218 }
219}
220
222{
223 return mSlider || mDial || mQgsDial || mQgsSlider || mIntSpinBox || mDoubleSpinBox;
224}
225
226void QgsRangeWidgetWrapper::valueChangedVariant( const QVariant &v )
227{
228 if ( v.userType() == QMetaType::Type::Int )
229 {
231 emit valueChanged( v.toInt() );
233 emit valuesChanged( v.toInt() );
234 }
235 else if ( v.userType() == QMetaType::Type::LongLong )
236 {
238 emit valueChanged( v.toLongLong() );
240 emit valuesChanged( v.toLongLong() );
241 }
242 else if ( v.userType() == QMetaType::Type::Double )
243 {
245 emit valueChanged( v.toDouble() );
247 emit valuesChanged( v.toDouble() );
248 }
249}
250
252{
253 QVariant value;
254
255 if ( mDoubleSpinBox )
256 {
257 const QMetaType::Type fieldType = field().type();
258 switch ( fieldType )
259 {
260 case QMetaType::Type::Double:
261 value = mDoubleSpinBox->value();
262 break;
263
264 case QMetaType::Type::LongLong:
265 value = static_cast<long long>( mDoubleSpinBox->value() );
266 break;
267
268 default:
269 break;
270 }
271
272 if ( value == mDoubleSpinBox->minimum() && config( u"AllowNull"_s, true ).toBool() )
273 {
275 }
276 }
277 else if ( mIntSpinBox )
278 {
279 value = mIntSpinBox->value();
280 if ( value == mIntSpinBox->minimum() && config( u"AllowNull"_s, true ).toBool() )
281 {
283 }
284 }
285 else if ( mQgsDial )
286 {
287 value = mQgsDial->variantValue();
288 }
289 else if ( mQgsSlider )
290 {
291 value = mQgsSlider->variantValue();
292 }
293 else if ( mDial )
294 {
295 value = mDial->value();
296 }
297 else if ( mSlider )
298 {
299 value = mSlider->value();
300 }
301
302 return value;
303}
304
305void QgsRangeWidgetWrapper::updateValues( const QVariant &value, const QVariantList & )
306{
307 if ( mDoubleSpinBox )
308 {
309 if ( QgsVariantUtils::isNull( value ) && config( u"AllowNull"_s, true ).toBool() )
310 {
311 mDoubleSpinBox->setValue( mDoubleSpinBox->minimum() );
312 }
313 else
314 {
315 mDoubleSpinBox->setValue( value.toDouble() );
316 }
317 }
318
319 if ( mIntSpinBox )
320 {
321 if ( QgsVariantUtils::isNull( value ) && config( u"AllowNull"_s, true ).toBool() )
322 {
323 mIntSpinBox->setValue( mIntSpinBox->minimum() );
324 }
325 else
326 {
327 mIntSpinBox->setValue( value.toInt() );
328 }
329 }
330
331 if ( mQgsDial )
332 {
333 mQgsDial->setValue( value );
334 }
335 else if ( mQgsSlider )
336 {
337 mQgsSlider->setValue( value );
338 }
339 else if ( mDial )
340 {
341 mDial->setValue( value.toInt() );
342 }
343 else if ( mSlider )
344 {
345 mSlider->setValue( value.toInt() );
346 }
347}
348
350{
351 if ( mDoubleSpinBox )
352 {
353 mDoubleSpinBox->setReadOnly( !enabled );
354 mDoubleSpinBox->setFrame( enabled );
355 }
356 else if ( mIntSpinBox )
357 {
358 mIntSpinBox->setReadOnly( !enabled );
359 mIntSpinBox->setFrame( enabled );
360 }
361 else
362 {
364 }
365}
static QString nullRepresentation()
Returns the string used to represent the value NULL throughout QGIS.
A QDial subclass with additional refinements.
Definition qgsdial.h:35
The QgsSpinBox is a spin box with a clear button that will set the value to the defined clear value.
void setSpecialValueText(const QString &txt)
Set the special-value text to be txt If set, the spin box will display this text instead of a numeric...
void setShowClearButton(bool showClearButton)
Sets whether the widget will show a clear button.
Q_DECL_DEPRECATED void valueChanged(const QVariant &value)
Emit this signal, whenever the value changed.
int fieldIdx() const
Access the field index.
QgsEditorWidgetWrapper(QgsVectorLayer *vl, int fieldIdx, QWidget *editor=nullptr, QWidget *parent=nullptr)
Create a new widget wrapper.
void setEnabled(bool enabled) override
Is used to enable or disable the edit functionality of the managed widget.
void valuesChanged(const QVariant &value, const QVariantList &additionalFieldValues=QVariantList())
Emit this signal, whenever the value changed.
void emitValueChanged()
Will call the value() method to determine the emitted value.
QgsField field() const
Access the field.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
QMetaType::Type type
Definition qgsfield.h:63
bool convertCompatible(QVariant &v, QString *errorMessage=nullptr) const
Converts the provided variant to a compatible format.
Definition qgsfield.cpp:479
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Wraps a range widget.
QWidget * createWidget(QWidget *parent) override
This method should create a new widget with the provided parent.
bool valid() const override
Returns true if the widget has been properly initialized.
static int defaultFieldPrecision(const QgsField &field)
Returns the default field precision to use for a field.
QgsRangeWidgetWrapper(QgsVectorLayer *layer, int fieldIdx, QWidget *editor, QWidget *parent=nullptr)
Constructor for QgsRangeWidgetWrapper.
QVariant value() const override
Will be used to access the widget's value.
void initWidget(QWidget *editor) override
This method should initialize the editor widget with runtime data.
void setEnabled(bool enabled) override
A custom QSlider with additional refinements.
Definition qgsslider.h:35
A spin box with a clear button that will set the value to the defined clear value.
Definition qgsspinbox.h:45
void setShowClearButton(bool showClearButton)
Sets whether the widget will show a clear button.
void setSpecialValueText(const QString &txt)
Set the special-value text to be txt If set, the spin box will display this text instead of a numeric...
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
Represents a vector layer which manages a vector based dataset.
QVariant config(const QString &key, const QVariant &defaultVal=QVariant()) const
Use this inside your overridden classes to access the configuration.
QgsVectorLayer * layer() const
Returns the vector layer associated with the widget.
QVariantMap config() const
Returns the whole config.
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:7451
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7450