Skip to content

Commit

Permalink
new validator to handle permissive double writing in form; fix raster…
Browse files Browse the repository at this point in the history
… properties form

QgsDoubleValidator modification after review

update doc
  • Loading branch information
speillet committed Jun 14, 2020
1 parent da21127 commit 893bfff
Show file tree
Hide file tree
Showing 16 changed files with 686 additions and 59 deletions.
142 changes: 142 additions & 0 deletions python/gui/auto_generated/qgsdoublevalidator.sip.in
@@ -0,0 +1,142 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/qgsdoublevalidator.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/




class QgsDoubleValidator : QRegularExpressionValidator
{
%Docstring

QgsDoubleValidator is a QLineEdit Validator that combines QDoubleValidator
and QRegularExpressionValidator to allow user to enter double with both
local and C interpretation.

.. versionadded:: 3.14
%End

%TypeHeaderCode
#include "qgsdoublevalidator.h"
%End
public:

explicit QgsDoubleValidator( QObject *parent );
%Docstring
Constructor for QgsDoubleValidator.

.. versionadded:: 3.14
%End

QgsDoubleValidator( QRegularExpression reg, double bottom, double top, QObject *parent );
%Docstring
Constructor for QgsDoubleValidator.

.. versionadded:: 3.14
%End

QgsDoubleValidator( double bottom, double top, QObject *parent );
%Docstring
Constructor for QgsDoubleValidator.

.. versionadded:: 3.14
%End

QgsDoubleValidator( double bottom, double top, int dec, QObject *parent );
%Docstring
Constructor for QgsDoubleValidator.

.. versionadded:: 3.14
%End

virtual QValidator::State validate( QString &input, int & ) const;

%Docstring
Evaluates input QString validity according to QRegularExpression
and ability to be converted in double value.

.. versionadded:: 3.14
%End

QValidator::State validate( QString input ) const;
%Docstring
Evaluates input QString validity according to QRegularExpression
and ability to be converted in double value.

.. versionadded:: 3.14
%End


static double toDouble( QString input );
%Docstring
Converts QString to double value.
It used locale interpretation first
and C locale interpretation as fallback

.. versionadded:: 3.14
%End

void setBottom( double bottom );
%Docstring
Set top range limit

.. seealso:: :py:func:`setTop`

.. seealso:: :py:func:`setRange`

.. versionadded:: 3.14
%End

void setTop( double top );
%Docstring
Set top range limit

.. seealso:: :py:func:`setBottom`

.. seealso:: :py:func:`setRange`

.. versionadded:: 3.14
%End

virtual void setRange( double bottom, double top );
%Docstring
Set bottom and top range limits

.. seealso:: :py:func:`setBottom`

.. seealso:: :py:func:`setTop`

.. versionadded:: 3.14
%End

double bottom() const;
%Docstring
Returns top range limit

.. seealso:: :py:func:`setBottom`

.. versionadded:: 3.14
%End

double top() const;
%Docstring
Returns top range limit

.. seealso:: :py:func:`setTop`

.. versionadded:: 3.14
%End

};

