Skip to content
Permalink
Browse files
Handle lists and address PR review comments.
  • Loading branch information
elpaso committed Oct 22, 2021
1 parent 3797cc6 commit 6a22e1ff4e2b0ab44b292f1bafaa3917e92e8abf
@@ -466,7 +466,7 @@ output.

Precision is ignored for integers.

.. versionadded:: 3.21
.. versionadded:: 3.22.1
%End


@@ -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 );
@@ -925,107 +925,122 @@ QgsLegendSymbolList QgsCategorizedSymbolRenderer::baseLegendSymbolItems() const

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

auto _displayString = [ ]( const QVariant & v, int precision ) -> QString
{
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};
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 ) )
// Special treatment for numeric types if group separator is set or decimalPoint is not a dot
if ( v.type() == QVariant::Double )
{
if ( precision > 0 )
// 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 ( -1 < v.toDouble() && v.toDouble() < 1 )
if ( precision > 0 )
{
return QLocale().toString( v.toDouble(), 'g', precision );
if ( -1 < v.toDouble() && v.toDouble() < 1 )
{
return QLocale().toString( v.toDouble(), 'g', precision );
}
else
{
return QLocale().toString( v.toDouble(), 'f', precision );
}
}
else
{
return QLocale().toString( v.toDouble(), 'f', precision );
// 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 );
}
}
}
}
else
// Default for doubles with precision
else if ( precision > 0 )
{
// 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 )
if ( -1 < v.toDouble() && v.toDouble() < 1 )
{
precision = 0;
return QLocale().toString( v.toDouble(), 'f', precision );
return QString::number( v.toDouble(), 'g', 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 );
}
return QString::number( v.toDouble(), 'f', precision );
}
}
}
// Default for doubles with precision
else if ( precision > 0 )
// Other numeric types than doubles
else if ( isNumeric &&
!( QLocale().numberOptions() & QLocale::NumberOption::OmitGroupSeparator ) )
{
if ( -1 < v.toDouble() && v.toDouble() < 1 )
{
return QString::number( v.toDouble(), 'g', precision );
}
else
{
return QString::number( v.toDouble(), 'f', precision );
}
bool ok;
const qlonglong converted( v.toLongLong( &ok ) );
if ( ok )
return QLocale().toString( converted );
}
}
// 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" );
}
else if ( v.type() == QVariant::StringList || v.type() == QVariant::List )
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( QStringLiteral( ", " ) );
result.append( var.toString() );
{
result.append( ';' );
}
result.append( _displayString( var, precision ) );
}
return result;
}

// Fallback if special rules do not apply
return v.toString();
else
{
return _displayString( v, precision );
}
}

QgsLegendSymbolList QgsCategorizedSymbolRenderer::legendSymbolItems() const
@@ -448,7 +448,7 @@ class CORE_EXPORT QgsCategorizedSymbolRenderer : public QgsFeatureRenderer
*
* \note Precision is ignored for integers.
*
* \since QGIS 3.21
* \since QGIS 3.22.1
*/
static QString displayString( const QVariant &value, int precision = -1 );

@@ -51,7 +51,6 @@
#include <QPainter>
#include <QFileDialog>
#include <QClipboard>
#include <QStyledItemDelegate>

///@cond PRIVATE

