diff --git a/src/app/vertextool/qgsvertexeditor.cpp b/src/app/vertextool/qgsvertexeditor.cpp index ffbe083afc78..34f8bea84d1f 100644 --- a/src/app/vertextool/qgsvertexeditor.cpp +++ b/src/app/vertextool/qgsvertexeditor.cpp @@ -211,15 +211,23 @@ bool QgsVertexEditorModel::setData( const QModelIndex &index, const QVariant &va return false; } - double x = ( index.column() == 0 ? value.toDouble() : mSelectedFeature->vertexMap().at( index.row() )->point().x() ); - double y = ( index.column() == 1 ? value.toDouble() : mSelectedFeature->vertexMap().at( index.row() )->point().y() ); + // Get double value wrt current locale. + bool ok; + double doubleValue = QLocale().toDouble( value.toString(), &ok ); + // If not valid and locale's decimal point is not '.' let's try with english locale + if ( ! ok && QLocale().decimalPoint() != '.' ) + { + doubleValue = QLocale( QLocale::English ).toDouble( value.toString() ); + } + + double x = ( index.column() == 0 ? doubleValue : mSelectedFeature->vertexMap().at( index.row() )->point().x() ); + double y = ( index.column() == 1 ? doubleValue : mSelectedFeature->vertexMap().at( index.row() )->point().y() ); if ( index.column() == mRCol ) // radius modified { if ( index.row() == 0 || index.row() >= mSelectedFeature->vertexMap().count() - 1 ) return false; - double r = value.toDouble(); double x1 = mSelectedFeature->vertexMap().at( index.row() - 1 )->point().x(); double y1 = mSelectedFeature->vertexMap().at( index.row() - 1 )->point().y(); double x2 = x; @@ -228,7 +236,7 @@ bool QgsVertexEditorModel::setData( const QModelIndex &index, const QVariant &va double y3 = mSelectedFeature->vertexMap().at( index.row() + 1 )->point().y(); QgsPoint result; - if ( QgsGeometryUtils::segmentMidPoint( QgsPoint( x1, y1 ), QgsPoint( x3, y3 ), result, r, QgsPoint( x2, y2 ) ) ) + if ( QgsGeometryUtils::segmentMidPoint( QgsPoint( x1, y1 ), QgsPoint( x3, y3 ), result, doubleValue, QgsPoint( x2, y2 ) ) ) { x = result.x(); y = result.y(); diff --git a/src/core/qgsfield.cpp b/src/core/qgsfield.cpp index a9d2a18fc04c..7e4c4fb74b15 100644 --- a/src/core/qgsfield.cpp +++ b/src/core/qgsfield.cpp @@ -22,6 +22,7 @@ #include #include +#include /*************************************************************************** * This class is considered CRITICAL and any change MUST be accompanied with @@ -208,9 +209,51 @@ QString QgsField::displayString( const QVariant &v ) const return QgsApplication::nullRepresentation(); } - if ( d->type == QVariant::Double && d->precision > 0 ) - return QString::number( v.toDouble(), 'f', d->precision ); - + // Special treatment for numeric types if group separator is set or decimalPoint is not a dot + if ( d->type == QVariant::Double ) + { + // Locales with decimal point != '.' or that require group separator: use QLocale + if ( QLocale().decimalPoint() != '.' || + !( QLocale().numberOptions() & QLocale::NumberOption::OmitGroupSeparator ) ) + { + if ( d->precision > 0 ) + { + return QLocale().toString( v.toDouble(), 'f', d->precision ); + } + else + { + // Precision is not set, let's guess it from the + // standard conversion to string + QString s( v.toString() ); + int dotPosition( s.indexOf( '.' ) ); + int precision; + if ( dotPosition < 0 ) + { + precision = 0; + } + else + { + precision = s.length() - dotPosition - 1; + } + return QLocale().toString( v.toDouble(), 'f', precision ); + } + } + // Default for doubles with precision + else if ( d->type == QVariant::Double && d->precision > 0 ) + { + return QString::number( v.toDouble(), 'f', d->precision ); + } + } + // Other numeric types than doubles + else if ( isNumeric() && + ! QLocale().numberOptions() & QLocale::NumberOption::OmitGroupSeparator ) + { + bool ok; + qlonglong converted( v.toLongLong( &ok ) ); + if ( ok ) + return QLocale().toString( converted ); + } + // Fallback if special rules do not apply return v.toString(); } @@ -234,6 +277,68 @@ bool QgsField::convertCompatible( QVariant &v ) const return false; } + // Give it a chance to convert to double since for not '.' locales + // we accept both comma and dot as decimal point + if ( d->type == QVariant::Double && v.type() == QVariant::String ) + { + QVariant tmp( v ); + if ( !tmp.convert( d->type ) ) + { + // This might be a string with thousand separator: use locale to convert + bool ok; + double d = QLocale().toDouble( v.toString(), &ok ); + if ( ok ) + { + v = QVariant( d ); + return true; + } + // For not 'dot' locales, we also want to accept '.' + if ( QLocale().decimalPoint() != '.' ) + { + d = QLocale( QLocale::English ).toDouble( v.toString(), &ok ); + if ( ok ) + { + v = QVariant( d ); + return true; + } + } + } + } + + // For string representation of an int we also might have thousand separator + if ( d->type == QVariant::Int && v.type() == QVariant::String ) + { + QVariant tmp( v ); + if ( !tmp.convert( d->type ) ) + { + // This might be a string with thousand separator: use locale to convert + bool ok; + int i = QLocale().toInt( v.toString(), &ok ); + if ( ok ) + { + v = QVariant( i ); + return true; + } + } + } + + // For string representation of a long we also might have thousand separator + if ( d->type == QVariant::LongLong && v.type() == QVariant::String ) + { + QVariant tmp( v ); + if ( !tmp.convert( d->type ) ) + { + // This might be a string with thousand separator: use locale to convert + bool ok; + qlonglong l = QLocale().toLongLong( v.toString(), &ok ); + if ( ok ) + { + v = QVariant( l ); + return true; + } + } + } + //String representations of doubles in QVariant will return false to convert( QVariant::Int ) //work around this by first converting to double, and then checking whether the double is convertible to int if ( d->type == QVariant::Int && v.canConvert( QVariant::Double ) ) @@ -258,6 +363,7 @@ bool QgsField::convertCompatible( QVariant &v ) const return true; } + if ( !v.convert( d->type ) ) { v = QVariant( d->type ); diff --git a/src/gui/editorwidgets/qgstexteditwrapper.cpp b/src/gui/editorwidgets/qgstexteditwrapper.cpp index 989f6ac31929..25fd2245e62d 100644 --- a/src/gui/editorwidgets/qgstexteditwrapper.cpp +++ b/src/gui/editorwidgets/qgstexteditwrapper.cpp @@ -217,7 +217,9 @@ void QgsTextEditWrapper::setWidgetValue( const QVariant &val ) v = QgsApplication::nullRepresentation(); } else - v = val.toString(); + { + v = field().displayString( val ); + } if ( mTextEdit ) { diff --git a/src/gui/qgsfieldvalidator.cpp b/src/gui/qgsfieldvalidator.cpp index bcc3ebe0e568..5ae4a432ffcd 100644 --- a/src/gui/qgsfieldvalidator.cpp +++ b/src/gui/qgsfieldvalidator.cpp @@ -56,7 +56,16 @@ QgsFieldValidator::QgsFieldValidator( QObject *parent, const QgsField &field, co { if ( mField.length() > 0 && mField.precision() > 0 ) { - QString re = QStringLiteral( "-?\\d{0,%1}(\\.\\d{0,%2})?" ).arg( mField.length() - mField.precision() ).arg( mField.precision() ); + QString re; + // Also accept locale's decimalPoint if it's not a dot + if ( QLocale().decimalPoint() != '.' ) + { + re = QStringLiteral( "-?\\d{0,%1}([\\.%2]\\d{0,%3})?" ).arg( mField.length() - mField.precision() ).arg( QLocale().decimalPoint() ).arg( mField.precision() ); + } + else + { + re = QStringLiteral( "-?\\d{0,%1}([\\.,]\\d{0,%2})?" ).arg( mField.length() - mField.precision() ).arg( mField.precision() ); + } mValidator = new QRegExpValidator( QRegExp( re ), parent ); } else if ( mField.length() > 0 && mField.precision() == 0 ) @@ -66,7 +75,16 @@ QgsFieldValidator::QgsFieldValidator( QObject *parent, const QgsField &field, co } else if ( mField.precision() > 0 ) { - QString re = QStringLiteral( "-?\\d*(\\.\\d{0,%1})?" ).arg( mField.precision() ); + QString re; + // Also accept locale's decimalPoint if it's not a dot + if ( QLocale().decimalPoint() != '.' ) + { + re = QStringLiteral( "-?\\d*([\\.%1]\\d{0,%2})?" ).arg( QLocale().decimalPoint(), mField.precision() ); + } + else + { + re = QStringLiteral( "-?\\d*([\\.]\\d{0,%1})?" ).arg( mField.precision() ); + } mValidator = new QRegExpValidator( QRegExp( re ), parent ); } else @@ -112,12 +130,6 @@ QValidator::State QgsFieldValidator::validate( QString &s, int &i ) const // delegate to the child validator if any if ( mValidator ) { - // force to use the '.' as a decimal point or in case we are using QDoubleValidator - // we can get a valid number with a comma depending on current locale - // ... but this will fail subsequently when converted from string to double and - // becomes a NULL! - if ( mField.type() == QVariant::Double ) - s = s.replace( ',', '.' ); QValidator::State result = mValidator->validate( s, i ); return result; } diff --git a/tests/src/core/testqgsfield.cpp b/tests/src/core/testqgsfield.cpp index 5a7081260553..3af6386ac582 100644 --- a/tests/src/core/testqgsfield.cpp +++ b/tests/src/core/testqgsfield.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -66,12 +67,12 @@ void TestQgsField::cleanupTestCase() void TestQgsField::init() { - + QLocale::setDefault( QLocale::English ); } void TestQgsField::cleanup() { - + QLocale::setDefault( QLocale::English ); } void TestQgsField::create() @@ -315,12 +316,12 @@ void TestQgsField::displayString() //test int value in int type QgsField intField2( QStringLiteral( "int" ), QVariant::Int, QStringLiteral( "int" ) ); QCOMPARE( intField2.displayString( 5 ), QString( "5" ) ); - QCOMPARE( intField2.displayString( 599999898999LL ), QString( "599999898999" ) ); + QCOMPARE( intField2.displayString( 599999898999LL ), QString( "599,999,898,999" ) ); //test long type QgsField longField( QStringLiteral( "long" ), QVariant::LongLong, QStringLiteral( "longlong" ) ); QCOMPARE( longField.displayString( 5 ), QString( "5" ) ); - QCOMPARE( longField.displayString( 599999898999LL ), QString( "599999898999" ) ); + QCOMPARE( longField.displayString( 599999898999LL ), QString( "599,999,898,999" ) ); //test NULL int QVariant nullInt = QVariant( QVariant::Int ); @@ -332,12 +333,57 @@ void TestQgsField::displayString() QgsField doubleFieldNoPrec( QStringLiteral( "double" ), QVariant::Double, QStringLiteral( "double" ), 10 ); QCOMPARE( doubleFieldNoPrec.displayString( 5.005005 ), QString( "5.005005" ) ); QCOMPARE( doubleFieldNoPrec.displayString( 5.005005005 ), QString( "5.005005005" ) ); - QCOMPARE( doubleFieldNoPrec.displayString( 599999898999.0 ), QString( "599999898999" ) ); + QCOMPARE( QLocale().numberOptions() & QLocale::NumberOption::OmitGroupSeparator, QLocale::NumberOption::DefaultNumberOptions ); + QCOMPARE( doubleFieldNoPrec.displayString( 599999898999.0 ), QString( "599,999,898,999" ) ); //test NULL double QVariant nullDouble = QVariant( QVariant::Double ); QCOMPARE( doubleField.displayString( nullDouble ), QString( "TEST NULL" ) ); + //test double value with German locale + QLocale::setDefault( QLocale::German ); + QCOMPARE( doubleField.displayString( 5.005005 ), QString( "5,005" ) ); + QCOMPARE( doubleFieldNoPrec.displayString( 5.005005 ), QString( "5,005005" ) ); + QCOMPARE( doubleFieldNoPrec.displayString( 5.005005005 ), QString( "5,005005005" ) ); + QCOMPARE( doubleFieldNoPrec.displayString( 599999898999.0 ), QString( "599.999.898.999" ) ); + QCOMPARE( doubleFieldNoPrec.displayString( 5999.123456 ), QString( "5.999,123456" ) ); + + //test value with custom German locale (OmitGroupSeparator) + QLocale customGerman( QLocale::German ); + customGerman.setNumberOptions( QLocale::NumberOption::OmitGroupSeparator ); + QLocale::setDefault( customGerman ); + QCOMPARE( doubleField.displayString( 5.005005 ), QString( "5,005" ) ); + QCOMPARE( doubleFieldNoPrec.displayString( 5.005005 ), QString( "5,005005" ) ); + QCOMPARE( doubleFieldNoPrec.displayString( 5.005005005 ), QString( "5,005005005" ) ); + QCOMPARE( doubleFieldNoPrec.displayString( 599999898999.0 ), QString( "599999898999" ) ); + QCOMPARE( doubleFieldNoPrec.displayString( 5999.123456 ), QString( "5999,123456" ) ); + + //test int value in int type with custom German locale (OmitGroupSeparator) + QCOMPARE( intField2.displayString( 5 ), QString( "5" ) ); + QCOMPARE( intField2.displayString( 599999898999LL ), QString( "599999898999" ) ); + + //test long type with custom German locale (OmitGroupSeparator) + QCOMPARE( longField.displayString( 5 ), QString( "5" ) ); + QCOMPARE( longField.displayString( 599999898999LL ), QString( "599999898999" ) ); + + //test value with custom english locale (OmitGroupSeparator) + QLocale customEnglish( QLocale::English ); + customEnglish.setNumberOptions( QLocale::NumberOption::OmitGroupSeparator ); + QLocale::setDefault( customEnglish ); + QCOMPARE( doubleField.displayString( 5.005005 ), QString( "5.005" ) ); + QCOMPARE( doubleFieldNoPrec.displayString( 5.005005 ), QString( "5.005005" ) ); + QCOMPARE( doubleFieldNoPrec.displayString( 5.005005005 ), QString( "5.005005005" ) ); + QCOMPARE( doubleFieldNoPrec.displayString( 599999898999.0 ), QString( "599999898999" ) ); + QCOMPARE( doubleFieldNoPrec.displayString( 5999.123456 ), QString( "5999.123456" ) ); + + //test int value in int type with custom english locale (OmitGroupSeparator) + QCOMPARE( intField2.displayString( 5 ), QString( "5" ) ); + QCOMPARE( intField2.displayString( 599999898999LL ), QString( "599999898999" ) ); + + //test long type with custom english locale (OmitGroupSeparator) + QCOMPARE( longField.displayString( 5 ), QString( "5" ) ); + QCOMPARE( longField.displayString( 599999898999LL ), QString( "599999898999" ) ); + } void TestQgsField::convertCompatible() @@ -452,6 +498,44 @@ void TestQgsField::convertCompatible() QCOMPARE( longlong.type(), QVariant::LongLong ); QCOMPARE( longlong, QVariant( 99999999999999999LL ) ); + //string representation of an int + QVariant stringInt( "123456" ); + QVERIFY( intField.convertCompatible( stringInt ) ); + QCOMPARE( stringInt.type(), QVariant::Int ); + QCOMPARE( stringInt, QVariant( 123456 ) ); + // now with group separator for english locale + stringInt = QVariant( "123,456" ); + QVERIFY( intField.convertCompatible( stringInt ) ); + QCOMPARE( stringInt.type(), QVariant::Int ); + QCOMPARE( stringInt, QVariant( "123456" ) ); + + //string representation of a longlong + QVariant stringLong( "99999999999999999" ); + QVERIFY( longlongField.convertCompatible( stringLong ) ); + QCOMPARE( stringLong.type(), QVariant::LongLong ); + QCOMPARE( stringLong, QVariant( 99999999999999999LL ) ); + // now with group separator for english locale + stringLong = QVariant( "99,999,999,999,999,999" ); + QVERIFY( longlongField.convertCompatible( stringLong ) ); + QCOMPARE( stringLong.type(), QVariant::LongLong ); + QCOMPARE( stringLong, QVariant( 99999999999999999LL ) ); + + + //string representation of a double + QVariant stringDouble( "123456.012345" ); + QVERIFY( doubleField.convertCompatible( stringDouble ) ); + QCOMPARE( stringDouble.type(), QVariant::Double ); + QCOMPARE( stringDouble, QVariant( 123456.012345 ) ); + // now with group separator for english locale + stringDouble = QVariant( "1,223,456.012345" ); + QVERIFY( doubleField.convertCompatible( stringDouble ) ); + QCOMPARE( stringDouble.type(), QVariant::Double ); + QCOMPARE( stringDouble, QVariant( 1223456.012345 ) ); + // This should not convert + stringDouble = QVariant( "1.223.456,012345" ); + QVERIFY( ! doubleField.convertCompatible( stringDouble ) ); + + //double with precision QgsField doubleWithPrecField( QStringLiteral( "double" ), QVariant::Double, QStringLiteral( "double" ), 10, 3 ); doubleVar = QVariant( 10.12345678 ); @@ -466,6 +550,62 @@ void TestQgsField::convertCompatible() QVERIFY( !stringWithLen.convertCompatible( stringVar ) ); QCOMPARE( stringVar.type(), QVariant::String ); QCOMPARE( stringVar.toString(), QString( "lon" ) ); + + + ///////////////////////////////////////////////////////// + // German locale tests + + //double with ',' as decimal separator for German locale + QLocale::setDefault( QLocale::German ); + QVariant doubleCommaVar( "1,2345" ); + QVERIFY( doubleField.convertCompatible( doubleCommaVar ) ); + QCOMPARE( doubleCommaVar.type(), QVariant::Double ); + QCOMPARE( doubleCommaVar.toString(), QString( "1.2345" ) ); + + //string representation of an int + stringInt = QVariant( "123456" ); + QVERIFY( intField.convertCompatible( stringInt ) ); + QCOMPARE( stringInt.type(), QVariant::Int ); + QCOMPARE( stringInt, QVariant( 123456 ) ); + // now with group separator for german locale + stringInt = QVariant( "123.456" ); + QVERIFY( intField.convertCompatible( stringInt ) ); + QCOMPARE( stringInt.type(), QVariant::Int ); + QCOMPARE( stringInt, QVariant( "123456" ) ); + + //string representation of a longlong + stringLong = QVariant( "99999999999999999" ); + QVERIFY( longlongField.convertCompatible( stringLong ) ); + QCOMPARE( stringLong.type(), QVariant::LongLong ); + QCOMPARE( stringLong, QVariant( 99999999999999999LL ) ); + // now with group separator for german locale + stringLong = QVariant( "99.999.999.999.999.999" ); + QVERIFY( longlongField.convertCompatible( stringLong ) ); + QCOMPARE( stringLong.type(), QVariant::LongLong ); + QCOMPARE( stringLong, QVariant( 99999999999999999LL ) ); + + //string representation of a double + stringDouble = QVariant( "123456,012345" ); + QVERIFY( doubleField.convertCompatible( stringDouble ) ); + QCOMPARE( stringDouble.type(), QVariant::Double ); + QCOMPARE( stringDouble, QVariant( 123456.012345 ) ); + // For doubles we also want to accept dot as a decimal point + stringDouble = QVariant( "123456.012345" ); + QVERIFY( doubleField.convertCompatible( stringDouble ) ); + QCOMPARE( stringDouble.type(), QVariant::Double ); + QCOMPARE( stringDouble, QVariant( 123456.012345 ) ); + // now with group separator for german locale + stringDouble = QVariant( "1.223.456,012345" ); + QVERIFY( doubleField.convertCompatible( stringDouble ) ); + QCOMPARE( stringDouble.type(), QVariant::Double ); + QCOMPARE( stringDouble, QVariant( 1223456.012345 ) ); + // Be are good citizens and we also accept english locale + stringDouble = QVariant( "1,223,456.012345" ); + QVERIFY( doubleField.convertCompatible( stringDouble ) ); + QCOMPARE( stringDouble.type(), QVariant::Double ); + QCOMPARE( stringDouble, QVariant( 1223456.012345 ) ); + + } void TestQgsField::dataStream() diff --git a/tests/src/python/test_qgsfieldformatters.py b/tests/src/python/test_qgsfieldformatters.py index 6e7778cba5a0..7bee27288164 100644 --- a/tests/src/python/test_qgsfieldformatters.py +++ b/tests/src/python/test_qgsfieldformatters.py @@ -266,8 +266,14 @@ def setUpClass(cls): QCoreApplication.setOrganizationDomain("QGIS_TestPyQgsColorScheme.com") QCoreApplication.setApplicationName("QGIS_TestPyQgsColorScheme") QgsSettings().clear() + QLocale.setDefault(QLocale(QLocale.English)) start_app() + @classmethod + def tearDownClass(cls): + """Reset locale""" + QLocale.setDefault(QLocale(QLocale.English)) + def test_representValue(self): layer = QgsVectorLayer("point?field=int:integer&field=double:double&field=long:long", @@ -277,23 +283,21 @@ def test_representValue(self): fieldFormatter = QgsRangeFieldFormatter() - QLocale.setDefault(QLocale.c()) - # Precision is ignored for integers and longlongs self.assertEqual(fieldFormatter.representValue(layer, 0, {'Precision': 1}, None, '123'), '123') - self.assertEqual(fieldFormatter.representValue(layer, 0, {'Precision': 1}, None, '123000'), '123000') - self.assertEqual(fieldFormatter.representValue(layer, 0, {'Precision': 1}, None, '9999999'), '9999999') # no scientific notation for integers! + self.assertEqual(fieldFormatter.representValue(layer, 0, {'Precision': 1}, None, '123000'), '123,000') + self.assertEqual(fieldFormatter.representValue(layer, 0, {'Precision': 1}, None, '9999999'), '9,999,999') # no scientific notation for integers! self.assertEqual(fieldFormatter.representValue(layer, 0, {'Precision': 1}, None, None), 'NULL') self.assertEqual(fieldFormatter.representValue(layer, 2, {'Precision': 1}, None, '123'), '123') - self.assertEqual(fieldFormatter.representValue(layer, 2, {'Precision': 1}, None, '123000'), '123000') - self.assertEqual(fieldFormatter.representValue(layer, 2, {'Precision': 1}, None, '9999999'), '9999999') # no scientific notation for long longs! + self.assertEqual(fieldFormatter.representValue(layer, 2, {'Precision': 1}, None, '123000'), '123,000') + self.assertEqual(fieldFormatter.representValue(layer, 2, {'Precision': 1}, None, '9999999'), '9,999,999') # no scientific notation for long longs! self.assertEqual(fieldFormatter.representValue(layer, 2, {'Precision': 1}, None, None), 'NULL') self.assertEqual(fieldFormatter.representValue(layer, 1, {'Precision': 1}, None, None), 'NULL') self.assertEqual(fieldFormatter.representValue(layer, 1, {'Precision': 1}, None, '123'), '123.0') self.assertEqual(fieldFormatter.representValue(layer, 1, {'Precision': 2}, None, None), 'NULL') - self.assertEqual(fieldFormatter.representValue(layer, 1, {'Precision': 2}, None, '123000'), '123000.00') + self.assertEqual(fieldFormatter.representValue(layer, 1, {'Precision': 2}, None, '123000'), '123,000.00') self.assertEqual(fieldFormatter.representValue(layer, 1, {'Precision': 2}, None, '0'), '0.00') self.assertEqual(fieldFormatter.representValue(layer, 1, {'Precision': 2}, None, '123'), '123.00') self.assertEqual(fieldFormatter.representValue(layer, 1, {'Precision': 2}, None, '0.123'), '0.12') @@ -308,6 +312,7 @@ def test_representValue(self): self.assertEqual(fieldFormatter.representValue(layer, 1, {'Precision': 3}, None, '-0.127'), '-0.127') self.assertEqual(fieldFormatter.representValue(layer, 1, {'Precision': 3}, None, '-1.27e-1'), '-0.127') + # Check with Italian locale QLocale.setDefault(QLocale('it')) self.assertEqual(fieldFormatter.representValue(layer, 0, {'Precision': 1}, None, '9999999'), @@ -333,6 +338,19 @@ def test_representValue(self): self.assertEqual(fieldFormatter.representValue(layer, 1, {'Precision': 3}, None, '-0.127'), '-0,127') self.assertEqual(fieldFormatter.representValue(layer, 1, {'Precision': 3}, None, '-1.27e-1'), '-0,127') + # Check with custom locale without thousand separator + + custom = QLocale('en') + custom.setNumberOptions(QLocale.OmitGroupSeparator) + QLocale.setDefault(custom) + + self.assertEqual(fieldFormatter.representValue(layer, 0, {'Precision': 1}, None, '9999999'), + '9999999') # scientific notation for integers! + self.assertEqual(fieldFormatter.representValue(layer, 2, {'Precision': 1}, None, '123'), '123') + self.assertEqual(fieldFormatter.representValue(layer, 2, {'Precision': 1}, None, '123000'), '123000') + self.assertEqual(fieldFormatter.representValue(layer, 2, {'Precision': 1}, None, '9999999'), '9999999') # scientific notation for long longs! + self.assertEqual(fieldFormatter.representValue(layer, 1, {'Precision': 2}, None, '123000'), '123000.00') + QgsProject.instance().removeAllMapLayers() diff --git a/tests/src/python/test_qgsfieldvalidator.py b/tests/src/python/test_qgsfieldvalidator.py index 607277194d8f..ce4a3a5c2357 100644 --- a/tests/src/python/test_qgsfieldvalidator.py +++ b/tests/src/python/test_qgsfieldvalidator.py @@ -14,7 +14,7 @@ import qgis # NOQA -from qgis.PyQt.QtCore import QVariant +from qgis.PyQt.QtCore import QVariant, QLocale from qgis.PyQt.QtGui import QValidator from qgis.core import QgsVectorLayer from qgis.gui import QgsFieldValidator @@ -39,45 +39,46 @@ def tearDown(self): """Run after each test.""" pass - def test_validator(self): - # Test the double - """ + def _fld_checker(self, field): + """ Expected results from validate QValidator::Invalid 0 The string is clearly invalid. QValidator::Intermediate 1 The string is a plausible intermediate value. QValidator::Acceptable 2 The string is acceptable as a final result; i.e. it is valid. """ + DECIMAL_SEPARATOR = QLocale().decimalPoint() + OTHER_SEPARATOR = ',' if DECIMAL_SEPARATOR == '.' else '.' - double_field = self.vl.fields()[self.vl.fields().indexFromName('double_field')] - self.assertEqual(double_field.precision(), 0) # this is what the provider reports :( - self.assertEqual(double_field.length(), 0) # not set - self.assertEqual(double_field.type(), QVariant.Double) - - validator = QgsFieldValidator(None, double_field, '0.0', '') + validator = QgsFieldValidator(None, field, '0.0', '') def _test(value, expected): ret = validator.validate(value, 0) - self.assertEqual(ret[0], expected, value) + self.assertEqual(ret[0], expected, "%s != %s" % (ret[0], expected)) if value: self.assertEqual(validator.validate('-' + value, 0)[0], expected, '-' + value) - # Check the decimal comma separator has been properly transformed - if expected != QValidator.Invalid: - self.assertEqual(ret[1], value.replace(',', '.')) # Valid _test('0.1234', QValidator.Acceptable) - _test('0,1234', QValidator.Acceptable) - _test('12345.1234e+123', QValidator.Acceptable) - _test('12345.1234e-123', QValidator.Acceptable) - _test('12345,1234e+123', QValidator.Acceptable) - _test('12345,1234e-123', QValidator.Acceptable) - _test('', QValidator.Acceptable) - # Out of range - _test('12345.1234e+823', QValidator.Intermediate) - _test('12345.1234e-823', QValidator.Intermediate) - _test('12345,1234e+823', QValidator.Intermediate) - _test('12345,1234e-823', QValidator.Intermediate) + # Apparently we accept comma only when locale say so + if DECIMAL_SEPARATOR != '.': + _test('0,1234', QValidator.Acceptable) + + # If precision is > 0, regexp validator is used (and it does not support sci notation) + if field.precision() == 0: + _test('12345.1234e+123', QValidator.Acceptable) + _test('12345.1234e-123', QValidator.Acceptable) + if DECIMAL_SEPARATOR != '.': + _test('12345,1234e+123', QValidator.Acceptable) + _test('12345,1234e-123', QValidator.Acceptable) + _test('', QValidator.Acceptable) + + # Out of range + _test('12345.1234e+823', QValidator.Intermediate) + _test('12345.1234e-823', QValidator.Intermediate) + if DECIMAL_SEPARATOR != '.': + _test('12345,1234e+823', QValidator.Intermediate) + _test('12345,1234e-823', QValidator.Intermediate) # Invalid _test('12345-1234', QValidator.Invalid) @@ -97,9 +98,39 @@ def _test(value, expected): # Invalid _test('12345-1234', QValidator.Invalid) - _test('12345.1234', QValidator.Invalid) + _test('12345%s1234' % DECIMAL_SEPARATOR, QValidator.Invalid) _test('onetwothree', QValidator.Invalid) + def test_doubleValidator(self): + """Test the double with default (system) locale""" + field = self.vl.fields()[self.vl.fields().indexFromName('double_field')] + self.assertEqual(field.precision(), 0) # this is what the provider reports :( + self.assertEqual(field.length(), 0) # not set + self.assertEqual(field.type(), QVariant.Double) + self._fld_checker(field) + + def test_doubleValidatorCommaLocale(self): + """Test the double with german locale""" + QLocale.setDefault(QLocale(QLocale.German, QLocale.Germany)) + assert QLocale().decimalPoint() == ',' + field = self.vl.fields()[self.vl.fields().indexFromName('double_field')] + self._fld_checker(field) + + def test_doubleValidatorDotLocale(self): + """Test the double with english locale""" + QLocale.setDefault(QLocale(QLocale.English)) + assert QLocale().decimalPoint() == '.' + field = self.vl.fields()[self.vl.fields().indexFromName('double_field')] + self._fld_checker(field) + + def test_precision(self): + """Test different precision""" + QLocale.setDefault(QLocale(QLocale.English)) + assert QLocale().decimalPoint() == '.' + field = self.vl.fields()[self.vl.fields().indexFromName('double_field')] + field.setPrecision(4) + self._fld_checker(field) + if __name__ == '__main__': unittest.main()