155 changes: 155 additions & 0 deletions src/gui/qgsdoublevalidator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#ifndef QGSDOUBLEVALIDATOR_H
#define QGSDOUBLEVALIDATOR_H

/***************************************************************************
qgsdoublevalidator.h - 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 "qgis_sip.h"

/**
* \ingroup gui
*
* QgsDoubleValidator is a QLineEdit Validator that combines QDoubleValidator
* and QRegularExpressionValidator to allow user to enter double with both
* local and C interpretation.
*
* \since QGIS 3.14
*/
class GUI_EXPORT QgsDoubleValidator : public QRegularExpressionValidator
{
Q_OBJECT

public:

/**
* Constructor for QgsDoubleValidator.
* \since QGIS 3.14
*/
explicit QgsDoubleValidator( QObject *parent );

/**
* Constructor for QgsDoubleValidator.
* \since QGIS 3.14
*/
QgsDoubleValidator( QRegularExpression reg, double bottom, double top, QObject *parent );

/**
* Constructor for QgsDoubleValidator.
* \since QGIS 3.14
*/
QgsDoubleValidator( double bottom, double top, QObject *parent );

/**
* Constructor for QgsDoubleValidator.
* \since QGIS 3.14
*/
QgsDoubleValidator( double bottom, double top, int dec, QObject *parent );

/**
* Evaluates input QString validity according to QRegularExpression
* and ability to be converted in double value.
* \since QGIS 3.14
*/
QValidator::State validate( QString &input, int & ) const override;

/**
* Evaluates input QString validity according to QRegularExpression
* and ability to be converted in double value.
* \since QGIS 3.14
*/
QValidator::State validate( QString input ) const;

/**
* Converts QString to double value.
* It used locale interpretation first
* and C locale interpretation as fallback
* \since QGIS 3.14
*/
static double toDouble( QString input, bool *ok ) SIP_SKIP;

/**
* Converts QString to double value.
* It used locale interpretation first
* and C locale interpretation as fallback
* \since QGIS 3.14
*/
static double toDouble( QString input );

/**
* Set top range limit
* \see setTop
* \see setRange
* \since QGIS 3.14
*/
void setBottom( double bottom ) { b = bottom; }

/**
* Set top range limit
* \see setBottom
* \see setRange
* \since QGIS 3.14
*/
void setTop( double top ) { t = top; }

/**
* Set bottom and top range limits
* \see setBottom
* \see setTop
* \since QGIS 3.14
*/
virtual void setRange( double bottom, double top )
{
b = bottom;
t = top;
}

/**
* Returns top range limit
* \see setBottom
* \since QGIS 3.14
*/
double bottom() const { return b; }

/**
* Returns top range limit
* \see setTop
* \since QGIS 3.14
*/
double top() const { return t; }

private:
// Disables copy constructing
Q_DISABLE_COPY( QgsDoubleValidator )

/**
* Bottom range limit
*/
double b;

/**
* Top range limit
*/
double t;
};

#endif // QGSDOUBLEVALIDATOR_H
18 changes: 9 additions & 9 deletions src/gui/raster/qgscolorrampshaderwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ QgsColorRampShader QgsColorRampShaderWidget::shader() const
continue;
}
QgsColorRampShader::ColorRampItem newColorRampItem;
newColorRampItem.value = currentItem->text( ValueColumn ).toDouble();
newColorRampItem.value = QLocale().toDouble( currentItem->text( ValueColumn ) );
newColorRampItem.color = currentItem->data( ColorColumn, Qt::EditRole ).value<QColor>();
newColorRampItem.label = currentItem->text( LabelColumn );
colorRampItems.append( newColorRampItem );
Expand Down Expand Up @@ -181,7 +181,7 @@ void QgsColorRampShaderWidget::autoLabel()
{
label = "<= " + currentItem->text( ValueColumn ) + unit;
}
else if ( currentItem->text( ValueColumn ).toDouble() == std::numeric_limits<double>::infinity() )
else if ( QLocale().toDouble( currentItem->text( ValueColumn ) ) == std::numeric_limits<double>::infinity() )
{
label = "> " + mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn ) + unit;
}
Expand Down Expand Up @@ -330,7 +330,7 @@ void QgsColorRampShaderWidget::classify()
for ( ; it != colorRampItemList.end(); ++it )
{
QgsTreeWidgetItemObject *newItem = new QgsTreeWidgetItemObject( mColormapTreeWidget );
newItem->setText( ValueColumn, QString::number( it->value, 'g', 15 ) );
newItem->setText( ValueColumn, QLocale().toString( it->value, 'g', 15 ) );
newItem->setData( ColorColumn, Qt::EditRole, it->color );
newItem->setText( LabelColumn, QString() ); // Labels will be populated in autoLabel()
newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
Expand Down Expand Up @@ -386,7 +386,7 @@ void QgsColorRampShaderWidget::applyColorRamp()
continue;
}

