Skip to content

Commit 4e37f38

Browse files
authored
Merge pull request #7188 from elpaso/locale-formatting
[bugfix] Fix double precision I/O in form widgets
2 parents e899849 + d9afb89 commit 4e37f38

File tree

7 files changed

+371
-54
lines changed

7 files changed

+371
-54
lines changed

src/app/vertextool/qgsvertexeditor.cpp

+12-4
Original file line numberDiff line numberDiff line change
@@ -211,15 +211,23 @@ bool QgsVertexEditorModel::setData( const QModelIndex &index, const QVariant &va
211211
return false;
212212
}
213213

214-
double x = ( index.column() == 0 ? value.toDouble() : mSelectedFeature->vertexMap().at( index.row() )->point().x() );
215-
double y = ( index.column() == 1 ? value.toDouble() : mSelectedFeature->vertexMap().at( index.row() )->point().y() );
214+
// Get double value wrt current locale.
215+
bool ok;
216+
double doubleValue = QLocale().toDouble( value.toString(), &ok );
217+
// If not valid and locale's decimal point is not '.' let's try with english locale
218+
if ( ! ok && QLocale().decimalPoint() != '.' )
219+
{
220+
doubleValue = QLocale( QLocale::English ).toDouble( value.toString() );
221+
}
222+
223+
double x = ( index.column() == 0 ? doubleValue : mSelectedFeature->vertexMap().at( index.row() )->point().x() );
224+
double y = ( index.column() == 1 ? doubleValue : mSelectedFeature->vertexMap().at( index.row() )->point().y() );
216225