/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/qgsdoublevalidator.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
1 change: 1 addition & 0 deletions python/gui/gui_auto.sip
Expand Up @@ -67,6 +67,7 @@
%Include auto_generated/qgsdial.sip
%Include auto_generated/qgsdialog.sip
%Include auto_generated/qgsdockwidget.sip
%Include auto_generated/qgsdoublevalidator.sip
%Include auto_generated/qgsencodingfiledialog.sip
%Include auto_generated/qgserrordialog.sip
%Include auto_generated/qgsexpressionbuilderdialog.sip
Expand Down
2 changes: 1 addition & 1 deletion src/core/raster/qgspalettedrasterrenderer.cpp
Expand Up @@ -499,7 +499,7 @@ QgsPalettedRasterRenderer::ClassData QgsPalettedRasterRenderer::classDataFromRas
int count = histogram.histogramVector.at( idx );
if ( count > 0 )
{
data << Class( currentValue, QColor(), QString::number( currentValue ) );
data << Class( currentValue, QColor(), QLocale().toString( currentValue ) );
presentValues++;
}
currentValue += interval;
Expand Down
2 changes: 2 additions & 0 deletions src/gui/CMakeLists.txt
Expand Up @@ -388,6 +388,7 @@ SET(QGIS_GUI_SRCS
qgsdetaileditemwidget.cpp
qgsdial.cpp
qgsdialog.cpp
qgsdoublevalidator.cpp
qgsdockwidget.cpp
qgsencodingfiledialog.cpp
qgserrordialog.cpp
Expand Down Expand Up @@ -619,6 +620,7 @@ SET(QGIS_GUI_HDRS
qgsdial.h
qgsdialog.h
qgsdockwidget.h
qgsdoublevalidator.h
qgsencodingfiledialog.h
qgserrordialog.h
qgsexpressionbuilderdialog.h
Expand Down
131 changes: 131 additions & 0 deletions src/gui/qgsdoublevalidator.cpp
@@ -0,0 +1,131 @@
/***************************************************************************
qgsdoublevalidator.cpp - description
-------------------
begin : June 2020
copyright : (C) 2020 by Sebastien Peillet
email : sebastien.peillet@oslandia.com
adapted version of Qgslonglongvalidator + QgsFieldValidator
***************************************************************************/

/***************************************************************************
* *
* 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 <limits>
#include <QRegExpValidator>
#include <QLocale>
#include "qgis_gui.h"

#include "qgsdoublevalidator.h"

const QString PERMISSIVE_DOUBLE = R"(-?[\d]{0,1000}([\.%1][\d]{0,1000})?(e[+-]?[\d]{0,%2})?)";

This comment has been minimized.

Copy link
@elpaso

elpaso Nov 4, 2020

Contributor

@SebastienPeillet

I don't understand how the validation is supposed to work with decimals,
say I want to use the validator for integers, I would set decimals = 0 but the regexp checks for the exponent so this would be valid and the test fails (assume en locale):

QCOMPARE( validator.toDouble( val ), 1.23 );
QCOMPARE( validator.validate( val ), QValidator::State::Invalid );

Can you please clarify?

This comment has been minimized.

Copy link
@SebastienPeillet

SebastienPeillet Nov 5, 2020

Contributor

It seems you are right, I guess I had some troubles with the thousands separator before removing it from the validator. But as it is the validator valids the value even if there is too much decimal.

This should work instead :

const QString PERMISSIVE_DOUBLE = R"(-?[\d]{0,1000}([\.%1][\d]{0,1000}e[+-]?[\d]{0,1000}|[\.%1][\d]{0,%2})?)";

or even :

const QString PERMISSIVE_DOUBLE = R"(-?[\d]{0,1000}([\.\%1][\d]{0,%2})?(e[+-]?[\d]{0,1000})?)";

if we choose to limit decimal in scientific notation, even if the value is a high number, but I'm not sure it's a good idea as it can round great number.

Notice that the value will return a Intermediate validation in validate function, and we can use it in toDouble to round value on the decimal

This comment has been minimized.

Copy link
@elpaso

elpaso Nov 6, 2020

Contributor

the second one should work, needs tests of course. Just be careful to always escape \ the decimal separator, just in case it's a ..

Can you please also add tests for negative numbers?

This comment has been minimized.

Copy link
@SebastienPeillet

SebastienPeillet Nov 6, 2020

Contributor

@elpaso I've done the change here. Do I create another PR or do you cherry pick the commit to your PR

This comment has been minimized.

Copy link
@elpaso

elpaso Nov 6, 2020

Contributor

Can you make a separate PR please?

I must admit that I have some problem (or not enough time) understanding your tests, I would have been much easier without that custom encoding.


QgsDoubleValidator::QgsDoubleValidator( QObject *parent )
: QRegularExpressionValidator( parent )
, b( std::numeric_limits<qreal>::min() )
, t( std::numeric_limits<qreal>::max() )
{
// The regular expression accept double with point as decimal point but also the locale decimal point
QRegularExpression reg( PERMISSIVE_DOUBLE.arg( locale().decimalPoint() ).arg( 1000 ) );
this->setRegularExpression( reg );
}

QgsDoubleValidator::QgsDoubleValidator( QRegularExpression reg, double bottom, double top, QObject *parent )
: QRegularExpressionValidator( parent )
, b( bottom )
, t( top )
{
this->setRegularExpression( reg );
}

QgsDoubleValidator::QgsDoubleValidator( double bottom, double top, QObject *parent )
: QRegularExpressionValidator( parent )
, b( bottom )
, t( top )
{
// The regular expression accept double with point as decimal point but also the locale decimal point
QRegularExpression reg( PERMISSIVE_DOUBLE.arg( locale().decimalPoint() ).arg( 1000 ) );
this->setRegularExpression( reg );
}

QgsDoubleValidator::QgsDoubleValidator( double bottom, double top, int dec, QObject *parent )
: QRegularExpressionValidator( parent )
, b( bottom )
, t( top )
{
// The regular expression accept double with point as decimal point but also the locale decimal point
QRegularExpression reg( PERMISSIVE_DOUBLE.arg( locale().decimalPoint() ).arg( QString::number( dec ) ) );
this->setRegularExpression( reg );
}

QValidator::State QgsDoubleValidator::validate( QString &input, int & ) const
{
if ( input.isEmpty() )
return Intermediate;


bool ok = false;
double entered = QgsDoubleValidator::toDouble( input, &ok );
if ( ! ok )
{
if ( regularExpression().match( input ).captured( 0 ) == input )
return Intermediate;
else
return Invalid;
}

if ( entered >= b && entered <= t && regularExpression().match( input ).captured( 0 ) == input )
return Acceptable;
else
return Intermediate;
}

QValidator::State QgsDoubleValidator::validate( QString input ) const
{
if ( input.isEmpty() )
return Intermediate;


bool ok = false;
double entered = QgsDoubleValidator::toDouble( input, &ok );
if ( ! ok )
{
if ( regularExpression().match( input ).captured( 0 ) == input )
return Intermediate;
else
return Invalid;
}

if ( entered >= b && entered <= t && regularExpression().match( input ).captured( 0 ) == input )
return Acceptable;
else
return Intermediate;
}

double QgsDoubleValidator::toDouble( QString input )
{
bool ok = false;
double value = QLocale().toDouble( input, &ok );
if ( ! ok )
{
value = QLocale( QLocale::C ).toDouble( input, &ok );
}
return value;
}

double QgsDoubleValidator::toDouble( QString input, bool *ok )
{
double value = QLocale().toDouble( input, ok );

if ( ! *ok )
{
value = QLocale( QLocale::C ).toDouble( input, ok );
}
return value ;
}

0 comments on commit 893bfff

Please sign in to comment.