Skip to content
Permalink
Browse files
Merge pull request #45587 from elpaso/bugfix-gh45582-vector-renderer-…
…localization-issues

Fix multiple localization issues with vector renderers
  • Loading branch information
elpaso committed Oct 29, 2021
2 parents b28a276 + 341e865 commit 5c3915b9eda3cb3f91fb6fc035bb7850ed773a9e
@@ -456,6 +456,20 @@ configuration.
.. versionadded:: 3.6
%End

static QString displayString( const QVariant &value, int precision = -1 );
%Docstring
Returns a localized representation of ``value`` with the given ``precision``,
if precision is -1 then precision is guessed from the default QVariant.toString
output.

.. note::

Precision is ignored for integers.

.. versionadded:: 3.22.1
%End


protected:


@@ -18,9 +18,24 @@ class QgsLUDialog: QDialog
public:
QgsLUDialog( QWidget *parent /TransferThis/ = 0, Qt::WindowFlags fl = QgsGuiUtils::ModalDialogFlags );
QString lowerValue() const;

double lowerValueDouble() const;
%Docstring
Returns the lower value.

.. versionadded:: 3.21
%End
void setLowerValue( const QString &val );
QString upperValue() const;

double upperValueDouble() const;
%Docstring
Returns the upper value.

.. versionadded:: 3.21
%End
void setUpperValue( const QString &val );

};

