Merge pull request #6246 from elpaso/rangeformatter

[bugfix] Range formatter for doubles and ints
This commit is contained in:
Alessandro Pasotti 2018-02-02 14:25:28 +01:00 committed by GitHub
commit 5c5ef3b6de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 274 additions and 2 deletions

View File

@ -287,6 +287,7 @@
%Include geometry/qgswkbtypes.sip %Include geometry/qgswkbtypes.sip
%Include ./3d/qgs3drendererregistry.sip %Include ./3d/qgs3drendererregistry.sip
%Include ./3d/qgsabstract3drenderer.sip %Include ./3d/qgsabstract3drenderer.sip
%Include fieldformatter/qgsrangefieldformatter.sip
%Include fieldformatter/qgsdatetimefieldformatter.sip %Include fieldformatter/qgsdatetimefieldformatter.sip
%Include fieldformatter/qgsfallbackfieldformatter.sip %Include fieldformatter/qgsfallbackfieldformatter.sip
%Include fieldformatter/qgskeyvaluefieldformatter.sip %Include fieldformatter/qgskeyvaluefieldformatter.sip

View File

@ -0,0 +1,42 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/fieldformatter/qgsrangefieldformatter.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
class QgsRangeFieldFormatter : QgsFieldFormatter
{
%Docstring
Field formatter for a range (double) field with precision and locale
.. versionadded:: 3.0
%End
%TypeHeaderCode
#include "qgsrangefieldformatter.h"
%End
public:
QgsRangeFieldFormatter();
%Docstring
Default constructor of field formatter for a range (double)field.
%End
virtual QString id() const;
virtual QString representValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const;
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/fieldformatter/qgsrangefieldformatter.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/

View File

@ -117,6 +117,13 @@ Defines if the clear value should be the minimum or maximum values of the widget
Returns the value used when clear() is called. Returns the value used when clear() is called.
.. seealso:: :py:func:`setClearValue` .. seealso:: :py:func:`setClearValue`
%End
void setLineEditAlignment( Qt::Alignment alignment );
%Docstring
Set alignment in the embedded line edit widget
:param alignment:
%End %End
virtual double valueFromText( const QString &text ) const; virtual double valueFromText( const QString &text ) const;

View File

@ -117,6 +117,13 @@ Defines if the clear value should be the minimum or maximum values of the widget
Returns the value used when clear() is called. Returns the value used when clear() is called.
.. seealso:: :py:func:`setClearValue` .. seealso:: :py:func:`setClearValue`
%End
void setLineEditAlignment( Qt::Alignment alignment );
%Docstring
Set alignment in the embedded line edit widget
:param alignment:
%End %End
virtual int valueFromText( const QString &text ) const; virtual int valueFromText( const QString &text ) const;

View File

@ -473,6 +473,7 @@ SET(QGIS_CORE_SRCS
3d/qgs3drendererregistry.cpp 3d/qgs3drendererregistry.cpp
3d/qgsabstract3drenderer.cpp 3d/qgsabstract3drenderer.cpp
fieldformatter/qgsrangefieldformatter.cpp
fieldformatter/qgsdatetimefieldformatter.cpp fieldformatter/qgsdatetimefieldformatter.cpp
fieldformatter/qgsfallbackfieldformatter.cpp fieldformatter/qgsfallbackfieldformatter.cpp
fieldformatter/qgskeyvaluefieldformatter.cpp fieldformatter/qgskeyvaluefieldformatter.cpp
@ -1110,6 +1111,7 @@ SET(QGIS_CORE_HDRS
3d/qgs3drendererregistry.h 3d/qgs3drendererregistry.h
3d/qgsabstract3drenderer.h 3d/qgsabstract3drenderer.h
fieldformatter/qgsrangefieldformatter.h
fieldformatter/qgsdatetimefieldformatter.h fieldformatter/qgsdatetimefieldformatter.h
fieldformatter/qgsfallbackfieldformatter.h fieldformatter/qgsfallbackfieldformatter.h
fieldformatter/qgskeyvaluefieldformatter.h fieldformatter/qgskeyvaluefieldformatter.h

View File

@ -0,0 +1,86 @@
/***************************************************************************
qgsrangefieldformatter.cpp - QgsRangeFieldFormatter
---------------------
begin : 01/02/2018
copyright : (C) 2018 by Alessandro Pasotti
email : elpaso at itopen dot it
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <QLocale>
#include "qgsrangefieldformatter.h"
#include "qgssettings.h"
#include "qgsfield.h"
#include "qgsvectorlayer.h"
QString QgsRangeFieldFormatter::id() const
{
return QStringLiteral( "Range" );
}
QString QgsRangeFieldFormatter::representValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const
{
Q_UNUSED( cache )
Q_UNUSED( config )
if ( value.isNull() )
{
return QgsApplication::nullRepresentation();
}
QString result;
// Prepare locale
std::function<QLocale()> f_locale = [ ]
{
QLocale locale( QgsApplication::instance()->locale() );
QLocale::NumberOptions options( locale.numberOptions() );
options |= QLocale::NumberOption::OmitGroupSeparator;
locale.setNumberOptions( options );
return locale;
};
const QgsField field = layer->fields().at( fieldIndex );
if ( field.type() == QVariant::Double &&
config.contains( QStringLiteral( "Precision" ) ) &&
value.isValid( ) )
{
bool ok;
double val( value.toDouble( &ok ) );
if ( ok )
{
int precision( config[ QStringLiteral( "Precision" ) ].toInt( &ok ) );
if ( ok )
{
// TODO: make the format configurable!
result = f_locale().toString( val, 'f', precision );
}
}
}
else if ( field.type() == QVariant::Int &&
value.isValid( ) )
{
bool ok;
double val( value.toInt( &ok ) );
if ( ok )
{
result = f_locale().toString( val );
}
}
else
{
result = value.toString();
}
return result;
}

View File

@ -0,0 +1,43 @@
/***************************************************************************
qgsrangefieldformatter.h - QgsRangeFieldFormatter
---------------------
begin : 01/02/2018
copyright : (C) 2018 by Alessandro Pasotti
email : elpaso at itopen dot it
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSRANGEFIELDFORMATTER_H
#define QGSRANGEFIELDFORMATTER_H
#include "qgis_core.h"
#include "qgsfieldformatter.h"
/**
* \ingroup core
* Field formatter for a range (double) field with precision and locale
*
* \since QGIS 3.0
*/
class CORE_EXPORT QgsRangeFieldFormatter : public QgsFieldFormatter
{
public:
/**
* Default constructor of field formatter for a range (double)field.
*/
QgsRangeFieldFormatter() = default;
QString id() const override;
QString representValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const override;
};
#endif // QGSRANGEFIELDFORMATTER_H

View File

@ -22,6 +22,7 @@
#include "qgsrelationreferencefieldformatter.h" #include "qgsrelationreferencefieldformatter.h"
#include "qgskeyvaluefieldformatter.h" #include "qgskeyvaluefieldformatter.h"
#include "qgslistfieldformatter.h" #include "qgslistfieldformatter.h"
#include "qgsrangefieldformatter.h"
#include "qgsfallbackfieldformatter.h" #include "qgsfallbackfieldformatter.h"
@ -34,6 +35,7 @@ QgsFieldFormatterRegistry::QgsFieldFormatterRegistry( QObject *parent )
addFieldFormatter( new QgsKeyValueFieldFormatter() ); addFieldFormatter( new QgsKeyValueFieldFormatter() );
addFieldFormatter( new QgsListFieldFormatter() ); addFieldFormatter( new QgsListFieldFormatter() );
addFieldFormatter( new QgsDateTimeFieldFormatter() ); addFieldFormatter( new QgsDateTimeFieldFormatter() );
addFieldFormatter( new QgsRangeFieldFormatter() );
mFallbackFieldFormatter = new QgsFallbackFieldFormatter(); mFallbackFieldFormatter = new QgsFallbackFieldFormatter();
} }

View File

@ -31,6 +31,8 @@ QgsDoubleSpinBox::QgsDoubleSpinBox( QWidget *parent )
{ {
mLineEdit = new QgsSpinBoxLineEdit(); mLineEdit = new QgsSpinBoxLineEdit();
// By default, group separator is off
setLocale( QLocale( QgsApplication::locale( ) ) );
setLineEdit( mLineEdit ); setLineEdit( mLineEdit );
QSize msz = minimumSizeHint(); QSize msz = minimumSizeHint();
@ -134,6 +136,11 @@ double QgsDoubleSpinBox::clearValue() const
return mCustomClearValue; return mCustomClearValue;
} }
void QgsDoubleSpinBox::setLineEditAlignment( Qt::Alignment alignment )
{
mLineEdit->setAlignment( alignment );
}
QString QgsDoubleSpinBox::stripped( const QString &originalText ) const QString QgsDoubleSpinBox::stripped( const QString &originalText ) const
{ {
//adapted from QAbstractSpinBoxPrivate::stripped //adapted from QAbstractSpinBoxPrivate::stripped

View File

@ -126,6 +126,12 @@ class GUI_EXPORT QgsDoubleSpinBox : public QDoubleSpinBox
*/ */
double clearValue() const; double clearValue() const;
/**
* Set alignment in the embedded line edit widget
* \param alignment
*/
void setLineEditAlignment( Qt::Alignment alignment );
double valueFromText( const QString &text ) const override; double valueFromText( const QString &text ) const override;
QValidator::State validate( QString &input, int &pos ) const override; QValidator::State validate( QString &input, int &pos ) const override;
void paintEvent( QPaintEvent *e ) override; void paintEvent( QPaintEvent *e ) override;

View File

@ -47,12 +47,14 @@ QWidget *QgsRangeWidgetWrapper::createWidget( QWidget *parent )
case QVariant::Double: case QVariant::Double:
{ {
editor = new QgsDoubleSpinBox( parent ); editor = new QgsDoubleSpinBox( parent );
static_cast<QgsDoubleSpinBox *>( editor )->setLineEditAlignment( Qt::AlignRight );
break; break;
} }
case QVariant::Int: case QVariant::Int:
case QVariant::LongLong: case QVariant::LongLong:
default: default:
editor = new QgsSpinBox( parent ); editor = new QgsSpinBox( parent );
static_cast<QgsSpinBox *>( editor )->setLineEditAlignment( Qt::AlignRight );
break; break;
} }
} }

