Skip to content

Commit d0026d1

Browse files
authored
Merge pull request #7769 from elpaso/bugfix-19695-qlocale-permissive-input
Bugfix 19695 qlocale permissive input
2 parents 5703390 + 2bf72b7 commit d0026d1

File tree

9 files changed

+101
-10
lines changed

9 files changed

+101
-10
lines changed

python/core/auto_generated/qgis.sip.in

+15
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,21 @@ numbers of digits between thousand separators
162162
.. versionadded:: 2.9
163163
%End
164164

165+
qlonglong qgsPermissiveToLongLong( QString string, bool &ok );
166+
%Docstring
167+
Converts a string to an qlonglong in a permissive way, e.g., allowing for incorrect
168+
numbers of digits between thousand separators
169+
170+
:param string: string to convert
171+
:param ok: will be set to true if conversion was successful
172+
173+
:return: string converted to int if possible
174+
175+
.. seealso:: :py:func:`permissiveToInt`
176+
177+
.. versionadded:: 3.4
178+
%End
179+
165180
bool qgsVariantLessThan( const QVariant &lhs, const QVariant &rhs );
166181
%Docstring
167182
Compares two QVariant values and returns whether the first is less than the second.

src/core/qgis.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,13 @@ int qgsPermissiveToInt( QString string, bool &ok )
108108
return QLocale().toInt( string, &ok );
109109
}
110110