/************************************************************************
@@ -19,6 +19,12 @@ class QgsCategorizedSymbolRendererWidget : QgsRendererWidget
#include "qgscategorizedsymbolrendererwidget.h"
%End
public:

enum CustomRoles
{
ValueRole
};

static QgsRendererWidget *create( QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer ) /Factory/;

QgsCategorizedSymbolRendererWidget( QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer );
@@ -216,12 +216,12 @@ QgsSnappingWidget::QgsSnappingWidget( QgsProject *project, QgsMapCanvas *canvas,
connect( mTypeButton, &QToolButton::triggered, this, &QgsSnappingWidget::typeButtonTriggered );

// tolerance
mToleranceSpinBox = new QDoubleSpinBox();
mToleranceSpinBox = new QgsDoubleSpinBox();
mToleranceSpinBox->setDecimals( 5 );
mToleranceSpinBox->setMaximum( 99999999.990000 );
mToleranceSpinBox->setToolTip( tr( "Snapping Tolerance in Defined Units" ) );
mToleranceSpinBox->setObjectName( QStringLiteral( "SnappingToleranceSpinBox" ) );
connect( mToleranceSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsSnappingWidget::changeTolerance );
connect( mToleranceSpinBox, qOverload< double >( &QgsDoubleSpinBox::valueChanged ), this, &QgsSnappingWidget::changeTolerance );

// units
mUnitsComboBox = new QComboBox();
@@ -158,7 +158,7 @@ class APP_EXPORT QgsSnappingWidget : public QWidget
QToolButton *mTypeButton = nullptr;
QAction *mTypeAction = nullptr; // hide widget does not work on toolbar, action needed
QList< QAction * > mSnappingFlagActions;
QDoubleSpinBox *mToleranceSpinBox = nullptr;
QgsDoubleSpinBox *mToleranceSpinBox = nullptr;
QgsScaleWidget *mMinScaleWidget = nullptr;
QgsScaleWidget *mMaxScaleWidget = nullptr;
QAction *mToleranceAction = nullptr; // hide widget does not work on toolbar, action needed
@@ -343,5 +343,5 @@ QString QgsClassificationMethod::labelForRange( const double lowerValue, const d
const QString lowerLabel = valueToLabel( lowerValue );
const QString upperLabel = valueToLabel( upperValue );

return labelFormat().arg( lowerLabel ).arg( upperLabel );
return labelFormat().arg( lowerLabel, upperLabel );
}
@@ -668,6 +668,39 @@ QgsFeatureRenderer *QgsCategorizedSymbolRenderer::create( QDomElement &element,
QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
QgsCategoryList cats;

// Value from string (long, ulong, double and string)
const auto valueFromString = []( const QString & value, const QString & valueType ) -> QVariant
{
if ( valueType == QStringLiteral( "double" ) )
{
bool ok;
const auto val { value.toDouble( &ok ) };
if ( ok )
{
return val;
}
}
else if ( valueType == QStringLiteral( "ulong" ) )
{
bool ok;
const auto val { value.toULongLong( &ok ) };
if ( ok )
{
return val;
}
}
else if ( valueType == QStringLiteral( "long" ) )
{
bool ok;
const auto val { value.toLongLong( &ok ) };
if ( ok )
{
return val;
}
}
return value;
};

QDomElement catElem = catsElem.firstChildElement();
while ( !catElem.isNull() )
{
@@ -676,7 +709,8 @@ QgsFeatureRenderer *QgsCategorizedSymbolRenderer::create( QDomElement &element,
QVariant value;
if ( catElem.hasAttribute( QStringLiteral( "value" ) ) )
{
value = QVariant( catElem.attribute( QStringLiteral( "value" ) ) );
value = valueFromString( catElem.attribute( QStringLiteral( "value" ) ), catElem.attribute( QStringLiteral( "type" ), QString() ) ) ;

}
else
{
@@ -686,7 +720,7 @@ QgsFeatureRenderer *QgsCategorizedSymbolRenderer::create( QDomElement &element,
{
if ( valElem.tagName() == QLatin1String( "val" ) )
{
values << QVariant( valElem.attribute( QStringLiteral( "value" ) ) );
values << valueFromString( valElem.attribute( QStringLiteral( "value" ) ), valElem.attribute( QStringLiteral( "type" ), QString() ) );
}
valElem = valElem.nextSiblingElement();
}
@@ -778,6 +812,28 @@ QDomElement QgsCategorizedSymbolRenderer::save( QDomDocument &doc, const QgsRead
rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "categorizedSymbol" ) );
rendererElem.setAttribute( QStringLiteral( "attr" ), mAttrName );

// String for type
// We just need string and three numeric types: double, ulong and long for unsigned, signed and float/double
const auto stringForType = []( const QVariant::Type type ) -> QString
{
if ( type == QVariant::Char || type == QVariant::Int || type == QVariant::LongLong )
{
return QStringLiteral( "long" );
}
else if ( type == QVariant::UInt || type == QVariant::ULongLong )
{
return QStringLiteral( "ulong" );
}
else if ( type == QVariant::Double )
{
return QStringLiteral( "double" ) ;
}
else // Default: string
{
return QStringLiteral( "string" );
}
};

// categories
if ( !mCategories.isEmpty() )
{
@@ -798,13 +854,15 @@ QDomElement QgsCategorizedSymbolRenderer::save( QDomDocument &doc, const QgsRead
for ( const QVariant &v : list )
{
QDomElement valueElem = doc.createElement( QStringLiteral( "val" ) );
valueElem.setAttribute( "value", v.toString() );
valueElem.setAttribute( QStringLiteral( "value" ), v.toString() );
valueElem.setAttribute( QStringLiteral( "type" ), stringForType( v.type() ) );
catElem.appendChild( valueElem );
}
}
else
{
catElem.setAttribute( QStringLiteral( "value" ), cat.value().toString() );
catElem.setAttribute( QStringLiteral( "type" ), stringForType( cat.value().type() ) );
}
catElem.setAttribute( QStringLiteral( "symbol" ), symbolName );
catElem.setAttribute( QStringLiteral( "label" ), cat.label() );
@@ -865,6 +923,126 @@ QgsLegendSymbolList QgsCategorizedSymbolRenderer::baseLegendSymbolItems() const
return lst;
}

QString QgsCategorizedSymbolRenderer::displayString( const QVariant &v, int precision )
{

auto _displayString = [ ]( const QVariant & v, int precision ) -> QString
{

if ( v.isNull() )
{
return QgsApplication::nullRepresentation();
}

const bool isNumeric {v.type() == QVariant::Double || v.type() == QVariant::Int || v.type() == QVariant::UInt || v.type() == QVariant::LongLong || v.type() == QVariant::ULongLong};

// Special treatment for numeric types if group separator is set or decimalPoint is not a dot
if ( v.type() == QVariant::Double )
{
// if value doesn't contain a double (a default value expression for instance),
// apply no transformation
bool ok;
v.toDouble( &ok );
if ( !ok )
return v.toString();

// Locales with decimal point != '.' or that require group separator: use QLocale
if ( QLocale().decimalPoint() != '.' ||
!( QLocale().numberOptions() & QLocale::NumberOption::OmitGroupSeparator ) )
{
if ( precision > 0 )
{
if ( -1 < v.toDouble() && v.toDouble() < 1 )
{
return QLocale().toString( v.toDouble(), 'g', precision );
}
else
{
return QLocale().toString( v.toDouble(), 'f', precision );
}
}
else
{
// Precision is not set, let's guess it from the
// standard conversion to string
const QString s( v.toString() );
const int dotPosition( s.indexOf( '.' ) );
int precision;
if ( dotPosition < 0 && s.indexOf( 'e' ) < 0 )
{
precision = 0;
return QLocale().toString( v.toDouble(), 'f', precision );
}
else
{
if ( dotPosition < 0 ) precision = 0;
else precision = s.length() - dotPosition - 1;

if ( -1 < v.toDouble() && v.toDouble() < 1 )
{
return QLocale().toString( v.toDouble(), 'g', precision );
}
else
{
return QLocale().toString( v.toDouble(), 'f', precision );
}
}
}
}
// Default for doubles with precision
else if ( precision > 0 )
{
if ( -1 < v.toDouble() && v.toDouble() < 1 )
{
return QString::number( v.toDouble(), 'g', precision );
}
else
{
return QString::number( v.toDouble(), 'f', precision );
}
}
}
// Other numeric types than doubles
else if ( isNumeric &&
!( QLocale().numberOptions() & QLocale::NumberOption::OmitGroupSeparator ) )
{
bool ok;
const qlonglong converted( v.toLongLong( &ok ) );
if ( ok )
return QLocale().toString( converted );
}
else if ( v.type() == QVariant::ByteArray )
{
return QObject::tr( "BLOB" );
}

// Fallback if special rules do not apply
return v.toString();
};

if ( v.type() == QVariant::StringList || v.type() == QVariant::List )
{
// Note that this code is never hit because the joining of lists (merged categories) happens
// in data(); I'm leaving this here anyway because it is tested and it may be useful for
// other purposes in the future.
QString result;
const QVariantList list = v.toList();
for ( const QVariant &var : list )
{
if ( !result.isEmpty() )
{
result.append( ';' );
}
result.append( _displayString( var, precision ) );
}
return result;
}
else
{
return _displayString( v, precision );
}
}

QgsLegendSymbolList QgsCategorizedSymbolRenderer::legendSymbolItems() const
{
if ( mDataDefinedSizeLegend && mSourceSymbol && mSourceSymbol->type() == Qgis::SymbolType::Marker )
@@ -1209,8 +1387,8 @@ QgsCategoryList QgsCategorizedSymbolRenderer::createCategories( const QList<QVar
QgsSymbol *newSymbol = symbol->clone();
if ( !value.isNull() )
{
int fieldIdx = fields.lookupField( attributeName );
QString categoryName = value.toString();
const int fieldIdx = fields.lookupField( attributeName );
QString categoryName = displayString( value );
if ( fieldIdx != -1 )
{
const QgsField field = fields.at( fieldIdx );
@@ -441,6 +441,18 @@ class CORE_EXPORT QgsCategorizedSymbolRenderer : public QgsFeatureRenderer
*/
static QgsCategoryList createCategories( const QVariantList &values, const QgsSymbol *symbol, QgsVectorLayer *layer = nullptr, const QString &fieldName = QString() );

/**
* Returns a localized representation of \a value with the given \a precision,
* if precision is -1 then precision is guessed from the default QVariant::toString
* output.
*
* \note Precision is ignored for integers.
*
* \since QGIS 3.22.1
*/
static QString displayString( const QVariant &value, int precision = -1 );


protected:
QString mAttrName;
QgsCategoryList mCategories;
@@ -498,6 +510,7 @@ class CORE_EXPORT QgsCategorizedSymbolRenderer : public QgsFeatureRenderer

//! Returns list of legend symbol items from individual categories
QgsLegendSymbolList baseLegendSymbolItems() const;

};

#endif // QGSCATEGORIZEDSYMBOLRENDERER_H
Loading

0 comments on commit 5c3915b

Please sign in to comment.