View File

@ -30,7 +30,6 @@ QgsSpinBox::QgsSpinBox( QWidget *parent )
: QSpinBox( parent ) : QSpinBox( parent )
{ {
mLineEdit = new QgsSpinBoxLineEdit(); mLineEdit = new QgsSpinBoxLineEdit();
setLineEdit( mLineEdit ); setLineEdit( mLineEdit );
QSize msz = minimumSizeHint(); QSize msz = minimumSizeHint();
@ -133,6 +132,11 @@ int QgsSpinBox::clearValue() const
return mCustomClearValue; return mCustomClearValue;
} }
void QgsSpinBox::setLineEditAlignment( Qt::Alignment alignment )
{
mLineEdit->setAlignment( alignment );
}
int QgsSpinBox::valueFromText( const QString &text ) const int QgsSpinBox::valueFromText( const QString &text ) const
{ {
if ( !mExpressionsEnabled ) if ( !mExpressionsEnabled )

View File

@ -126,6 +126,12 @@ class GUI_EXPORT QgsSpinBox : public QSpinBox
*/ */
int clearValue() const; int clearValue() const;
/**
* Set alignment in the embedded line edit widget
* \param alignment
*/
void setLineEditAlignment( Qt::Alignment alignment );
int valueFromText( const QString &text ) const override; int valueFromText( const QString &text ) const override;
QValidator::State validate( QString &input, int &pos ) const override; QValidator::State validate( QString &input, int &pos ) const override;

View File

@ -16,7 +16,7 @@ import qgis # NOQA
from qgis.core import (QgsFeature, QgsProject, QgsRelation, QgsVectorLayer, from qgis.core import (QgsFeature, QgsProject, QgsRelation, QgsVectorLayer,
QgsValueMapFieldFormatter, QgsValueRelationFieldFormatter, QgsValueMapFieldFormatter, QgsValueRelationFieldFormatter,
QgsRelationReferenceFieldFormatter, QgsSettings) QgsRelationReferenceFieldFormatter, QgsRangeFieldFormatter, QgsSettings)
from qgis.testing import start_app, unittest from qgis.testing import start_app, unittest
@ -197,5 +197,62 @@ class TestQgsRelationReferenceFieldFormatter(unittest.TestCase):
QgsProject.instance().removeAllMapLayers() QgsProject.instance().removeAllMapLayers()
class TestQgsRangeFieldFormatter(unittest.TestCase):
def test_representValue(self):
layer = QgsVectorLayer("point?field=int:integer&field=double:double",
"layer", "memory")
self.assertTrue(layer.isValid())
QgsProject.instance().addMapLayers([layer])
fieldFormatter = QgsRangeFieldFormatter()
# Precision is ignored for integers
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, 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, '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')
self.assertEqual(fieldFormatter.representValue(layer, 1, {'Precision': 2}, None, '0.127'), '0.13')
self.assertEqual(fieldFormatter.representValue(layer, 1, {'Precision': 3}, None, '0'), '0.000')
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')
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')
self.assertEqual(fieldFormatter.representValue(layer, 1, {'Precision': 2}, None, '-0.127'), '-0.13')
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')
QgsSettings().setValue("locale/overrideFlag", True)
QgsSettings().setValue("locale/userLocale", 'it')
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, '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')
self.assertEqual(fieldFormatter.representValue(layer, 1, {'Precision': 2}, None, '0.127'), '0,13')
self.assertEqual(fieldFormatter.representValue(layer, 1, {'Precision': 3}, None, '0'), '0,000')
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')
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')
self.assertEqual(fieldFormatter.representValue(layer, 1, {'Precision': 2}, None, '-0.127'), '-0,13')
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')
QgsProject.instance().removeAllMapLayers()
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()