Skip to content

Commit

Permalink
Merge pull request #6246 from elpaso/rangeformatter
Browse files Browse the repository at this point in the history
[bugfix] Range formatter for doubles and ints
  • Loading branch information
elpaso committed Feb 2, 2018
2 parents b7e19d8 + 31de324 commit 5c5ef3b
Show file tree
Hide file tree
Showing 14 changed files with 274 additions and 2 deletions.
1 change: 1 addition & 0 deletions python/core/core_auto.sip
Expand Up @@ -287,6 +287,7 @@
%Include geometry/qgswkbtypes.sip
%Include ./3d/qgs3drendererregistry.sip
%Include ./3d/qgsabstract3drenderer.sip
%Include fieldformatter/qgsrangefieldformatter.sip
%Include fieldformatter/qgsdatetimefieldformatter.sip
%Include fieldformatter/qgsfallbackfieldformatter.sip
%Include fieldformatter/qgskeyvaluefieldformatter.sip
Expand Down
42 changes: 42 additions & 0 deletions python/core/fieldformatter/qgsrangefieldformatter.sip.in
@@ -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 *
************************************************************************/
7 changes: 7 additions & 0 deletions python/gui/editorwidgets/qgsdoublespinbox.sip.in
Expand Up @@ -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.

.. seealso:: :py:func:`setClearValue`
%End

void setLineEditAlignment( Qt::Alignment alignment );
%Docstring
Set alignment in the embedded line edit widget

:param alignment:
%End

virtual double valueFromText( const QString &text ) const;
Expand Down
7 changes: 7 additions & 0 deletions python/gui/editorwidgets/qgsspinbox.sip.in
Expand Up @@ -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.

.. seealso:: :py:func:`setClearValue`
%End

void setLineEditAlignment( Qt::Alignment alignment );
%Docstring
Set alignment in the embedded line edit widget

:param alignment:
%End

virtual int valueFromText( const QString &text ) const;
Expand Down
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -473,6 +473,7 @@ SET(QGIS_CORE_SRCS
3d/qgs3drendererregistry.cpp
3d/qgsabstract3drenderer.cpp

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

fieldformatter/qgsrangefieldformatter.h
fieldformatter/qgsdatetimefieldformatter.h
fieldformatter/qgsfallbackfieldformatter.h
fieldformatter/qgskeyvaluefieldformatter.h
Expand Down
86 changes: 86 additions & 0 deletions src/core/fieldformatter/qgsrangefieldformatter.cpp
@@ -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;
}
43 changes: 43 additions & 0 deletions src/core/fieldformatter/qgsrangefieldformatter.h
@@ -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
2 changes: 2 additions & 0 deletions src/core/qgsfieldformatterregistry.cpp
Expand Up @@ -22,6 +22,7 @@
#include "qgsrelationreferencefieldformatter.h"
#include "qgskeyvaluefieldformatter.h"
#include "qgslistfieldformatter.h"
#include "qgsrangefieldformatter.h"
#include "qgsfallbackfieldformatter.h"


Expand All @@ -34,6 +35,7 @@ QgsFieldFormatterRegistry::QgsFieldFormatterRegistry( QObject *parent )
addFieldFormatter( new QgsKeyValueFieldFormatter() );
addFieldFormatter( new QgsListFieldFormatter() );
addFieldFormatter( new QgsDateTimeFieldFormatter() );
addFieldFormatter( new QgsRangeFieldFormatter() );

mFallbackFieldFormatter = new QgsFallbackFieldFormatter();
}
Expand Down
7 changes: 7 additions & 0 deletions src/gui/editorwidgets/qgsdoublespinbox.cpp
Expand Up @@ -31,6 +31,8 @@ QgsDoubleSpinBox::QgsDoubleSpinBox( QWidget *parent )
{
mLineEdit = new QgsSpinBoxLineEdit();

// By default, group separator is off
setLocale( QLocale( QgsApplication::locale( ) ) );
setLineEdit( mLineEdit );

QSize msz = minimumSizeHint();
Expand Down Expand Up @@ -134,6 +136,11 @@ double QgsDoubleSpinBox::clearValue() const
return mCustomClearValue;
}

void QgsDoubleSpinBox::setLineEditAlignment( Qt::Alignment alignment )
{
mLineEdit->setAlignment( alignment );
}

QString QgsDoubleSpinBox::stripped( const QString &originalText ) const
{
//adapted from QAbstractSpinBoxPrivate::stripped
Expand Down
6 changes: 6 additions & 0 deletions src/gui/editorwidgets/qgsdoublespinbox.h
Expand Up @@ -126,6 +126,12 @@ class GUI_EXPORT QgsDoubleSpinBox : public QDoubleSpinBox
*/
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;
QValidator::State validate( QString &input, int &pos ) const override;
void paintEvent( QPaintEvent *e ) override;
Expand Down
2 changes: 2 additions & 0 deletions src/gui/editorwidgets/qgsrangewidgetwrapper.cpp
Expand Up @@ -47,12 +47,14 @@ QWidget *QgsRangeWidgetWrapper::createWidget( QWidget *parent )
case QVariant::Double:
{
editor = new QgsDoubleSpinBox( parent );
static_cast<QgsDoubleSpinBox *>( editor )->setLineEditAlignment( Qt::AlignRight );
break;
}
case QVariant::Int:
case QVariant::LongLong:
default:
editor = new QgsSpinBox( parent );
static_cast<QgsSpinBox *>( editor )->setLineEditAlignment( Qt::AlignRight );
break;
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/gui/editorwidgets/qgsspinbox.cpp
Expand Up @@ -30,7 +30,6 @@ QgsSpinBox::QgsSpinBox( QWidget *parent )
: QSpinBox( parent )
{
mLineEdit = new QgsSpinBoxLineEdit();

setLineEdit( mLineEdit );

QSize msz = minimumSizeHint();
Expand Down Expand Up @@ -133,6 +132,11 @@ int QgsSpinBox::clearValue() const
return mCustomClearValue;
}

void QgsSpinBox::setLineEditAlignment( Qt::Alignment alignment )
{
mLineEdit->setAlignment( alignment );
}

int QgsSpinBox::valueFromText( const QString &text ) const
{
if ( !mExpressionsEnabled )
Expand Down
6 changes: 6 additions & 0 deletions src/gui/editorwidgets/qgsspinbox.h
Expand Up @@ -126,6 +126,12 @@ class GUI_EXPORT QgsSpinBox : public QSpinBox
*/
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;
QValidator::State validate( QString &input, int &pos ) const override;

Expand Down
59 changes: 58 additions & 1 deletion tests/src/python/test_qgsfieldformatters.py
Expand Up @@ -16,7 +16,7 @@

from qgis.core import (QgsFeature, QgsProject, QgsRelation, QgsVectorLayer,
QgsValueMapFieldFormatter, QgsValueRelationFieldFormatter,
QgsRelationReferenceFieldFormatter, QgsSettings)
QgsRelationReferenceFieldFormatter, QgsRangeFieldFormatter, QgsSettings)

from qgis.testing import start_app, unittest

Expand Down Expand Up @@ -197,5 +197,62 @@ def test_representValue(self):
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__':
unittest.main()

0 comments on commit 5c5ef3b

Please sign in to comment.