QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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 {
30  updateScales();
31 
32  setEditable( true );
33  setInsertPolicy( QComboBox::NoInsert );
34  setCompleter( nullptr );
35  connect( this, static_cast<void ( QComboBox::* )( const QString & )>( &QComboBox::activated ), this, &QgsScaleComboBox::fixupScale );
36  connect( lineEdit(), &QLineEdit::editingFinished, this, &QgsScaleComboBox::fixupScale );
37  fixupScale();
38 }
39 
40 void 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 
120 bool 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 
168 void QgsScaleComboBox::setScale( double scale )
169 {
171 }
172 
173 void 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 
203 QString 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 
223 double 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 
263 void QgsScaleComboBox::setAllowNull( bool allowNull )
264 {
265  mAllowNull = allowNull;
266  lineEdit()->setClearButtonEnabled( allowNull );
267  updateScales();
268 }
269 
271 {
272  return mAllowNull;
273 }
274 
275 void QgsScaleComboBox::setMinScale( double scale )
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:279
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:1246