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