@@ -251,7 +250,7 @@ QVariant QgsCategorizedSymbolRendererModel::data( const QModelIndex &index, int
}
break;
}
case Qt::UserRole:
case QgsCategorizedSymbolRendererWidget::CustomRoles::ValueRole:
{
if ( index.column() == 1 )
return category.value();
@@ -502,7 +501,7 @@ QgsCategorizedRendererViewItemDelegate::QgsCategorizedRendererViewItemDelegate(

QWidget *QgsCategorizedRendererViewItemDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
{
const QVariant::Type userType { index.data( Qt::UserRole ).type() };
const QVariant::Type userType { index.data( QgsCategorizedSymbolRendererWidget::CustomRoles::ValueRole ).type() };
QgsDoubleSpinBox *editor = nullptr;
switch ( userType )
{
@@ -98,19 +98,6 @@ class QgsCategorizedRendererViewItemDelegate: public QStyledItemDelegate
};


/*
class QgsCategorizedRendererItemEditorFactory: public QItemEditorFactory
{
Q_OBJECT
public:
QgsCategorizedRendererItemEditorFactory();
// QItemEditorFactory interface
QWidget* createEditor(int userType, QWidget* parent) const override;
};
*/
///@endcond

#endif
@@ -123,6 +110,16 @@ class GUI_EXPORT QgsCategorizedSymbolRendererWidget : public QgsRendererWidget,
{
Q_OBJECT
public:

/**
* CustomRoles enum represent custom roles for the widget.
* \since QGIS 3.22.1
*/
enum CustomRoles
{
ValueRole = Qt::UserRole + 1 //!< Category value
};

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

QgsCategorizedSymbolRendererWidget( QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer );
@@ -735,27 +735,57 @@ def test_displayString(self):
"""Test the displayString method"""

# Default locale for tests is EN
original_locale = QLocale()
locale = QLocale(QLocale.English)
locale.setNumberOptions(QLocale.DefaultNumberOptions)
QLocale().setDefault(locale)

self.assertEqual(QgsCategorizedSymbolRenderer.displayString(1234.56), "1,234.56")
self.assertEqual(QgsCategorizedSymbolRenderer.displayString(1234.56, 4), "1,234.5600")
self.assertEqual(QgsCategorizedSymbolRenderer.displayString(1234567), "1,234,567")
self.assertEqual(QgsCategorizedSymbolRenderer.displayString(1234567.0, 4), "1,234,567.0000")
# Precision is ignored for integers
self.assertEqual(QgsCategorizedSymbolRenderer.displayString(1234567, 4), "1,234,567")

original_locale = QLocale()
# Test list
self.assertEqual(QgsCategorizedSymbolRenderer.displayString([1234567, 891234], 4), "1,234,567;891,234")
self.assertEqual(QgsCategorizedSymbolRenderer.displayString([1234567.123, 891234.123], 4), "1,234,567.1230;891,234.1230")

locale.setNumberOptions(QLocale.OmitGroupSeparator)
QLocale().setDefault(locale)
self.assertTrue(QLocale().numberOptions() & QLocale.OmitGroupSeparator)
self.assertEqual(QgsCategorizedSymbolRenderer.displayString([1234567, 891234], 4), "1234567;891234")
self.assertEqual(QgsCategorizedSymbolRenderer.displayString([1234567.123, 891234.123], 4), "1234567.1230;891234.1230")

# Test a non-dot locale
QLocale().setDefault(QLocale(QLocale.Italian))
locale = QLocale(QLocale.Italian)
locale.setNumberOptions(QLocale.DefaultNumberOptions)
QLocale().setDefault(locale)
self.assertEqual(QgsCategorizedSymbolRenderer.displayString(1234.56), "1.234,56")
self.assertEqual(QgsCategorizedSymbolRenderer.displayString(1234.56, 4), "1.234,5600")
self.assertEqual(QgsCategorizedSymbolRenderer.displayString(1234567), "1.234.567")
self.assertEqual(QgsCategorizedSymbolRenderer.displayString(1234567.0, 4), "1.234.567,0000")
# Precision is ignored for integers
self.assertEqual(QgsCategorizedSymbolRenderer.displayString(1234567, 4), "1.234.567")

# Test list
self.assertEqual(QgsCategorizedSymbolRenderer.displayString([1234567, 891234], 4), "1.234.567;891.234")
self.assertEqual(QgsCategorizedSymbolRenderer.displayString([1234567.123, 891234.123], 4), "1.234.567,1230;891.234,1230")

locale.setNumberOptions(QLocale.OmitGroupSeparator)
QLocale().setDefault(locale)
self.assertEqual(QgsCategorizedSymbolRenderer.displayString([1234567, 891234], 4), "1234567;891234")
self.assertEqual(QgsCategorizedSymbolRenderer.displayString([1234567.123, 891234.123], 4), "1234567,1230;891234,1230")

QLocale().setDefault(original_locale)

def test_loclizedCategories(self):
def test_localizedCategories(self):

# Default locale for tests is EN
original_locale = QLocale()
locale = QLocale(QLocale.English)
locale.setNumberOptions(QLocale.DefaultNumberOptions)
QLocale().setDefault(locale)

layer = QgsVectorLayer("Point?field=flddbl:double&field=fldint:integer", "addfeat", "memory")
result = QgsCategorizedSymbolRenderer.createCategories([1234.5, 2345.6, 3456.7], QgsMarkerSymbol(), layer, 'flddouble')
@@ -764,13 +794,12 @@ def test_loclizedCategories(self):
self.assertEqual(result[1].label(), '2,345.6')
self.assertEqual(result[2].label(), '3,456.7')

original_locale = QLocale()
# Test a non-dot locale
QLocale().setDefault(QLocale(QLocale.Italian))

result = QgsCategorizedSymbolRenderer.createCategories([1234.5, 2345.6, 3456.7], QgsMarkerSymbol(), layer, 'flddouble')
result = QgsCategorizedSymbolRenderer.createCategories([[1234.5, 6789.1], 2345.6, 3456.7], QgsMarkerSymbol(), layer, 'flddouble')

self.assertEqual(result[0].label(), '1.234,5')
self.assertEqual(result[0].label(), '1.234,5;6.789,1')
self.assertEqual(result[1].label(), '2.345,6')
self.assertEqual(result[2].label(), '3.456,7')

@@ -789,10 +818,10 @@ def test_loclizedCategories(self):
project.read(temp_file)
results = project.mapLayersByName('addfeat')[0].renderer().categories()

self.assertEqual(result[0].label(), '1.234,5')
self.assertEqual(result[0].label(), '1.234,5;6.789,1')
self.assertEqual(result[1].label(), '2.345,6')
self.assertEqual(result[2].label(), '3.456,7')
self.assertEqual(result[0].value(), 1234.5)
self.assertEqual(result[0].value(), [1234.5, 6789.1])
self.assertEqual(result[1].value(), 2345.6)
self.assertEqual(result[2].value(), 3456.7)

0 comments on commit 6a22e1f

Please sign in to comment.