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