Fix incorrect symbols appearing for categorized and graduated renderers

This was triggered by 18614e11, which revealed a deeper bug in the handling
of symbols by these renderers. For both renderers a const method returns
a non-const pointer to a symbol and is used to modify this symbol in place.

Because both renderers store their classes in a QList (implicitly shared)
this means that all shared class lists were being updated when a clone of
a renderer has its symbols altered in this way. Prior to 18614e these
class QLists were being detached and copied in random ways.

Work around this by forcing a deep copy of the class lists when a renderer
is cloned.
This commit is contained in:
Nyall Dawson 2015-12-18 16:02:49 +11:00
parent 6a973d63c8
commit e9cd82902b
4 changed files with 15 additions and 8 deletions

View File

@ -149,20 +149,21 @@ void QgsRendererCategoryV2::toSld( QDomDocument &doc, QDomElement &element, QgsS
QgsCategorizedSymbolRendererV2::QgsCategorizedSymbolRendererV2( const QString& attrName, const QgsCategoryList& categories )
: QgsFeatureRendererV2( "categorizedSymbol" )
, mAttrName( attrName )
, mCategories( categories )
, mInvertedColorRamp( false )
, mScaleMethod( DEFAULT_SCALE_METHOD )
, mAttrNum( -1 )
, mCounting( false )
{
for ( int i = 0; i < mCategories.count(); ++i )
//important - we need a deep copy of the categories list, not a shared copy. This is required because
//QgsRendererCategoryV2::symbol() is marked const, and so retrieving the symbol via this method does not
//trigger a detachment and copy of mCategories BUT that same method CAN be used to modify a symbol in place
Q_FOREACH ( const QgsRendererCategoryV2& cat, categories )
{
if ( !mCategories.at( i ).symbol() )
if ( cat.symbol() )
{
QgsDebugMsg( "invalid symbol in a category! ignoring..." );
mCategories.removeAt( i-- );
}
//mCategories.insert(cat.value().toString(), cat);
mCategories << cat;
}
}

View File

@ -284,7 +284,6 @@ void QgsRendererRangeV2LabelFormat::saveToDomElement( QDomElement &element )
QgsGraduatedSymbolRendererV2::QgsGraduatedSymbolRendererV2( const QString& attrName, const QgsRangeList& ranges )
: QgsFeatureRendererV2( "graduatedSymbol" )
, mAttrName( attrName )
, mRanges( ranges )
, mMode( Custom )
, mInvertedColorRamp( false )
, mScaleMethod( DEFAULT_SCALE_METHOD )
@ -294,6 +293,15 @@ QgsGraduatedSymbolRendererV2::QgsGraduatedSymbolRendererV2( const QString& attrN
{
// TODO: check ranges for sanity (NULL symbols, invalid ranges)
//important - we need a deep copy of the ranges list, not a shared copy. This is required because
//QgsRendererRangeV2::symbol() is marked const, and so retrieving the symbol via this method does not
//trigger a detachment and copy of mRanges BUT that same method CAN be used to modify a symbol in place
Q_FOREACH ( const QgsRendererRangeV2& range, ranges )
{
mRanges << range;
}
}
QgsGraduatedSymbolRendererV2::~QgsGraduatedSymbolRendererV2()

View File

@ -116,7 +116,6 @@ class CORE_EXPORT QgsGraduatedSymbolRendererV2 : public QgsFeatureRendererV2
public:
QgsGraduatedSymbolRendererV2( const QString& attrName = QString(), const QgsRangeList& ranges = QgsRangeList() );
QgsGraduatedSymbolRendererV2( const QgsGraduatedSymbolRendererV2 & other );
virtual ~QgsGraduatedSymbolRendererV2();

View File

@ -138,7 +138,6 @@ class TestQgsRulebasedRenderer(TestCase):
assert self.r3.children()[0].filterExpression() == '"id" = 1'
assert self.r3.children()[1].filterExpression() == '"id" = 2'
@unittest.skip("temporarily disabled")
def testRefineWithRanges(self):
# Test refining rule with ranges (refs #10815)