QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
qgsscalecombobox.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsscalecombobox.h
3 ------------------------
4 begin : January 7, 2012
5 copyright : (C) 2012 by Alexander Bruy
6 email : alexander dot bruy at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgis.h"
19#include "qgslogger.h"
20#include "qgsscalecombobox.h"
21#include "qgssettings.h"
22
23#include <QAbstractItemView>
24#include <QLocale>
25#include <QLineEdit>
26
28 : QComboBox( parent )
29{
31
32 setEditable( true );
33 setInsertPolicy( QComboBox::NoInsert );
34 setCompleter( nullptr );
35 connect( this, qOverload< int >( &QComboBox::activated ), this, &QgsScaleComboBox::fixupScale );
36 connect( lineEdit(), &QLineEdit::editingFinished, this, &QgsScaleComboBox::fixupScale );
37 fixupScale();
38}
39
40void QgsScaleComboBox::updateScales( const QStringList &scales )
41{
42 QStringList myScalesList;
43 const QString oldScale = currentText();
44
45 if ( scales.isEmpty() )
46 {
47 const QgsSettings settings;
48 const QString myScales = settings.value( QStringLiteral( "Map/scales" ), Qgis::defaultProjectScales() ).toString();
49 if ( !myScales.isEmpty() )
50 {
51 myScalesList = myScales.split( ',' );
52 }
53 }
54 else
55 {
56 QStringList::const_iterator scaleIt = scales.constBegin();
57 for ( ; scaleIt != scales.constEnd(); ++scaleIt )
58 {
59 myScalesList.append( *scaleIt );
60 }
61 }
62
63 QStringList parts;
64 double denominator;
65 bool ok;
66 for ( int i = 0; i < myScalesList.size(); ++i )
67 {
68 parts = myScalesList[ i ] .split( ':' );
69 denominator = QLocale().toDouble( parts[1], &ok );
70 if ( ok )
71 {
72 myScalesList[ i ] = toString( denominator );
73 }
74 }
75
76 blockSignals( true );
77 clear();
78 addItems( myScalesList );
79 setScaleString( oldScale );
80 blockSignals( false );
81}
82
84{
85 QComboBox::showPopup();
86
87 if ( !currentText().contains( ':' ) )
88 {
89 return;
90 }
91 QStringList parts = currentText().split( ':' );
92 bool ok;
93 int idx = 0;
94 int min = 999999;
95 const long currScale = parts.at( 1 ).toLong( &ok );
96 long nextScale, delta;
97 for ( int i = 0; i < count(); i++ )
98 {
99 parts = itemText( i ).split( ':' );
100 nextScale = parts.at( 1 ).toLong( &ok );
101 delta = std::labs( currScale - nextScale );
102 if ( delta < min )
103 {
104 min = delta;
105 idx = i;
106 }
107 }
108
109 blockSignals( true );
110 view()->setCurrentIndex( model()->index( idx, 0 ) );
111 blockSignals( false );
112 view()->setMinimumWidth( view()->sizeHintForColumn( 0 ) );
113}
114
116{
117 return toString( mScale );
118}
119
120bool QgsScaleComboBox::setScaleString( const QString &string )
121{
122 const double oldScale = mScale;
123 if ( mAllowNull && string.trimmed().isEmpty() )
124 {
125 mScale = std::numeric_limits< double >::quiet_NaN();
126 setEditText( toString( mScale ) );
127 clearFocus();
128 if ( !std::isnan( oldScale ) )
129 {
130 emit scaleChanged( mScale );
131 }
132 return true;
133 }
134
135 bool ok;
136 double newScale = toDouble( string, &ok );
137 if ( newScale > mMinScale && newScale != 0 && mMinScale != 0 )
138 {
139 newScale = mMinScale;
140 }
141 if ( ! ok )
142 {
143 return false;
144 }
145 else
146 {
147 mScale = newScale;
148 setEditText( toString( mScale ) );
149 clearFocus();
150 if ( mScale != oldScale )
151 {
152 emit scaleChanged( mScale );
153 }
154 return true;
155 }
156}
157
159{
160 return mScale;
161}
162
164{
165 return std::isnan( mScale );
166}
167
168void QgsScaleComboBox::setScale( double scale )
169{
171}
172
173void QgsScaleComboBox::fixupScale()
174{
175 if ( mAllowNull && currentText().trimmed().isEmpty() )
176 {
177 setScale( std::numeric_limits< double >::quiet_NaN() );
178 return;
179 }
180
181 const QStringList txtList = currentText().split( ':' );
182 const bool userSetScale = txtList.size() != 2;
183
184 bool ok;
185 double newScale = toDouble( currentText(), &ok );
186
187 // Valid string representation
188 if ( ok )
189 {
190 // if a user types scale = 2345, we transform to 1:2345
191 if ( userSetScale && newScale < 1.0 && !qgsDoubleNear( newScale, 0.0 ) )
192 {
193 newScale = 1 / newScale;
194 }
195 setScale( newScale );
196 }
197 else
198 {
199 setScale( mScale );
200 }
201}
202
203QString QgsScaleComboBox::toString( double scale )
204{
205 if ( std::isnan( scale ) )
206 {
207 return QString();
208 }
209 if ( scale == 0 )
210 {
211 return QStringLiteral( "0" );
212 }
213 else if ( scale <= 1 )
214 {
215 return QStringLiteral( "%1:1" ).arg( QLocale().toString( static_cast< int >( std::round( 1.0 / scale ) ) ) );
216 }
217 else
218 {
219 return QStringLiteral( "1:%1" ).arg( QLocale().toString( static_cast< float >( std::round( scale ) ), 'f', 0 ) );
220 }
221}
222
223double QgsScaleComboBox::toDouble( const QString &scaleString, bool *returnOk )
224{
225 bool ok = false;
226 QString scaleTxt( scaleString );
227
228 const double denominator = qgsPermissiveToDouble( scaleTxt, ok );
229 double scale = !qgsDoubleNear( denominator, 0.0 ) ? 1.0 / denominator : 0.0;
230 if ( ok )
231 {
232 // Create a text version and set that text and rescan
233 // Idea is to get the same rounding.
234 scaleTxt = toString( scale );
235 }
236 else
237 {
238 // It is now either X:Y or not valid
239 QStringList txtList = scaleTxt.split( ':' );
240 if ( 2 == txtList.size() )
241 {
242 bool okX = false;
243 bool okY = false;
244 const int x = qgsPermissiveToInt( txtList[ 0 ], okX );
245 const int y = qgsPermissiveToInt( txtList[ 1 ], okY );
246 if ( okX && okY && x != 0 )
247 {
248 // Scale is fraction of x and y
249 scale = static_cast< double >( y ) / static_cast< double >( x );
250 ok = true;
251 }
252 }
253 }
254
255 // Set up optional return flag
256 if ( returnOk )
257 {
258 *returnOk = ok;
259 }
260 return scale;
261}
262
263void QgsScaleComboBox::setAllowNull( bool allowNull )
264{
265 mAllowNull = allowNull;
266 lineEdit()->setClearButtonEnabled( allowNull );
267 updateScales();
268}
269
271{
272 return mAllowNull;
273}
274
276{
277 mMinScale = scale;
278 if ( mScale > mMinScale && mScale != 0 && mMinScale != 0 )
279 {
280 setScale( mMinScale );
281 }
282}
283
285{
286 if ( allowNull() )
287 setScale( std::numeric_limits< double >::quiet_NaN() );
288}
static QString defaultProjectScales()
A string with default project scales.
Definition: qgis.cpp:271
static QString toString(double scale)
Helper function to convert a scale double to scale string.
void updateScales(const QStringList &scales=QStringList())
Sets the list of predefined scales to show in the combobox.
QString scaleString() const
Returns the selected scale as a string, e.g.
bool setScaleString(const QString &string)
Set the selected scale from a string, e.g.
void setAllowNull(bool allowNull)
Sets whether the scale combobox can be set to a NULL value.
QgsScaleComboBox(QWidget *parent=nullptr)
Constructor for QgsScaleComboBox.
bool isNull() const
Returns true if the combo box is currently set to a "null" value.
bool allowNull() const
Returns true if the combobox can be set to a NULL value.
static double toDouble(const QString &string, bool *ok=nullptr)
Helper function to convert a scale string to double.
void setScale(double scale)
Set the selected scale from a double.
void showPopup() override
void setNull()
Sets the combo box to the null value.
void setMinScale(double scale)
Set the minimum allowed scale.
void scaleChanged(double scale)
Emitted when user has finished editing/selecting a new scale.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
double qgsPermissiveToDouble(QString string, bool &ok)
Converts a string to a double in a permissive way, e.g., allowing for incorrect numbers of digits bet...
Definition: qgis.cpp:71
int qgsPermissiveToInt(QString string, bool &ok)
Converts a string to an integer in a permissive way, e.g., allowing for incorrect numbers of digits b...
Definition: qgis.cpp:78
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2527