QGIS API Documentation 3.38.0-Grenoble (exported)
Loading...
Searching...
No Matches
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"
20#include "qgsscalecombobox.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 scalesList;
43 const QString oldScale = currentText();
44
45 if ( scales.isEmpty() )
46 {
48 }
49 else
50 {
51 scalesList = scales;
52 }
53
54 QStringList cleanedScalesList;
55 for ( const QString &scale : std::as_const( scalesList ) )
56 {
57 const QStringList parts = scale.split( ':' );
58 if ( parts.size() < 2 )
59 continue;
60
61 bool ok = false;
62 const double denominator = QLocale().toDouble( parts[1], &ok );
63 if ( ok )
64 {
65 cleanedScalesList.push_back( toString( denominator ) );
66 }
67 else
68 {
69 const double denominator = parts[1].toDouble( &ok );
70 if ( ok )
71 {
72 cleanedScalesList.push_back( toString( denominator ) );
73 }
74 }
75 }
76
77 blockSignals( true );
78 clear();
79 addItems( cleanedScalesList );
80 setScaleString( oldScale );
81 blockSignals( false );
82}
83
84void QgsScaleComboBox::setPredefinedScales( const QVector<double> &scales )
85{
86 if ( scales.isEmpty() )
87 {
89 return;
90 }
91
92 const QString oldScale = currentText();
93
94 QStringList scalesStringList;
95 scalesStringList.reserve( scales.size() );
96 for ( double denominator : scales )
97 {
98 scalesStringList.push_back( toString( denominator ) );
99 }
100
101 blockSignals( true );
102 clear();
103 addItems( scalesStringList );
104 setScaleString( oldScale );
105 blockSignals( false );
106}
107
109{
110 QComboBox::showPopup();
111
112 if ( !currentText().contains( ':' ) )
113 {
114 return;
115 }
116 QStringList parts = currentText().split( ':' );
117 bool ok;
118 int idx = 0;
119 int min = 999999;
120 const long currScale = parts.at( 1 ).toLong( &ok );
121 long nextScale, delta;
122 for ( int i = 0; i < count(); i++ )
123 {
124 parts = itemText( i ).split( ':' );
125 nextScale = parts.at( 1 ).toLong( &ok );
126 delta = std::labs( currScale - nextScale );
127 if ( delta < min )
128 {
129 min = delta;
130 idx = i;
131 }
132 }
133
134 blockSignals( true );
135 view()->setCurrentIndex( model()->index( idx, 0 ) );
136 blockSignals( false );
137 view()->setMinimumWidth( view()->sizeHintForColumn( 0 ) );
138}
139
141{
142 return toString( mScale );
143}
144
145bool QgsScaleComboBox::setScaleString( const QString &string )
146{
147 const double oldScale = mScale;
148 if ( mAllowNull && string.trimmed().isEmpty() )
149 {
150 mScale = std::numeric_limits< double >::quiet_NaN();
151 setEditText( toString( mScale ) );
152 clearFocus();
153 if ( !std::isnan( oldScale ) )
154 {
155 emit scaleChanged( mScale );
156 }
157 return true;
158 }
159
160 bool ok;
161 double newScale = toDouble( string, &ok );
162 if ( newScale > mMinScale && newScale != 0 && mMinScale != 0 )
163 {
164 newScale = mMinScale;
165 }
166 if ( ! ok )
167 {
168 return false;
169 }
170 else
171 {
172 mScale = newScale;
173 setEditText( toString( mScale ) );
174 clearFocus();
175 if ( mScale != oldScale )
176 {
177 emit scaleChanged( mScale );
178 }
179 return true;
180 }
181}
182
184{
185 return mScale;
186}
187
189{
190 return std::isnan( mScale );
191}
192
193void QgsScaleComboBox::setScale( double scale )
194{
196}
197
198void QgsScaleComboBox::fixupScale()
199{
200 if ( mAllowNull && currentText().trimmed().isEmpty() )
201 {
202 setScale( std::numeric_limits< double >::quiet_NaN() );
203 return;
204 }
205
206 const QStringList txtList = currentText().split( ':' );
207 const bool userSetScale = txtList.size() != 2;
208
209 bool ok;
210 double newScale = toDouble( currentText(), &ok );
211
212 // Valid string representation
213 if ( ok )
214 {
215 // if a user types scale = 2345, we transform to 1:2345
216 if ( userSetScale && newScale < 1.0 && !qgsDoubleNear( newScale, 0.0 ) )
217 {
218 newScale = 1 / newScale;
219 }
220 setScale( newScale );
221 }
222 else
223 {
224 setScale( mScale );
225 }
226}
227
228QString QgsScaleComboBox::toString( double scale )
229{
230 if ( std::isnan( scale ) )
231 {
232 return QString();
233 }
234 if ( scale == 0 )
235 {
236 return QStringLiteral( "0" );
237 }
238 else if ( scale <= 1 )
239 {
240 return QStringLiteral( "%1:1" ).arg( QLocale().toString( static_cast< int >( std::round( 1.0 / scale ) ) ) );
241 }
242 else
243 {
244 return QStringLiteral( "1:%1" ).arg( QLocale().toString( static_cast< float >( std::round( scale ) ), 'f', 0 ) );
245 }
246}
247
248double QgsScaleComboBox::toDouble( const QString &scaleString, bool *returnOk )
249{
250 bool ok = false;
251 QString scaleTxt( scaleString );
252
253 const double denominator = qgsPermissiveToDouble( scaleTxt, ok );
254 double scale = !qgsDoubleNear( denominator, 0.0 ) ? 1.0 / denominator : 0.0;
255 if ( ok )
256 {
257 // Create a text version and set that text and rescan
258 // Idea is to get the same rounding.
259 scaleTxt = toString( scale );
260 }
261 else
262 {
263 // It is now either X:Y or not valid
264 QStringList txtList = scaleTxt.split( ':' );
265 if ( 2 == txtList.size() )
266 {
267 bool okX = false;
268 bool okY = false;
269 const int x = qgsPermissiveToInt( txtList[ 0 ], okX );
270 const int y = qgsPermissiveToInt( txtList[ 1 ], okY );
271 if ( okX && okY && x != 0 )
272 {
273 // Scale is fraction of x and y
274 scale = static_cast< double >( y ) / static_cast< double >( x );
275 ok = true;
276 }
277 }
278 }
279
280 // Set up optional return flag
281 if ( returnOk )
282 {
283 *returnOk = ok;
284 }
285 return scale;
286}
287
288void QgsScaleComboBox::setAllowNull( bool allowNull )
289{
290 mAllowNull = allowNull;
291 lineEdit()->setClearButtonEnabled( allowNull );
292 updateScales();
293}
294
296{
297 return mAllowNull;
298}
299
301{
302 mMinScale = scale;
303 if ( mScale > mMinScale && mScale != 0 && mMinScale != 0 )
304 {
305 setScale( mMinScale );
306 }
307}
308
310{
311 if ( allowNull() )
312 setScale( std::numeric_limits< double >::quiet_NaN() );
313}
void setPredefinedScales(const QVector< double > &scales)
Sets the list of predefined scales to show in the combobox.
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.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
static const QgsSettingsEntryStringList * settingsMapScales
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:72
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:79
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:5445