Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Fix categorized renderer localization
  • Loading branch information
elpaso committed Oct 22, 2021
1 parent f6e922e commit 3797cc6
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 4 deletions.
64 changes: 61 additions & 3 deletions src/core/symbology/qgscategorizedsymbolrenderer.cpp
Expand Up @@ -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() )
{
Expand All @@ -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
{
Expand All @@ -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();
}
Expand Down Expand Up @@ -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() )
{
Expand All @@ -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() );
Expand Down
74 changes: 74 additions & 0 deletions src/gui/symbology/qgscategorizedsymbolrendererwidget.cpp
Expand Up @@ -51,6 +51,7 @@
#include <QPainter>
#include <QFileDialog>
#include <QClipboard>
#include <QStyledItemDelegate>

///@cond PRIVATE

Expand Down Expand Up @@ -250,6 +251,12 @@ QVariant QgsCategorizedSymbolRendererModel::data( const QModelIndex &index, int
}
break;
}
case Qt::UserRole:
{
if ( index.column() == 1 )
return category.value();
break;
}
}

return QVariant();
Expand Down Expand Up @@ -487,6 +494,72 @@ void QgsCategorizedSymbolRendererViewStyle::drawPrimitive( PrimitiveElement elem
QProxyStyle::drawPrimitive( element, option, painter, widget );
}


QgsCategorizedRendererViewItemDelegate::QgsCategorizedRendererViewItemDelegate( QObject *parent )
: QStyledItemDelegate( parent )
{
}

QWidget *QgsCategorizedRendererViewItemDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
{
const QVariant::Type userType { index.data( Qt::UserRole ).type() };
QgsDoubleSpinBox *editor = nullptr;
switch ( userType )
{
case QVariant::Type::Double:
{
editor = new QgsDoubleSpinBox( parent );
editor->setDecimals( 12 );
editor->setMaximum( std::numeric_limits<double>::max() );
editor->setMinimum( std::numeric_limits<double>::lowest() );
break;
}
case QVariant::Type::Int:
{
editor = new QgsDoubleSpinBox( parent );
editor->setDecimals( 0 );
editor->setMaximum( std::numeric_limits<int>::max() );
editor->setMinimum( std::numeric_limits<int>::min() );
break;
}
case QVariant::Type::Char:
{
editor = new QgsDoubleSpinBox( parent );
editor->setDecimals( 0 );
editor->setMaximum( std::numeric_limits<char>::max() );
editor->setMinimum( std::numeric_limits<char>::min() );
break;
}
case QVariant::Type::UInt:
{
editor = new QgsDoubleSpinBox( parent );
editor->setDecimals( 0 );
editor->setMaximum( std::numeric_limits<unsigned int>::max() );
editor->setMinimum( 0 );
break;
}
case QVariant::Type::LongLong:
{
editor = new QgsDoubleSpinBox( parent );
editor->setDecimals( 0 );
editor->setMaximum( static_cast<double>( std::numeric_limits<qlonglong>::max() ) );
editor->setMinimum( std::numeric_limits<qlonglong>::min() );
break;
}
case QVariant::Type::ULongLong:
{
editor = new QgsDoubleSpinBox( parent );
editor->setDecimals( 0 );
editor->setMaximum( static_cast<double>( std::numeric_limits<unsigned long long>::max() ) );
editor->setMinimum( 0 );
break;
}
default:
break;
}
return editor ? editor : QStyledItemDelegate::createEditor( parent, option, index );
}

///@endcond

// ------------------------------ Widget ------------------------------------
Expand Down Expand Up @@ -555,6 +628,7 @@ QgsCategorizedSymbolRendererWidget::QgsCategorizedSymbolRendererWidget( QgsVecto
viewCategories->resizeColumnToContents( 0 );
viewCategories->resizeColumnToContents( 1 );
viewCategories->resizeColumnToContents( 2 );
viewCategories->setItemDelegateForColumn( 1, new QgsCategorizedRendererViewItemDelegate( viewCategories ) );

viewCategories->setStyle( new QgsCategorizedSymbolRendererViewStyle( viewCategories ) );
connect( viewCategories->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsCategorizedSymbolRendererWidget::selectionChanged );
Expand Down
30 changes: 30 additions & 0 deletions src/gui/symbology/qgscategorizedsymbolrendererwidget.h
Expand Up @@ -20,6 +20,7 @@
#include "qgsrendererwidget.h"
#include "qgsproxystyle.h"
#include <QStandardItem>
#include <QStyledItemDelegate>


class QgsCategorizedSymbolRenderer;
Expand Down Expand Up @@ -81,6 +82,35 @@ class QgsCategorizedSymbolRendererViewStyle: public QgsProxyStyle
void drawPrimitive( PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr ) const override;
};

/**
* \ingroup gui
* \brief Custom delegate for localized numeric input.
*/
class QgsCategorizedRendererViewItemDelegate: public QStyledItemDelegate
{
Q_OBJECT

public:
explicit QgsCategorizedRendererViewItemDelegate( QObject *parent = nullptr );

// QAbstractItemDelegate interface
QWidget *createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const override;
};


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

#endif
Expand Down
6 changes: 6 additions & 0 deletions src/ui/qgsludialogbase.ui
Expand Up @@ -53,6 +53,9 @@
</item>
<item row="0" column="1">
<widget class="QgsDoubleSpinBox" name="mLowerEdit">
<property name="decimals">
<number>12</number>
</property>
<property name="minimum">
<double>-1000000000000.000000000000000</double>
</property>
Expand All @@ -63,6 +66,9 @@
</item>
<item row="1" column="1">
<widget class="QgsDoubleSpinBox" name="mUpperEdit">
<property name="decimals">
<number>12</number>
</property>
<property name="minimum">
<double>-1000000000000.000000000000000</double>
</property>
Expand Down
43 changes: 42 additions & 1 deletion tests/src/python/test_qgscategorizedsymbolrenderer.py
Expand Up @@ -41,7 +41,7 @@
QgsEmbeddedSymbolRenderer,
QgsGeometry
)
from qgis.PyQt.QtCore import Qt, QVariant, QSize, QLocale
from qgis.PyQt.QtCore import Qt, QVariant, QSize, QLocale, QTemporaryDir
from qgis.PyQt.QtGui import QColor
from qgis.PyQt.QtXml import QDomDocument

Expand Down Expand Up @@ -755,6 +755,47 @@ def test_displayString(self):

QLocale().setDefault(original_locale)

def test_loclizedCategories(self):

layer = QgsVectorLayer("Point?field=flddbl:double&field=fldint:integer", "addfeat", "memory")
result = QgsCategorizedSymbolRenderer.createCategories([1234.5, 2345.6, 3456.7], QgsMarkerSymbol(), layer, 'flddouble')

self.assertEqual(result[0].label(), '1,234.5')
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')

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

# Test round trip
temp_dir = QTemporaryDir()
temp_file = os.path.join(temp_dir.path(), 'project.qgs')

project = QgsProject()
layer.setRenderer(QgsCategorizedSymbolRenderer('Class', result))
project.addMapLayers([layer])
project.write(temp_file)

QLocale().setDefault(original_locale)

project = QgsProject()
project.read(temp_file)
results = project.mapLayersByName('addfeat')[0].renderer().categories()

self.assertEqual(result[0].label(), '1.234,5')
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[1].value(), 2345.6)
self.assertEqual(result[2].value(), 3456.7)


if __name__ == "__main__":
unittest.main()

0 comments on commit 3797cc6

Please sign in to comment.