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