Skip to content
Permalink
Browse files

Make style model decoration icons size responsive

We hack the model a bit here, but as much as possible avoid bleeding
view properties into the model API.

So we use a QObject property ("icon_size") to specify icons sizes
for the model to generate. This is set on instances
of the model to indicate the required sizes for decorations in all
views connected to the model, and allows the model to have size responsive icons.

By using a QObject property we avoid having public GUI/view related API within
the model, and mostly avoid view related properties contaminating the pure model,
yet still have pixel-perfect symbol renders for the required view icon sizes.
  • Loading branch information
nyalldawson committed Sep 12, 2018
1 parent 28836d2 commit d7edeac37962ef4fd24b9130dc84a08e0a7715d8
Showing with 70 additions and 3 deletions.
  1. +26 −2 src/core/symbology/qgsstylemodel.cpp
  2. +44 −1 tests/src/python/test_qgsstylemodel.py
@@ -18,6 +18,7 @@
#include "qgssymbollayerutils.h"
#include <QIcon>

const double ICON_PADDING_FACTOR = 0.16;

QgsStyleModel::QgsStyleModel( QgsStyle *style, QObject *parent )
: QAbstractItemModel( parent )
@@ -70,18 +71,41 @@ QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const

case Qt::DecorationRole:
{
// check the model custom property for icon sizes to generate. This is used
// by instances of the model to indicate the required sizes for decorations in all
// views connected to the model, and allows the model to have size responsive icons.
// By using a QObject property we avoid having public GUI/view related API within
// the model, and mostly avoid view related properties contaminating the pure model...
const QVariantList iconSizes = property( "icon_sizes" ).toList();

switch ( index.column() )
{
case Name:
if ( !isColorRamp )
{
std::unique_ptr< QgsSymbol > symbol( mStyle->symbol( name ) );
return QgsSymbolLayerUtils::symbolPreviewIcon( symbol.get(), QSize( 24, 24 ), 2 );
QIcon icon;
icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), QSize( 24, 24 ), 1 ) );

for ( const QVariant &size : iconSizes )
{
QSize s = size.toSize();
icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ) ) );
}

return icon;
}
else
{
std::unique_ptr< QgsColorRamp > ramp( mStyle->colorRamp( name ) );
return QgsSymbolLayerUtils::colorRampPreviewIcon( ramp.get(), QSize( 24, 24 ), 2 );
QIcon icon;
icon.addPixmap( QgsSymbolLayerUtils::colorRampPreviewPixmap( ramp.get(), QSize( 24, 24 ), 1 ) );
for ( const QVariant &size : iconSizes )
{
QSize s = size.toSize();
icon.addPixmap( QgsSymbolLayerUtils::colorRampPreviewPixmap( ramp.get(), s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ) ) );
}
return icon;
}
case Tags:
return QVariant();
@@ -23,7 +23,7 @@
QgsStyle,
QgsStyleProxyModel)
from qgis.testing import start_app, unittest
from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtCore import Qt, QSize
from qgis.PyQt.QtGui import QColor

start_app()
@@ -715,6 +715,49 @@ def test_filter_proxy(self):
model.setSmartGroupId(-1)
self.assertEqual(model.rowCount(), 8)

def testIconSize(self):
"""
Test that model has responsive icon sizes for decorations
"""
style = QgsStyle()
style.createMemoryDatabase()

symbol_a = createMarkerSymbol()
symbol_a.setColor(QColor(255, 10, 10))
self.assertTrue(style.addSymbol('a', symbol_a, True))
ramp_a = QgsLimitedRandomColorRamp(5)
self.assertTrue(style.addColorRamp('ramp a', ramp_a, True))

model = QgsStyleModel(style)
self.assertEqual(model.rowCount(), 2)
for i in range(2):
icon = model.data(model.index(i, 0), Qt.DecorationRole)
# by default, only 24x24 icon
self.assertEqual(icon.availableSizes(), [QSize(24, 24)])
self.assertEqual(icon.actualSize(QSize(10, 10)), QSize(10, 10))
self.assertEqual(icon.actualSize(QSize(24, 24)), QSize(24, 24))
self.assertEqual(icon.actualSize(QSize(90, 90)), QSize(24, 24))

model.setProperty('icon_sizes', [QSize(24, 24), QSize(100, 90)])
icon = model.data(model.index(i, 0), Qt.DecorationRole)
self.assertEqual(icon.availableSizes(), [QSize(24, 24), QSize(100, 90)])
self.assertEqual(icon.actualSize(QSize(10, 10)), QSize(10, 10))
self.assertEqual(icon.actualSize(QSize(24, 24)), QSize(24, 24))
self.assertEqual(icon.actualSize(QSize(25, 25)), QSize(25, 22))
self.assertEqual(icon.actualSize(QSize(90, 90)), QSize(90, 81))
self.assertEqual(icon.actualSize(QSize(125, 125)), QSize(100, 90))

model.setProperty('icon_sizes', [QSize(100, 90), QSize(200, 180)])
icon = model.data(model.index(i, 0), Qt.DecorationRole)
self.assertEqual(icon.availableSizes(), [QSize(24, 24), QSize(100, 90), QSize(200, 180)])
self.assertEqual(icon.actualSize(QSize(10, 10)), QSize(10, 10))
self.assertEqual(icon.actualSize(QSize(24, 24)), QSize(24, 24))
self.assertEqual(icon.actualSize(QSize(25, 25)), QSize(25, 22))
self.assertEqual(icon.actualSize(QSize(90, 90)), QSize(90, 81))
self.assertEqual(icon.actualSize(QSize(125, 125)), QSize(125, 112))
self.assertEqual(icon.actualSize(QSize(225, 225)), QSize(200, 180))
model.setProperty('icon_sizes', None)


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

0 comments on commit d7edeac

Please sign in to comment.
You can’t perform that action at this time.