QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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
38{
39 constexpr int DEFAULT_PRECISION_DOUBLE = 4;
40 const int fieldPrecision = field.precision();
41 switch ( field.type() )
42 {
43 case QMetaType::Type::Float:
44 case QMetaType::Type::Double:
45 return fieldPrecision > 0 ? fieldPrecision : DEFAULT_PRECISION_DOUBLE;
46
47 // we use the double spin box for long long fields in order to get sufficient range of min/max values
48 case QMetaType::Type::LongLong:
49 return 0;
50
51 default:
52 break;
53 }
54
55 return fieldPrecision;
56}
57
58QWidget *QgsRangeWidgetWrapper::createWidget( QWidget *parent )
59{
60 QWidget *editor = nullptr;
61
62 if ( config( u"Style"_s ).toString() == "Dial"_L1 )
63 {
64 editor = new QgsDial( parent );
65 }
66 else if ( config( u"Style"_s ).toString() == "Slider"_L1 )
67 {
68 editor = new QgsSlider( Qt::Horizontal, parent );
69 }
70 else
71 {
72 switch ( layer()->fields().at( fieldIdx() ).type() )
73 {
74 case QMetaType::Type::Double:
75 // for long long field types we have to use a double spin box with 0 decimal places,
76 // as the min/max value supported by QSpinBox is not large enough
77 case QMetaType::Type::LongLong:
78 {
79 editor = new QgsDoubleSpinBox( parent );
80 static_cast<QgsDoubleSpinBox *>( editor )->setLineEditAlignment( Qt::AlignRight );
81 break;
82 }
83
84 case QMetaType::Type::Int:
85 default:
86 editor = new QgsSpinBox( parent );
87 static_cast<QgsSpinBox *>( editor )->setLineEditAlignment( Qt::AlignRight );
88 break;
89 }
90 }
91
92 return editor;
93}
94
95template<class T> static void setupIntEditor( const QVariant &min, const QVariant &max, const QVariant &step, T *slider, QgsRangeWidgetWrapper *wrapper )
96{
97 // must use a template function because those methods are overloaded and not inherited by some classes
98 slider->setMinimum( min.isValid() ? min.toInt() : std::numeric_limits<int>::lowest() );
99 slider->setMaximum( max.isValid() ? max.toInt() : std::numeric_limits<int>::max() );
100 slider->setSingleStep( step.isValid() ? step.toInt() : 1 );
101 QObject::connect( slider, qOverload<int>( &T::valueChanged ), wrapper, &QgsRangeWidgetWrapper::emitValueChanged );
102}
103
104template<class T> static void setupVariantEditor( const QVariant &min, const QVariant &max, const QVariant &step, T *slider, QgsRangeWidgetWrapper *wrapper )
105{
106 // must use a template function because those methods are overloaded and not inherited by some classes
107 slider->setMinimum( min.isValid() ? min.toInt() : std::numeric_limits<int>::lowest() );
108 slider->setMaximum( max.isValid() ? max.toInt() : std::numeric_limits<int>::max() );
109 slider->setSingleStep( step.isValid() ? step.toInt() : 1 );
110 QObject::connect( slider, qOverload<const QVariant &>( &T::valueChanged ), wrapper, &QgsRangeWidgetWrapper::emitValueChanged );
111}
112
114{
115 mDoubleSpinBox = qobject_cast<QDoubleSpinBox *>( editor );
116 mIntSpinBox = qobject_cast<QSpinBox *>( editor );
117
118 mDial = qobject_cast<QDial *>( editor );
119 mSlider = qobject_cast<QSlider *>( editor );
120 mQgsDial = qobject_cast<QgsDial *>( editor );
121 mQgsSlider = qobject_cast<QgsSlider *>( editor );
122
123 const bool allowNull = config( u"AllowNull"_s, true ).toBool();
124
125 QVariant min( config( u"Min"_s ) );
126 QVariant max( config( u"Max"_s ) );
127 QVariant step( config( u"Step"_s ) );
128 const QVariant precision( config( u"Precision"_s ) );
129
130 if ( mDoubleSpinBox )
131 {
132 const double stepval = step.isValid() ? step.toDouble() : 1.0;
133 double minval = min.isValid() ? min.toDouble() : std::numeric_limits<double>::lowest();
134 const double maxval = max.isValid() ? max.toDouble() : std::numeric_limits<double>::max();
135
136 const QgsField field = layer()->fields().at( fieldIdx() );
137 const int precisionval = precision.isValid() ? precision.toInt() : defaultFieldPrecision( field );
138 mDoubleSpinBox->setDecimals( precisionval );
139
140 QgsDoubleSpinBox *qgsWidget = qobject_cast<QgsDoubleSpinBox *>( mDoubleSpinBox );
141
142
143 if ( qgsWidget )
144 qgsWidget->setShowClearButton( allowNull );
145 // Make room for null value: lower the minimum to allow for NULL special values
146 if ( allowNull )
147 {
148 double decr;
149 if ( precisionval > 0 )
150 {
151 decr = std::pow( 10, -precisionval );
152 }
153 else
154 {
155 decr = stepval;
156 }
157 minval -= decr;
158 // Note: call setMinimum here or setValue won't work
159 mDoubleSpinBox->setMinimum( minval );
160 mDoubleSpinBox->setValue( minval );
161 QgsDoubleSpinBox *doubleSpinBox( qobject_cast<QgsDoubleSpinBox *>( mDoubleSpinBox ) );
162 if ( doubleSpinBox )
164 else
165 mDoubleSpinBox->setSpecialValueText( QgsApplication::nullRepresentation() );
166 }
167 mDoubleSpinBox->setMinimum( minval );
168 mDoubleSpinBox->setMaximum( maxval );
169 mDoubleSpinBox->setSingleStep( stepval );
170 if ( config( u"Suffix"_s ).isValid() )
171 mDoubleSpinBox->setSuffix( config( u"Suffix"_s ).toString() );
172
173 connect( mDoubleSpinBox, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, [this]( double ) { emitValueChanged(); } );
174 }
175 else if ( mIntSpinBox )
176 {
177 QgsSpinBox *qgsWidget = qobject_cast<QgsSpinBox *>( mIntSpinBox );
178 if ( qgsWidget )
179 qgsWidget->setShowClearButton( allowNull );
180 int minval = min.isValid() ? min.toInt() : std::numeric_limits<int>::lowest();
181 const int maxval = max.isValid() ? max.toInt() : std::numeric_limits<int>::max();
182 const uint stepval = step.isValid() ? step.toUInt() : 1;
183 if ( allowNull )
184 {
185 // make sure there is room for a new value (i.e. signed integer does not overflow)
186 const int minvalOverflow = uint( minval ) - stepval;
187 if ( minvalOverflow < minval )
188 {
189 minval = minvalOverflow;
190 }
191 mIntSpinBox->setValue( minval );
192 QgsSpinBox *intSpinBox( qobject_cast<QgsSpinBox *>( mIntSpinBox ) );
193 if ( intSpinBox )
195 else
196 mIntSpinBox->setSpecialValueText( QgsApplication::nullRepresentation() );
197 }
198 setupIntEditor( minval, maxval, stepval, mIntSpinBox, this );
199 if ( config( u"Suffix"_s ).isValid() )
200 mIntSpinBox->setSuffix( config( u"Suffix"_s ).toString() );
201 }
202 else
203 {
204 ( void ) field().convertCompatible( min );
205 ( void ) field().convertCompatible( max );
206 ( void ) field().convertCompatible( step );
207 if ( mQgsDial )
208 setupVariantEditor( min, max, step, mQgsDial, this );
209 else if ( mQgsSlider )
210 setupVariantEditor( min, max, step, mQgsSlider, this );
211 else if ( mDial )
212 setupIntEditor( min, max, step, mDial, this );
213 else if ( mSlider )
214 setupIntEditor( min, max, step, mSlider, this );
215 }
216}
217
219{
220 return mSlider || mDial || mQgsDial || mQgsSlider || mIntSpinBox || mDoubleSpinBox;
221}
222
223void QgsRangeWidgetWrapper::valueChangedVariant( const QVariant &v )
224{
225 if ( v.userType() == QMetaType::Type::Int )
226 {
228 emit valueChanged( v.toInt() );
230 emit valuesChanged( v.toInt() );
231 }
232 else if ( v.userType() == QMetaType::Type::LongLong )
233 {
235 emit valueChanged( v.toLongLong() );
237 emit valuesChanged( v.toLongLong() );
238 }
239 else if ( v.userType() == QMetaType::Type::Double )
240 {
242 emit valueChanged( v.toDouble() );
244 emit valuesChanged( v.toDouble() );
245 }
246}
247
249{
250 QVariant value;
251
252 if ( mDoubleSpinBox )
253 {
254 const QMetaType::Type fieldType = field().type();
255 switch ( fieldType )
256 {
257 case QMetaType::Type::Double:
258 value = mDoubleSpinBox->value();
259 break;
260
261 case QMetaType::Type::LongLong:
262 value = static_cast<long long>( mDoubleSpinBox->value() );
263 break;
264
265 default:
266 break;
267 }
268
269 if ( value == mDoubleSpinBox->minimum() && config( u"AllowNull"_s, true ).toBool() )
270 {
272 }
273 }
274 else if ( mIntSpinBox )
275 {
276 value = mIntSpinBox->value();
277 if ( value == mIntSpinBox->minimum() && config( u"AllowNull"_s, true ).toBool() )
278 {
280 }
281 }
282 else if ( mQgsDial )
283 {
284 value = mQgsDial->variantValue();
285 }
286 else if ( mQgsSlider )
287 {
288 value = mQgsSlider->variantValue();
289 }
290 else if ( mDial )
291 {
292 value = mDial->value();
293 }
294 else if ( mSlider )
295 {
296 value = mSlider->value();
297 }
298
299 return value;
300}
301
302void QgsRangeWidgetWrapper::updateValues( const QVariant &value, const QVariantList & )
303{
304 if ( mDoubleSpinBox )
305 {
306 if ( QgsVariantUtils::isNull( value ) && config( u"AllowNull"_s, true ).toBool() )
307 {
308 mDoubleSpinBox->setValue( mDoubleSpinBox->minimum() );
309 }
310 else
311 {
312 mDoubleSpinBox->setValue( value.toDouble() );
313 }
314 }
315
316 if ( mIntSpinBox )
317 {
318 if ( QgsVariantUtils::isNull( value ) && config( u"AllowNull"_s, true ).toBool() )
319 {
320 mIntSpinBox->setValue( mIntSpinBox->minimum() );
321 }
322 else
323 {
324 mIntSpinBox->setValue( value.toInt() );
325 }
326 }
327
328 if ( mQgsDial )
329 {
330 mQgsDial->setValue( value );
331 }
332 else if ( mQgsSlider )
333 {
334 mQgsSlider->setValue( value );
335 }
336 else if ( mDial )
337 {
338 mDial->setValue( value.toInt() );
339 }
340 else if ( mSlider )
341 {
342 mSlider->setValue( value.toInt() );
343 }
344}
345
347{
348 if ( mDoubleSpinBox )
349 {
350 mDoubleSpinBox->setReadOnly( !enabled );
351 mDoubleSpinBox->setFrame( enabled );
352 }
353 else if ( mIntSpinBox )
354 {
355 mIntSpinBox->setReadOnly( !enabled );
356 mIntSpinBox->setFrame( enabled );
357 }
358 else
359 {
361 }
362}
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:470
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:7504
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7503