217226
if ( index.column() == mRCol ) // radius modified
218227
{
219228
if ( index.row() == 0 || index.row() >= mSelectedFeature->vertexMap().count() - 1 )
220229
return false;
221230

222-
double r = value.toDouble();
223231
double x1 = mSelectedFeature->vertexMap().at( index.row() - 1 )->point().x();
224232
double y1 = mSelectedFeature->vertexMap().at( index.row() - 1 )->point().y();
225233
double x2 = x;
@@ -228,7 +236,7 @@ bool QgsVertexEditorModel::setData( const QModelIndex &index, const QVariant &va
228236
double y3 = mSelectedFeature->vertexMap().at( index.row() + 1 )->point().y();
229237

230238
QgsPoint result;
231-
if ( QgsGeometryUtils::segmentMidPoint( QgsPoint( x1, y1 ), QgsPoint( x3, y3 ), result, r, QgsPoint( x2, y2 ) ) )
239+
if ( QgsGeometryUtils::segmentMidPoint( QgsPoint( x1, y1 ), QgsPoint( x3, y3 ), result, doubleValue, QgsPoint( x2, y2 ) ) )
232240
{
233241
x = result.x();
234242
y = result.y();

src/core/qgsfield.cpp

+109-3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include <QDataStream>
2424
#include <QIcon>
25+
#include <QLocale>
2526

2627
/***************************************************************************
2728
* This class is considered CRITICAL and any change MUST be accompanied with
@@ -208,9 +209,51 @@ QString QgsField::displayString( const QVariant &v ) const
208209
return QgsApplication::nullRepresentation();
209210
}
210211

211-
if ( d->type == QVariant::Double && d->precision > 0 )
212-
return QString::number( v.toDouble(), 'f', d->precision );
213-
212+
// Special treatment for numeric types if group separator is set or decimalPoint is not a dot
213+
if ( d->type == QVariant::Double )
214+
{
215+
// Locales with decimal point != '.' or that require group separator: use QLocale
216+
if ( QLocale().decimalPoint() != '.' ||
217+
!( QLocale().numberOptions() & QLocale::NumberOption::OmitGroupSeparator ) )
218+
{
219+
if ( d->precision > 0 )
220+
{
221+
return QLocale().toString( v.toDouble(), 'f', d->precision );
222+
}
223+
else
224+
{
225+
// Precision is not set, let's guess it from the
226+
// standard conversion to string
227+
QString s( v.toString() );
228+
int dotPosition( s.indexOf( '.' ) );
229+
int precision;
230+
if ( dotPosition < 0 )
231+
{
232+
precision = 0;
233+
}
234+
else
235+
{
236+
precision = s.length() - dotPosition - 1;
237+
}
238+
return QLocale().toString( v.toDouble(), 'f', precision );
239+
}
240+
}
241+
// Default for doubles with precision
242+
else if ( d->type == QVariant::Double && d->precision > 0 )
243+
{
244+
return QString::number( v.toDouble(), 'f', d->precision );
245+
}
246+
}
247+
// Other numeric types than doubles
248+
else if ( isNumeric() &&
249+
! QLocale().numberOptions() & QLocale::NumberOption::OmitGroupSeparator )
250+
{
251+
bool ok;
252+
qlonglong converted( v.toLongLong( &ok ) );
253+
if ( ok )
254+
return QLocale().toString( converted );
255+
}
256+
// Fallback if special rules do not apply
214257
return v.toString();
215258
}
216259

@@ -234,6 +277,68 @@ bool QgsField::convertCompatible( QVariant &v ) const
234277
return false;
235278
}
236279

280+
// Give it a chance to convert to double since for not '.' locales
281+
// we accept both comma and dot as decimal point
282+
if ( d->type == QVariant::Double && v.type() == QVariant::String )
283+
{
284+
QVariant tmp( v );
285+
if ( !tmp.convert( d->type ) )
286+
{
287+
// This might be a string with thousand separator: use locale to convert
288+
bool ok;
289+
double d = QLocale().toDouble( v.toString(), &ok );
290+
if ( ok )
291+
{
292+
v = QVariant( d );
293+
return true;
294+
}
295+
// For not 'dot' locales, we also want to accept '.'
296+
if ( QLocale().decimalPoint() != '.' )
297+
{
298+
d = QLocale( QLocale::English ).toDouble( v.toString(), &ok );
299+
if ( ok )
300+
{
301+
v = QVariant( d );
302+
return true;
303+
}
304+
}
305+
}
306+
}
307+
308+
// For string representation of an int we also might have thousand separator
309+
if ( d->type == QVariant::Int && v.type() == QVariant::String )
310+
{
311+
QVariant tmp( v );
312+
if ( !tmp.convert( d->type ) )
313+
{
314+
// This might be a string with thousand separator: use locale to convert
315+
bool ok;
316+
int i = QLocale().toInt( v.toString(), &ok );
317+
if ( ok )
318+
{
319+
v = QVariant( i );
320+
return true;
321+
}
322+
}
323+
}
324+
325+
// For string representation of a long we also might have thousand separator
326+
if ( d->type == QVariant::LongLong && v.type() == QVariant::String )
327+
{
328+
QVariant tmp( v );
329+
if ( !tmp.convert( d->type ) )
330+
{
331+
// This might be a string with thousand separator: use locale to convert
332+
bool ok;
333+
qlonglong l = QLocale().toLongLong( v.toString(), &ok );
334+
if ( ok )
335+
{
336+
v = QVariant( l );
337+
return true;
338+
}
339+
}
340+
}
341+
237342
//String representations of doubles in QVariant will return false to convert( QVariant::Int )
238343
//work around this by first converting to double, and then checking whether the double is convertible to int
239344
if ( d->type == QVariant::Int && v.canConvert( QVariant::Double ) )
@@ -258,6 +363,7 @@ bool QgsField::convertCompatible( QVariant &v ) const
258363
return true;
259364
}
260365

366+
261367
if ( !v.convert( d->type ) )
262368
{
263369
v = QVariant( d->type );

src/gui/editorwidgets/qgstexteditwrapper.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,9 @@ void QgsTextEditWrapper::setWidgetValue( const QVariant &val )
217217
v = QgsApplication::nullRepresentation();
218218
}
219219
else
220-
v = val.toString();
220+
{
221+
v = field().displayString( val );
222+
}
221223

222224
if ( mTextEdit )
223225
{

src/gui/qgsfieldvalidator.cpp

+20-8
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,16 @@ QgsFieldValidator::QgsFieldValidator( QObject *parent, const QgsField &field, co
5656
{
5757
if ( mField.length() > 0 && mField.precision() > 0 )
5858
{
59-
QString re = QStringLiteral( "-?\\d{0,%1}(\\.\\d{0,%2})?" ).arg( mField.length() - mField.precision() ).arg( mField.precision() );
59+
QString re;
60+
// Also accept locale's decimalPoint if it's not a dot
61+
if ( QLocale().decimalPoint() != '.' )
62+
{
63+
re = QStringLiteral( "-?\\d{0,%1}([\\.%2]\\d{0,%3})?" ).arg( mField.length() - mField.precision() ).arg( QLocale().decimalPoint() ).arg( mField.precision() );
64+
}
65+
else
66+
{
67+
re = QStringLiteral( "-?\\d{0,%1}([\\.,]\\d{0,%2})?" ).arg( mField.length() - mField.precision() ).arg( mField.precision() );
68+
}
6069
mValidator = new QRegExpValidator( QRegExp( re ), parent );
6170
}
6271
else if ( mField.length() > 0 && mField.precision() == 0 )
@@ -66,7 +75,16 @@ QgsFieldValidator::QgsFieldValidator( QObject *parent, const QgsField &field, co
6675
}
6776
else if ( mField.precision() > 0 )
6877
{
69-
QString re = QStringLiteral( "-?\\d*(\\.\\d{0,%1})?" ).arg( mField.precision() );
78+
QString re;
79+
// Also accept locale's decimalPoint if it's not a dot
80+
if ( QLocale().decimalPoint() != '.' )
81+
{
82+
re = QStringLiteral( "-?\\d*([\\.%1]\\d{0,%2})?" ).arg( QLocale().decimalPoint(), mField.precision() );
83+
}
84+
else
85+
{
86+
re = QStringLiteral( "-?\\d*([\\.]\\d{0,%1})?" ).arg( mField.precision() );
87+
}
7088
mValidator = new QRegExpValidator( QRegExp( re ), parent );
7189
}
7290
else
@@ -112,12 +130,6 @@ QValidator::State QgsFieldValidator::validate( QString &s, int &i ) const
112130
// delegate to the child validator if any
113131
if ( mValidator )
114132
{
115-
// force to use the '.' as a decimal point or in case we are using QDoubleValidator
116-
// we can get a valid number with a comma depending on current locale
117-
// ... but this will fail subsequently when converted from string to double and
118-
// becomes a NULL!
119-
if ( mField.type() == QVariant::Double )
120-
s = s.replace( ',', '.' );
121133
QValidator::State result = mValidator->validate( s, i );
122134
return result;
123135
}

0 commit comments

Comments
 (0)