111+
qlonglong qgsPermissiveToLongLong( QString string, bool &ok )
112+
{
113+
//remove any thousands separators
114+
string.remove( QLocale().groupSeparator() );
115+
return QLocale().toLongLong( string, &ok );
116+
}
117+
111118
void *qgsMalloc( size_t size )
112119
{
113120
if ( size == 0 || long( size ) < 0 )

src/core/qgis.h

+11
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,17 @@ CORE_EXPORT double qgsPermissiveToDouble( QString string, bool &ok );
409409
*/
410410
CORE_EXPORT int qgsPermissiveToInt( QString string, bool &ok );
411411

412+
/**
413+
* Converts a string to an qlonglong in a permissive way, e.g., allowing for incorrect
414+
* numbers of digits between thousand separators
415+
* \param string string to convert
416+
* \param ok will be set to true if conversion was successful
417+
* \returns string converted to int if possible
418+
* \see permissiveToInt
419+
* \since QGIS 3.4
420+
*/
421+
CORE_EXPORT qlonglong qgsPermissiveToLongLong( QString string, bool &ok );
422+
412423
/**
413424
* Compares two QVariant values and returns whether the first is less than the second.
414425
* Useful for sorting lists of variants, correctly handling sorting of the various

src/core/qgsfield.cpp

+5-5
Original file line numberDiff line numberDiff line change
@@ -285,8 +285,8 @@ bool QgsField::convertCompatible( QVariant &v ) const
285285
if ( !tmp.convert( d->type ) )
286286
{
287287
// This might be a string with thousand separator: use locale to convert
288-
bool ok;
289-
double d = QLocale().toDouble( v.toString(), &ok );
288+
bool ok = false;
289+
double d = qgsPermissiveToDouble( v.toString(), ok );
290290
if ( ok )
291291
{
292292
v = QVariant( d );
@@ -295,7 +295,7 @@ bool QgsField::convertCompatible( QVariant &v ) const
295295
// For not 'dot' locales, we also want to accept '.'
296296
if ( QLocale().decimalPoint() != '.' )
297297
{
298-
d = QLocale( QLocale::English ).toDouble( v.toString(), &ok );
298+
d = QLocale( QLocale::C ).toDouble( v.toString(), &ok );
299299
if ( ok )
300300
{
301301
v = QVariant( d );
@@ -313,7 +313,7 @@ bool QgsField::convertCompatible( QVariant &v ) const
313313
{
314314
// This might be a string with thousand separator: use locale to convert
315315
bool ok;
316-
int i = QLocale().toInt( v.toString(), &ok );
316+
int i = qgsPermissiveToInt( v.toString(), ok );
317317
if ( ok )
318318
{
319319
v = QVariant( i );
@@ -330,7 +330,7 @@ bool QgsField::convertCompatible( QVariant &v ) const
330330
{
331331
// This might be a string with thousand separator: use locale to convert
332332
bool ok;
333-
qlonglong l = QLocale().toLongLong( v.toString(), &ok );
333+
qlonglong l = qgsPermissiveToLongLong( v.toString(), ok );
334334
if ( ok )
335335
{
336336
v = QVariant( l );

src/gui/editorwidgets/qgstexteditwrapper.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,15 @@ void QgsTextEditWrapper::setWidgetValue( const QVariant &val )
221221
v = field().displayString( val );
222222
}
223223

224+
// For numbers, remove the group separator that might cause validation errors
225+
// when the user is editing the field value.
226+
// We are checking for editable layer because in the form field context we do not
227+
// want to strip the separator unless the layer is editable
228+
if ( layer() && layer()->isEditable() && ! QLocale().groupSeparator().isNull() && field().isNumeric() )
229+
{
230+
v = v.remove( QLocale().groupSeparator() );
231+
}
232+
224233
if ( mTextEdit )
225234
{
226235
if ( val != value() )

src/gui/qgsattributeform.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -1463,6 +1463,12 @@ void QgsAttributeForm::init()
14631463
connect( mLayer, &QgsVectorLayer::editingStarted, this, &QgsAttributeForm::synchronizeEnabledState );
14641464
connect( mLayer, &QgsVectorLayer::editingStopped, this, &QgsAttributeForm::synchronizeEnabledState );
14651465

1466+
// This triggers a refresh of the form widget and gives a chance to re-format the
1467+
// value to those widgets that have a different representation when in edit mode
1468+
connect( mLayer, &QgsVectorLayer::editingStarted, this, [ = ] { setFeature( feature() ); } );
1469+
connect( mLayer, &QgsVectorLayer::editingStopped, this, [ = ] { setFeature( feature() ); } );
1470+
1471+
14661472
Q_FOREACH ( QgsAttributeFormInterface *iface, mInterfaces )
14671473
{
14681474
iface->initForm();

src/gui/symbology/qgsgraduatedsymbolrendererwidget.cpp

+16-5
Original file line numberDiff line numberDiff line change
@@ -1013,8 +1013,13 @@ void QgsGraduatedSymbolRendererWidget::changeRange( int rangeIdx )
10131013

10141014
if ( dialog.exec() == QDialog::Accepted )
10151015
{
1016-
double lowerValue = dialog.lowerValue().toDouble();
1017-
double upperValue = dialog.upperValue().toDouble();
1016+
bool ok = false;
1017+
double lowerValue = qgsPermissiveToDouble( dialog.lowerValue(), ok );
1018+
if ( ! ok )
1019+
lowerValue = 0.0;
1020+
double upperValue = qgsPermissiveToDouble( dialog.upperValue(), ok );
1021+
if ( ! ok )
1022+
upperValue = 0.0;
10181023
mRenderer->updateRangeUpperValue( rangeIdx, upperValue );
10191024
mRenderer->updateRangeLowerValue( rangeIdx, lowerValue );
10201025

@@ -1138,9 +1143,15 @@ QList<QgsSymbol *> QgsGraduatedSymbolRendererWidget::selectedSymbols()
11381143
{
11391144
continue;
11401145
}
1141-
1142-
double lowerBound = list.at( 0 ).toDouble();
1143-
double upperBound = list.at( 2 ).toDouble();
1146+
// Not strictly necessary because the range should have been sanitized already
1147+
// after user input, but being permissive never hurts
1148+
bool ok = false;
1149+
double lowerBound = qgsPermissiveToDouble( list.at( 0 ), ok );
1150+
if ( ! ok )
1151+
lowerBound = 0.0;
1152+
double upperBound = qgsPermissiveToDouble( list.at( 2 ), ok );
1153+
if ( ! ok )
1154+
upperBound = 0.0;
11441155
QgsSymbol *s = findSymbolForRange( lowerBound, upperBound, ranges );
11451156
if ( s )
11461157
{

tests/src/core/testqgis.cpp

+26
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class TestQgis : public QObject
3838

3939
void permissiveToDouble();
4040
void permissiveToInt();
41+
void permissiveToLongLong();
4142
void doubleToString();
4243
void signalBlocker();
4344
void qVariantCompare_data();
@@ -127,6 +128,31 @@ void TestQgis::permissiveToInt()
127128
QCOMPARE( result, 1000 );
128129
}
129130

131+
void TestQgis::permissiveToLongLong()
132+
{
133+
//good inputs
134+
bool ok = false;
135+
qlonglong result = qgsPermissiveToLongLong( QStringLiteral( "1000" ), ok );
136+
QVERIFY( ok );
137+
QCOMPARE( result, 1000 );
138+
ok = false;
139+
result = qgsPermissiveToLongLong( QStringLiteral( "1%01000" ).arg( QLocale().groupSeparator() ), ok );
140+
QVERIFY( ok );
141+
QCOMPARE( result, 1000 );
142+
143+
//bad input
144+
ok = false;
145+
( void ) qgsPermissiveToLongLong( QStringLiteral( "a" ), ok );
146+
QVERIFY( !ok );
147+
148+
//messy input (invalid thousand separator position), should still be converted
149+
ok = false;
150+
result = qgsPermissiveToLongLong( QStringLiteral( "10%0100" ).arg( QLocale().groupSeparator() ), ok );
151+
QVERIFY( ok );
152+
QCOMPARE( result, 1000 );
153+
154+
}
155+
130156
void TestQgis::doubleToString()
131157
{
132158
QCOMPARE( qgsDoubleToString( 5.6783212, 5 ), QString( "5.67832" ) );

tests/src/core/testqgsfield.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,12 @@ void TestQgsField::convertCompatible()
607607
QCOMPARE( stringDouble.type(), QVariant::Double );
608608
QCOMPARE( stringDouble, QVariant( 1223456.012345 ) );
609609

610+
// Test that wrongly formatted decimal separator are also accepted
611+
QLocale::setDefault( QLocale::German );
612+
stringDouble = QVariant( "12.23.456,012345" );
613+
QVERIFY( doubleField.convertCompatible( stringDouble ) );
614+
QCOMPARE( stringDouble.type(), QVariant::Double );
615+
QCOMPARE( stringDouble, QVariant( 1223456.012345 ) );
610616

611617
}
612618

0 commit comments

Comments
 (0)