double value = currentItem->text( ValueColumn ).toDouble();
double value = QLocale().toDouble( currentItem->text( ValueColumn ) );
double position = ( value - mMin ) / ( mMax - mMin );
whileBlocking( static_cast<QgsTreeWidgetItemObject *>( currentItem ) )->setData( ColorColumn, Qt::EditRole, ramp->color( position ) );
}
Expand All @@ -406,7 +406,7 @@ void QgsColorRampShaderWidget::populateColormapTreeWidget( const QList<QgsColorR
for ( ; it != colorRampItems.constEnd(); ++it )
{
QgsTreeWidgetItemObject *newItem = new QgsTreeWidgetItemObject( mColormapTreeWidget );
newItem->setText( ValueColumn, QString::number( it->value, 'g', 15 ) );
newItem->setText( ValueColumn, QLocale().toString( it->value, 'g', 15 ) );
newItem->setData( ColorColumn, Qt::EditRole, it->color );
newItem->setText( LabelColumn, it->label );
newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
Expand Down Expand Up @@ -492,7 +492,7 @@ void QgsColorRampShaderWidget::mLoadFromFileButton_clicked()
inputStringComponents = inputLine.split( ',' );
if ( inputStringComponents.size() == 6 )
{
QgsColorRampShader::ColorRampItem currentItem( inputStringComponents[0].toDouble(),
QgsColorRampShader::ColorRampItem currentItem( QLocale().toDouble( inputStringComponents[0] ),
QColor::fromRgb( inputStringComponents[1].toInt(), inputStringComponents[2].toInt(),
inputStringComponents[3].toInt(), inputStringComponents[4].toInt() ),
inputStringComponents[5] );
Expand Down Expand Up @@ -570,7 +570,7 @@ void QgsColorRampShaderWidget::mExportToFileButton_clicked()
continue;
}
color = currentItem->data( ColorColumn, Qt::EditRole ).value<QColor>();
outputStream << currentItem->text( ValueColumn ).toDouble() << ',';
outputStream << QLocale().toDouble( currentItem->text( ValueColumn ) ) << ',';
outputStream << color.red() << ',' << color.green() << ',' << color.blue() << ',' << color.alpha() << ',';
if ( currentItem->text( LabelColumn ).isEmpty() )
{
Expand Down Expand Up @@ -737,9 +737,9 @@ void QgsColorRampShaderWidget::loadMinimumMaximumFromTree()
return;
}

double min = item->text( ValueColumn ).toDouble();
double min = QLocale().toDouble( item->text( ValueColumn ) );
item = mColormapTreeWidget->topLevelItem( mColormapTreeWidget->topLevelItemCount() - 1 );
double max = item->text( ValueColumn ).toDouble();
double max = QLocale().toDouble( item->text( ValueColumn ) );

if ( !qgsDoubleNear( mMin, min ) || !qgsDoubleNear( mMax, max ) )
{
Expand Down
33 changes: 17 additions & 16 deletions src/gui/raster/qgsmultibandcolorrendererwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "qgsrasterlayer.h"
#include "qgsrasterdataprovider.h"
#include "qgsrasterminmaxwidget.h"
#include "qgsdoublevalidator.h"

QgsMultiBandColorRendererWidget::QgsMultiBandColorRendererWidget( QgsRasterLayer *layer, const QgsRectangle &extent )
: QgsRasterRendererWidget( layer, extent )
Expand Down Expand Up @@ -119,12 +120,12 @@ void QgsMultiBandColorRendererWidget::setMapCanvas( QgsMapCanvas *canvas )

void QgsMultiBandColorRendererWidget::createValidators()
{
mRedMinLineEdit->setValidator( new QDoubleValidator( mRedMinLineEdit ) );
mRedMaxLineEdit->setValidator( new QDoubleValidator( mRedMinLineEdit ) );
mGreenMinLineEdit->setValidator( new QDoubleValidator( mGreenMinLineEdit ) );
mGreenMaxLineEdit->setValidator( new QDoubleValidator( mGreenMinLineEdit ) );
mBlueMinLineEdit->setValidator( new QDoubleValidator( mBlueMinLineEdit ) );
mBlueMaxLineEdit->setValidator( new QDoubleValidator( mBlueMinLineEdit ) );
mRedMinLineEdit->setValidator( new QgsDoubleValidator( mRedMinLineEdit ) );
mRedMaxLineEdit->setValidator( new QgsDoubleValidator( mRedMinLineEdit ) );
mGreenMinLineEdit->setValidator( new QgsDoubleValidator( mGreenMinLineEdit ) );
mGreenMaxLineEdit->setValidator( new QgsDoubleValidator( mGreenMinLineEdit ) );
mBlueMinLineEdit->setValidator( new QgsDoubleValidator( mBlueMinLineEdit ) );
mBlueMaxLineEdit->setValidator( new QgsDoubleValidator( mBlueMinLineEdit ) );
}

void QgsMultiBandColorRendererWidget::setCustomMinMaxValues( QgsMultiBandColorRenderer *r,
Expand All @@ -150,8 +151,8 @@ void QgsMultiBandColorRendererWidget::setCustomMinMaxValues( QgsMultiBandColorRe
QgsContrastEnhancement *blueEnhancement = nullptr;

bool redMinOk, redMaxOk;
double redMin = mRedMinLineEdit->text().toDouble( &redMinOk );
double redMax = mRedMaxLineEdit->text().toDouble( &redMaxOk );
double redMin = QgsDoubleValidator::toDouble( mRedMinLineEdit->text(), &redMinOk );
double redMax = QgsDoubleValidator::toDouble( mRedMaxLineEdit->text(), &redMaxOk );
if ( redMinOk && redMaxOk && redBand != -1 )
{
redEnhancement = new QgsContrastEnhancement( ( Qgis::DataType )(
Expand All @@ -161,8 +162,8 @@ void QgsMultiBandColorRendererWidget::setCustomMinMaxValues( QgsMultiBandColorRe
}

bool greenMinOk, greenMaxOk;
double greenMin = mGreenMinLineEdit->text().toDouble( &greenMinOk );
double greenMax = mGreenMaxLineEdit->text().toDouble( &greenMaxOk );
double greenMin = QgsDoubleValidator::toDouble( mGreenMinLineEdit->text(), &greenMinOk );
double greenMax = QgsDoubleValidator::toDouble( mGreenMaxLineEdit->text(), &greenMaxOk );
if ( greenMinOk && greenMaxOk && greenBand != -1 )
{
greenEnhancement = new QgsContrastEnhancement( ( Qgis::DataType )(
Expand All @@ -172,8 +173,8 @@ void QgsMultiBandColorRendererWidget::setCustomMinMaxValues( QgsMultiBandColorRe
}

bool blueMinOk, blueMaxOk;
double blueMin = mBlueMinLineEdit->text().toDouble( &blueMinOk );
double blueMax = mBlueMaxLineEdit->text().toDouble( &blueMaxOk );
double blueMin = QgsDoubleValidator::toDouble( mBlueMinLineEdit->text(), &blueMinOk );
double blueMax = QgsDoubleValidator::toDouble( mBlueMaxLineEdit->text(), &blueMaxOk );
if ( blueMinOk && blueMaxOk && blueBand != -1 )
{
blueEnhancement = new QgsContrastEnhancement( ( Qgis::DataType )(
Expand Down Expand Up @@ -292,7 +293,7 @@ void QgsMultiBandColorRendererWidget::loadMinMax( int bandNo, double min, double
}
else
{
myMinLineEdit->setText( QString::number( min ) );
myMinLineEdit->setText( QLocale().toString( min ) );
}

if ( std::isnan( max ) )
Expand All @@ -301,7 +302,7 @@ void QgsMultiBandColorRendererWidget::loadMinMax( int bandNo, double min, double
}
else
{
myMaxLineEdit->setText( QString::number( max ) );
myMaxLineEdit->setText( QLocale().toString( max ) );
}
mDisableMinMaxWidgetRefresh = false;
}
Expand All @@ -320,8 +321,8 @@ void QgsMultiBandColorRendererWidget::setMinMaxValue( const QgsContrastEnhanceme
return;
}

minEdit->setText( QString::number( ce->minimumValue() ) );
maxEdit->setText( QString::number( ce->maximumValue() ) );
minEdit->setText( QLocale().toString( ce->minimumValue() ) );
maxEdit->setText( QLocale().toString( ce->maximumValue() ) );

// QgsMultiBandColorRenderer is using individual contrast enhancements for each
// band, but this widget GUI has one for all
Expand Down
2 changes: 1 addition & 1 deletion src/gui/raster/qgspalettedrendererwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -727,7 +727,7 @@ bool QgsPalettedRendererModel::insertRows( int row, int count, const QModelIndex
beginInsertRows( QModelIndex(), row, row + count - 1 );
for ( int i = row; i < row + count; ++i, ++nextValue )
{
mData.insert( i, QgsPalettedRasterRenderer::Class( nextValue, QColor( 200, 200, 200 ), QString::number( nextValue ) ) );
mData.insert( i, QgsPalettedRasterRenderer::Class( nextValue, QColor( 200, 200, 200 ), QLocale().toString( nextValue ) ) );
}
endInsertRows();
emit classesChanged();
Expand Down
5 changes: 3 additions & 2 deletions src/gui/raster/qgsrasterhistogramwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "qgsrasterhistogramwidget.h"
#include "qgsrasterminmaxwidget.h"
#include "qgsrasterdataprovider.h"
#include "qgsdoublevalidator.h"
#include "qgssettings.h"

#include <QMenu>
Expand Down Expand Up @@ -104,8 +105,8 @@ QgsRasterHistogramWidget::QgsRasterHistogramWidget( QgsRasterLayer *lyr, QWidget
}

// histo min/max selectors
leHistoMin->setValidator( new QDoubleValidator( this ) );
leHistoMax->setValidator( new QDoubleValidator( this ) );
leHistoMin->setValidator( new QgsDoubleValidator( this ) );
leHistoMax->setValidator( new QgsDoubleValidator( this ) );
// this might generate many refresh events! test..
// connect( leHistoMin, SIGNAL( textChanged( const QString & ) ), this, SLOT( updateHistoMarkers() ) );
// connect( leHistoMax, SIGNAL( textChanged( const QString & ) ), this, SLOT( updateHistoMarkers() ) );
Expand Down
18 changes: 11 additions & 7 deletions src/gui/raster/qgsrasterlayerproperties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
#include "qgsprovidermetadata.h"
#include "qgsproviderregistry.h"
#include "qgsrasterlayertemporalproperties.h"
#include "qgsdoublevalidator.h"

#include "qgsrasterlayertemporalpropertieswidget.h"
#include "qgsprojecttimesettings.h"
Expand Down Expand Up @@ -187,7 +188,7 @@ QgsRasterLayerProperties::QgsRasterLayerProperties( QgsMapLayer *lyr, QgsMapCanv
chkUseScaleDependentRendering->setChecked( lyr->hasScaleBasedVisibility() );
mScaleRangeWidget->setScaleRange( lyr->minimumScale(), lyr->maximumScale() );

leNoDataValue->setValidator( new QDoubleValidator( -std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), 1000, this ) );
leNoDataValue->setValidator( new QgsDoubleValidator( -std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), 1000, this ) );

// build GUI components
QIcon myPyramidPixmap( QgsApplication::getThemeIcon( "/mIconPyramid.svg" ) );
Expand Down Expand Up @@ -820,7 +821,8 @@ void QgsRasterLayerProperties::sync()
// TODO: no data ranges
if ( provider->sourceHasNoDataValue( 1 ) )
{
lblSrcNoDataValue->setText( QgsRasterBlock::printValue( provider->sourceNoDataValue( 1 ) ) );
double v = QgsRasterBlock::printValue( provider->sourceNoDataValue( 1 ) ).toDouble();
lblSrcNoDataValue->setText( QLocale().toString( v, 'g' ) );
}
else
{
Expand Down Expand Up @@ -976,7 +978,7 @@ void QgsRasterLayerProperties::apply()
if ( "" != leNoDataValue->text() )
{
bool myDoubleOk = false;
double myNoDataValue = leNoDataValue->text().toDouble( &myDoubleOk );
double myNoDataValue = QgsDoubleValidator::toDouble( leNoDataValue->text(), &myDoubleOk );
if ( myDoubleOk )
{
QgsRasterRange myNoDataRange( myNoDataValue, myNoDataValue );
Expand Down Expand Up @@ -1628,17 +1630,18 @@ void QgsRasterLayerProperties::setTransparencyCell( int row, int column, double
{
case Qgis::Float32:
case Qgis::Float64:
lineEdit->setValidator( new QDoubleValidator( nullptr ) );
lineEdit->setValidator( new QgsDoubleValidator( nullptr ) );
if ( !std::isnan( value ) )
{
valueString = QgsRasterBlock::printValue( value );
double v = QgsRasterBlock::printValue( value ).toDouble();
valueString = QLocale().toString( v, 'g' ) ;
}
break;
default:
lineEdit->setValidator( new QIntValidator( nullptr ) );
if ( !std::isnan( value ) )
{
valueString = QString::number( static_cast<int>( value ) );
valueString = QLocale().toString( static_cast<int>( value ) );
}
break;
}
Expand All @@ -1658,7 +1661,8 @@ void QgsRasterLayerProperties::setTransparencyCellValue( int row, int column, do
{
QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, column ) );
if ( !lineEdit ) return;
lineEdit->setText( QgsRasterBlock::printValue( value ) );
double v = QgsRasterBlock::printValue( value ).toDouble();
lineEdit->setText( QLocale().toString( v, 'g' ) );
lineEdit->adjustSize();
adjustTransparencyCellWidth( row, column );
tableTransparency->resizeColumnsToContents();
Expand Down
14 changes: 8 additions & 6 deletions src/gui/raster/qgsrastertransparencywidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
* *
***************************************************************************/
#include <QWidget>
#include <QDoubleValidator>
#include <QIntValidator>
#include <QFile>
#include <QTextStream>
Expand All @@ -33,6 +32,7 @@
#include "qgsmapcanvas.h"
#include "qgsrasteridentifyresult.h"
#include "qgsmultibandcolorrenderer.h"
#include "qgsdoublevalidator.h"


QgsRasterTransparencyWidget::QgsRasterTransparencyWidget( QgsRasterLayer *layer, QgsMapCanvas *canvas, QWidget *parent )
Expand Down Expand Up @@ -120,7 +120,8 @@ void QgsRasterTransparencyWidget::syncToLayer()
QgsDebugMsg( QStringLiteral( "noDataRangeList.size = %1" ).arg( noDataRangeList.size() ) );
if ( !noDataRangeList.isEmpty() )
{
leNoDataValue->setText( QgsRasterBlock::printValue( noDataRangeList.value( 0 ).min() ) );
double v = QgsRasterBlock::printValue( noDataRangeList.value( 0 ).min() ).toDouble();
leNoDataValue->setText( QLocale().toString( v ) );
}
else
{
Expand Down Expand Up @@ -395,7 +396,7 @@ void QgsRasterTransparencyWidget::apply()
if ( "" != leNoDataValue->text() )
{
bool myDoubleOk = false;
double myNoDataValue = leNoDataValue->text().toDouble( &myDoubleOk );
double myNoDataValue = QgsDoubleValidator::toDouble( leNoDataValue->text(), &myDoubleOk );
if ( myDoubleOk )
{
QgsRasterRange myNoDataRange( myNoDataValue, myNoDataValue );
Expand Down Expand Up @@ -635,10 +636,11 @@ void QgsRasterTransparencyWidget::setTransparencyCell( int row, int column, doub
{
case Qgis::Float32:
case Qgis::Float64:
lineEdit->setValidator( new QDoubleValidator( nullptr ) );
lineEdit->setValidator( new QgsDoubleValidator( nullptr ) );
if ( !std::isnan( value ) )
{
valueString = QgsRasterBlock::printValue( value );
double v = QgsRasterBlock::printValue( value ).toDouble();
valueString = QLocale().toString( v );
}
break;
default:
Expand Down Expand Up @@ -690,6 +692,6 @@ double QgsRasterTransparencyWidget::transparencyCellValue( int row, int column )
{
std::numeric_limits<double>::quiet_NaN();
}
return lineEdit->text().toDouble();
return QgsDoubleValidator::toDouble( lineEdit->text() );

}
17 changes: 9 additions & 8 deletions src/gui/raster/qgssinglebandgrayrendererwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "qgsrasterlayer.h"
#include "qgsrasterdataprovider.h"
#include "qgsrasterminmaxwidget.h"
#include "qgsdoublevalidator.h"

QgsSingleBandGrayRendererWidget::QgsSingleBandGrayRendererWidget( QgsRasterLayer *layer, const QgsRectangle &extent )
: QgsRasterRendererWidget( layer, extent )
Expand All @@ -32,8 +33,8 @@ QgsSingleBandGrayRendererWidget::QgsSingleBandGrayRendererWidget( QgsRasterLayer
mGradientComboBox->insertItem( 0, tr( "Black to white" ), QgsSingleBandGrayRenderer::BlackToWhite );
mGradientComboBox->insertItem( 1, tr( "White to black" ), QgsSingleBandGrayRenderer::WhiteToBlack );

mMinLineEdit->setValidator( new QDoubleValidator( mMinLineEdit ) );
mMaxLineEdit->setValidator( new QDoubleValidator( mMaxLineEdit ) );
mMinLineEdit->setValidator( new QgsDoubleValidator( mMinLineEdit ) );
mMaxLineEdit->setValidator( new QgsDoubleValidator( mMaxLineEdit ) );

if ( mRasterLayer )
{
Expand Down Expand Up @@ -89,8 +90,8 @@ QgsRasterRenderer *QgsSingleBandGrayRendererWidget::renderer()

QgsContrastEnhancement *e = new QgsContrastEnhancement( ( Qgis::DataType )(
provider->dataType( band ) ) );
e->setMinimumValue( mMinLineEdit->text().toDouble() );
e->setMaximumValue( mMaxLineEdit->text().toDouble() );
e->setMinimumValue( QgsDoubleValidator::toDouble( mMinLineEdit->text() ) );
e->setMaximumValue( QgsDoubleValidator::toDouble( mMaxLineEdit->text() ) );
e->setContrastEnhancementAlgorithm( ( QgsContrastEnhancement::ContrastEnhancementAlgorithm )( mContrastEnhancementComboBox->currentData().toInt() ) );

QgsSingleBandGrayRenderer *renderer = new QgsSingleBandGrayRenderer( provider, band );
Expand Down Expand Up @@ -151,7 +152,7 @@ void QgsSingleBandGrayRendererWidget::loadMinMax( int bandNo, double min, double
}
else
{
mMinLineEdit->setText( QString::number( min ) );
mMinLineEdit->setText( QLocale().toString( min ) );
}

if ( std::isnan( max ) )
Expand All @@ -160,7 +161,7 @@ void QgsSingleBandGrayRendererWidget::loadMinMax( int bandNo, double min, double
}
else
{
mMaxLineEdit->setText( QString::number( max ) );
mMaxLineEdit->setText( QLocale().toString( max ) );
}
mDisableMinMaxWidgetRefresh = false;
}
Expand Down Expand Up @@ -188,8 +189,8 @@ void QgsSingleBandGrayRendererWidget::setFromRenderer( const QgsRasterRenderer *
{
//minmax
mDisableMinMaxWidgetRefresh = true;
mMinLineEdit->setText( QString::number( ce->minimumValue() ) );
mMaxLineEdit->setText( QString::number( ce->maximumValue() ) );
mMinLineEdit->setText( QLocale().toString( ce->minimumValue() ) );
mMaxLineEdit->setText( QLocale().toString( ce->maximumValue() ) );
mDisableMinMaxWidgetRefresh = false;
//contrast enhancement algorithm
mContrastEnhancementComboBox->setCurrentIndex(
Expand Down
19 changes: 10 additions & 9 deletions src/gui/raster/qgssinglebandpseudocolorrendererwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "qgsrasterdataprovider.h"
#include "qgsrastershader.h"
#include "qgsrasterminmaxwidget.h"
#include "qgsdoublevalidator.h"
#include "qgstreewidgetitem.h"
#include "qgssettings.h"
#include "qgsmapcanvas.h"
Expand Down Expand Up @@ -67,8 +68,8 @@ QgsSingleBandPseudoColorRendererWidget::QgsSingleBandPseudoColorRendererWidget(
}

// Must be before adding items to mBandComboBox (signal)
mMinLineEdit->setValidator( new QDoubleValidator( mMinLineEdit ) );
mMaxLineEdit->setValidator( new QDoubleValidator( mMaxLineEdit ) );
mMinLineEdit->setValidator( new QgsDoubleValidator( mMinLineEdit ) );
mMaxLineEdit->setValidator( new QgsDoubleValidator( mMaxLineEdit ) );

mMinMaxWidget = new QgsRasterMinMaxWidget( layer, this );
mMinMaxWidget->setExtent( extent );
Expand Down Expand Up @@ -192,7 +193,7 @@ void QgsSingleBandPseudoColorRendererWidget::loadMinMax( int bandNo, double min,
}
else
{
whileBlocking( mMinLineEdit )->setText( QString::number( min ) );
whileBlocking( mMinLineEdit )->setText( QLocale().toString( min ) );
}

if ( std::isnan( max ) )
Expand All @@ -201,13 +202,13 @@ void QgsSingleBandPseudoColorRendererWidget::loadMinMax( int bandNo, double min,
}
else
{
whileBlocking( mMaxLineEdit )->setText( QString::number( max ) );
whileBlocking( mMaxLineEdit )->setText( QLocale().toString( max ) );
}

// We compare old min and new min as text because QString::number keeps a fixed number of significant
// digits (default 6) and so loaded min/max will always differ from current one, which triggers a
// classification, and wipe out every user modification (see https://github.com/qgis/QGIS/issues/36172)
if ( mMinLineEdit->text() != QString::number( min ) || mMaxLineEdit->text() != QString::number( max ) )
if ( mMinLineEdit->text() != QLocale().toString( min ) || mMaxLineEdit->text() != QLocale().toString( max ) )
{
whileBlocking( mColorRampShaderWidget )->setRasterBand( bandNo );
whileBlocking( mColorRampShaderWidget )->setMinimumMaximumAndClassify( min, max );
Expand All @@ -217,8 +218,8 @@ void QgsSingleBandPseudoColorRendererWidget::loadMinMax( int bandNo, double min,

void QgsSingleBandPseudoColorRendererWidget::loadMinMaxFromTree( double min, double max )
{
whileBlocking( mMinLineEdit )->setText( QString::number( min ) );
whileBlocking( mMaxLineEdit )->setText( QString::number( max ) );
whileBlocking( mMinLineEdit )->setText( QLocale().toString( min ) );
whileBlocking( mMaxLineEdit )->setText( QLocale().toString( max ) );
minMaxModified();
}

Expand All @@ -228,7 +229,7 @@ void QgsSingleBandPseudoColorRendererWidget::setLineEditValue( QLineEdit *lineEd
QString s;
if ( !std::isnan( value ) )
{
s = QString::number( value );
s = QLocale().toString( value );
}
lineEdit->setText( s );
}
Expand All @@ -240,7 +241,7 @@ double QgsSingleBandPseudoColorRendererWidget::lineEditValue( const QLineEdit *l
return std::numeric_limits<double>::quiet_NaN();
}

return lineEdit->text().toDouble();
return QgsDoubleValidator::toDouble( lineEdit->text() );
}

void QgsSingleBandPseudoColorRendererWidget::mMinLineEdit_textEdited( const QString & )
Expand Down
1 change: 1 addition & 0 deletions tests/src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,4 @@ ADD_QGIS_TEST(tableeditorwidget testqgstableeditor.cpp)
ADD_QGIS_TEST(newdatabasetablewidget testqgsnewdatabasetablewidget.cpp)
ADD_QGIS_TEST(ogrproviderguitest testqgsogrprovidergui.cpp)
ADD_QGIS_TEST(singlebandpseudocolorrendererwidget testqgssinglebandpseudocolorrendererwidget.cpp)
ADD_QGIS_TEST(doublevalidator testqgsdoublevalidator.cpp)
185 changes: 185 additions & 0 deletions tests/src/gui/testqgsdoublevalidator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/***************************************************************************
testqgsdoublespinbox.cpp
--------------------------------------
Date : June 2020
Copyright : (C) 2020 Sebastien Peillet
Email : sebastien dot peillet at oslandia dot com
***************************************************************************
* *
* 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 "qgstest.h"

#include "qgsdoublevalidator.h"
#include <QLineEdit>

class TestQgsDoubleValidator: public QObject
{
Q_OBJECT
private slots:
void initTestCase(); // will be called before the first testfunction is executed.
void cleanupTestCase(); // will be called after the last testfunction was executed.
void init(); // will be called before each testfunction is executed.
void cleanup(); // will be called after every testfunction.

void validate();
void validate_data();
void toDouble_data();
void toDouble();

private:

};

void TestQgsDoubleValidator::initTestCase()
{

}

void TestQgsDoubleValidator::cleanupTestCase()
{
}

void TestQgsDoubleValidator::init()
{
}

void TestQgsDoubleValidator::cleanup()
{
}

void TestQgsDoubleValidator::validate_data()
{
QTest::addColumn<QString>( "actualState" );
QTest::addColumn<int>( "expState" );

QTest::newRow( "C decimal" ) << QString( "4cd6" ) << int( QValidator::Acceptable );
QTest::newRow( "locale decimal" ) << QString( "4ld6" ) << int( QValidator::Acceptable );
QTest::newRow( "locale decimal" ) << QString( "4444ld6" ) << int( QValidator::Acceptable );

// QgsDoubleValidator doesn't expect group separator but it tolerates it,
// so the result will be QValidator::Intermediate and not QValidator::Acceptable
QTest::newRow( "locale group separator + locale decimal" ) << QString( "4lg444ld6" ) << int( QValidator::Intermediate );
QTest::newRow( "locale group separator + c decimal" ) << QString( "4lg444cd6" ) << int( QValidator::Invalid );
QTest::newRow( "c group separator + locale decimal" ) << QString( "4cg444ld6" ) << int( QValidator::Invalid );
QTest::newRow( "c group separator + c decimal" ) << QString( "4cg444cd6" ) << int( QValidator::Intermediate );

QTest::newRow( "outside the range + local decimal" ) << QString( "3ld6" ) << int( QValidator::Intermediate );
QTest::newRow( "outside the range + c decimal" ) << QString( "3cd6" ) << int( QValidator::Intermediate );
QTest::newRow( "string" ) << QString( "string" ) << int( QValidator::Invalid );

}

void TestQgsDoubleValidator::toDouble_data()
{
QTest::addColumn<QString>( "actualValue" );
QTest::addColumn<double>( "expValue" );

QTest::newRow( "C decimal" ) << QString( "4cd6" ) << 4.6;
QTest::newRow( "locale decimal" ) << QString( "4ld6" ) << 4.6;
QTest::newRow( "locale decimal" ) << QString( "4444ld6" ) << 4444.6;

// QgsDoubleValidator doesn't expect group separator but it tolerates it,
// so the result will be QValidator::Intermediate and not QValidator::Acceptable
QTest::newRow( "locale group separator + locale decimal" ) << QString( "4lg444ld6" ) << 4444.6;
QTest::newRow( "locale group separator + c decimal" ) << QString( "4lg444cd6" ) << 0.0;
QTest::newRow( "c group separator + locale decimal" ) << QString( "4cg444ld6" ) << 0.0;
QTest::newRow( "c group separator + c decimal" ) << QString( "4cg444cd6" ) << 4444.6;

QTest::newRow( "outside the range + local decimal" ) << QString( "3ld6" ) << 3.6;
QTest::newRow( "outside the range + c decimal" ) << QString( "3cd6" ) << 3.6;
QTest::newRow( "string" ) << QString( "string" ) << 0.0;

}

void TestQgsDoubleValidator::validate()
{
QLineEdit *lineEdit = new QLineEdit();
QgsDoubleValidator *validator = new QgsDoubleValidator( 4, 10000, lineEdit );
lineEdit->setValidator( validator );

QFETCH( QString, actualState );
QFETCH( int, expState );
QString value;
int expectedValue;

QVector<QLocale>listLocale( {QLocale::English, QLocale::French, QLocale::German, QLocale::Italian} );
QLocale loc;
for ( int i = 0; i < listLocale.count(); ++i )
{
loc = listLocale.at( i );
QLocale().setDefault( loc );
validator->setLocale( loc );
value = actualState;
value = value.replace( "ld", QLocale().decimalPoint() )
.replace( "cd", QLocale( QLocale::C ).decimalPoint() )
.replace( "lg", QLocale().groupSeparator() )
.replace( "cg", QLocale( QLocale::C ).groupSeparator() );
expectedValue = expState;
// if the local group separator / decimal point is equal to the C one,
// expected result will be different for double with test with mixed
// local/C characters.
// Example with lg as local group separator
// cg as C group separator
// ld as local decimal point
// cd as C decimal point
// for 4cg444ld6 double, if cg == lg then 4cg444ld6 == 4lg444ld6
// and validator->validate(4lg444ld6) == 1 and not 0
// for 4cg444ld6 double, if cd == ld then 4cg444ld6 == 4cg444cd6
// and validator->validate(4cg444cd6) == 1 and not 0
if ( ( QLocale( QLocale::C ).groupSeparator() == QLocale().groupSeparator() ||
QLocale( QLocale::C ).decimalPoint() == QLocale().decimalPoint() )
&& value != "string" && expectedValue == 0 )
expectedValue = 1;
QCOMPARE( int( validator->validate( value ) ), expectedValue );
}
}

void TestQgsDoubleValidator::toDouble()
{
QFETCH( QString, actualValue );
QFETCH( double, expValue );
QString value;
double expectedValue;

QVector<QLocale>listLocale( {QLocale::English, QLocale::French, QLocale::German, QLocale::Italian} );
QLocale loc;
for ( int i = 0; i < listLocale.count(); ++i )
{
loc = listLocale.at( i );
QLocale().setDefault( loc );
value = actualValue;
value = value.replace( "ld", QLocale().decimalPoint() )
.replace( "cd", QLocale( QLocale::C ).decimalPoint() )
.replace( "lg", QLocale().groupSeparator() )
.replace( "cg", QLocale( QLocale::C ).groupSeparator() );
expectedValue = expValue;
// if the local group separator / decimal point is equal to the C one,
// expected result will be different for double with test with mixed
// local/C characters.
// Example with lg as local group separator
// cg as C group separator
// ld as local decimal point
// cd as C group decimal point
// for 4cg444ld6 double, if cg == lg then 4cg444ld6 == 4lg444ld6
// and QgsDoubleValidator::toDouble(4lg444ld6) == 4444.6 and not 0.0
// for 4cg444ld6 double, if cd == ld then 4cg444ld6 == 4cg444cd6
// and QgsDoubleValidator::toDouble(4cg444cd6) == 4444.6 and not 0.0
if ( ( QLocale( QLocale::C ).groupSeparator() == QLocale().groupSeparator() ||
QLocale( QLocale::C ).decimalPoint() == QLocale().decimalPoint() )
&& value != "string" && expectedValue == 0.0 )
expectedValue = 4444.6;

QCOMPARE( QgsDoubleValidator::toDouble( value ), expectedValue );
}

}

QGSTEST_MAIN( TestQgsDoubleValidator )
#include "testqgsdoublevalidator.moc"