Skip to content

Commit

Permalink
Fix crash when using the identify tool on a categorized render
Browse files Browse the repository at this point in the history
with an unchecked category corresponding to the feature at the
clicked point

Also fix count of default category symbols
  • Loading branch information
nyalldawson committed Mar 1, 2018
1 parent 4e182be commit 0acdcfa
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 21 deletions.
46 changes: 44 additions & 2 deletions python/core/symbology/qgscategorizedsymbolrenderer.sip.in
Expand Up @@ -45,7 +45,22 @@ copy constructor
void setLabel( const QString &label );

bool renderState() const;
%Docstring
Returns true if the category is currently enabled and should be rendered.

.. seealso:: :py:func:`setRenderState`

.. versionadded:: 2.5
%End

void setRenderState( bool render );
%Docstring
Sets whether the category is currently enabled and should be rendered.

.. seealso:: :py:func:`renderState`

.. versionadded:: 2.5
%End

QString dump() const;

Expand Down Expand Up @@ -252,9 +267,36 @@ Will return null if the functionality is disabled.
hashtable for faster access to symbols
%End

QgsSymbol *skipRender();
QgsSymbol *skipRender() /Deprecated/;
%Docstring

QgsSymbol *symbolForValue( const QVariant &value );
.. deprecated:: No longer used, will be removed in QGIS 4.0
%End

QgsSymbol *symbolForValue( const QVariant &value ) /Deprecated/;
%Docstring
Returns the matching symbol corresponding to an attribute ``value``.

.. deprecated:: use variant which takes a second bool argument instead.
%End


QgsSymbol *symbolForValue( const QVariant &value, bool &foundMatchingSymbol /Out/ ) /PyName=symbolForValue2/;
%Docstring
Returns the matching symbol corresponding to an attribute ``value``.

Will return None if no matching symbol was found for ``value``, or
if the category corresponding to ``value`` is currently disabled (see QgsRendererCategory.renderState()).

If ``foundMatchingSymbol`` is specified then it will be set to true if
a matching category was found. This can be used to differentiate between
a None returned as a result of no matching category vs a None as a result
of disabled categories.

.. note::

available in Python bindings as symbolForValue2
%End

private:
QgsCategorizedSymbolRenderer( const QgsCategorizedSymbolRenderer & );
Expand Down
1 change: 1 addition & 0 deletions python/gui/qgisinterface.sip.in
Expand Up @@ -849,6 +849,7 @@ Unregister a previously registered custom drop ``handler`` for layout windows.
%End



virtual void openURL( const QString &url, bool useQgisDocDirectory = true ) = 0 /Deprecated/;
%Docstring
Open a url in the users browser. By default the QGIS doc directory is used
Expand Down
2 changes: 1 addition & 1 deletion python/server/qgsmapserviceexception.sip.in
Expand Up @@ -16,7 +16,7 @@ class QgsMapServiceException : QgsOgcServiceException
%Docstring
Exception class for WMS service exceptions (for compatibility only).

\deprecated Use QsgServerException
.. deprecated:: Use QsgServerException

The most important codes are:
* "InvalidFormat"
Expand Down
8 changes: 6 additions & 2 deletions scripts/sipify.pl
Expand Up @@ -192,6 +192,9 @@ sub processDoxygenLine {
if ( $line =~ m/\\since .*?([\d\.]+)/i ) {
return "\n.. versionadded:: $1\n";
}
if ( $line =~ m/\\deprecated (.*)/i ) {
return "\n.. deprecated:: $1\n";
}

# create links in see also
if ( $line =~ m/\\see +(\w+(\.\w+)*)(\([^()]*\))?/ ) {
Expand Down Expand Up @@ -596,7 +599,7 @@ sub detect_non_method_member{
next;
}
# Skip Q_OBJECT, Q_PROPERTY, Q_ENUM, Q_GADGET etc.
if ($LINE =~ m/^\s*Q_(OBJECT|ENUMS|ENUM|FLAG|PROPERTY|GADGET|DECLARE_METATYPE|DECLARE_TYPEINFO|DECL_DEPRECATED|NOWARN_DEPRECATED_(PUSH|POP))\b.*?$/){
if ($LINE =~ m/^\s*Q_(OBJECT|ENUMS|ENUM|FLAG|PROPERTY|GADGET|DECLARE_METATYPE|DECLARE_TYPEINFO|NOWARN_DEPRECATED_(PUSH|POP))\b.*?$/){
next;
}

Expand Down Expand Up @@ -848,7 +851,8 @@ sub detect_non_method_member{
$LINE =~ s/\s*\boverride\b//;
$LINE =~ s/\s*\bextern \b//;
$LINE =~ s/\s*\bMAYBE_UNUSED \b//;
$LINE =~ s/\s*\bNODISCARD \b//;
$LINE =~ s/\s*\bNODISCARD \b//;
$LINE =~ s/\s*\bQ_DECL_DEPRECATED\b//;
$LINE =~ s/^(\s*)?(const )?(virtual |static )?inline /$1$2$3/;
$LINE =~ s/\bconstexpr\b/const/;
$LINE =~ s/\bnullptr\b/0/g;
Expand Down
31 changes: 18 additions & 13 deletions src/core/symbology/qgscategorizedsymbolrenderer.cpp
Expand Up @@ -168,21 +168,25 @@ void QgsCategorizedSymbolRenderer::rebuildHash()
for ( int i = 0; i < mCategories.size(); ++i )
{
const QgsRendererCategory &cat = mCategories.at( i );
mSymbolHash.insert( cat.value().toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : skipRender() );
mSymbolHash.insert( cat.value().toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : nullptr );
}
}

QgsSymbol *QgsCategorizedSymbolRenderer::skipRender()
{
static QgsMarkerSymbol *skipRender = nullptr;
if ( !skipRender )
skipRender = new QgsMarkerSymbol();

return skipRender;
return nullptr;
}

QgsSymbol *QgsCategorizedSymbolRenderer::symbolForValue( const QVariant &value )
{
bool found = false;
return symbolForValue( value, found );
}

QgsSymbol *QgsCategorizedSymbolRenderer::symbolForValue( const QVariant &value, bool &foundMatchingSymbol )
{
foundMatchingSymbol = false;

// TODO: special case for int, double
QHash<QString, QgsSymbol *>::const_iterator it = mSymbolHash.constFind( value.isNull() ? QLatin1String( "" ) : value.toString() );
if ( it == mSymbolHash.constEnd() )
Expand All @@ -198,6 +202,8 @@ QgsSymbol *QgsCategorizedSymbolRenderer::symbolForValue( const QVariant &value )
return nullptr;
}

foundMatchingSymbol = true;

return *it;
}

Expand Down Expand Up @@ -228,15 +234,14 @@ QgsSymbol *QgsCategorizedSymbolRenderer::originalSymbolForFeature( QgsFeature &f
{
QVariant value = valueForFeature( feature, context );

bool foundCategory = false;
// find the right symbol for the category
QgsSymbol *symbol = symbolForValue( value );
if ( symbol == skipRender() )
return nullptr;
QgsSymbol *symbol = symbolForValue( value, foundCategory );

if ( !symbol )
if ( !foundCategory )
{
// if no symbol found use default one
return symbolForValue( QVariant( "" ) );
// if no symbol found, use default symbol
return symbolForValue( QVariant( "" ), foundCategory );
}

return symbol;
Expand Down Expand Up @@ -793,7 +798,7 @@ QSet<QString> QgsCategorizedSymbolRenderer::legendKeysForFeature( QgsFeature &fe
{
if ( value == cat.value() )
{
if ( cat.renderState() )
if ( cat.renderState() || mCounting )
return QSet< QString >() << QString::number( i );
else
return QSet< QString >();
Expand Down
40 changes: 37 additions & 3 deletions src/core/symbology/qgscategorizedsymbolrenderer.h
Expand Up @@ -56,8 +56,18 @@ class CORE_EXPORT QgsRendererCategory
void setSymbol( QgsSymbol *s SIP_TRANSFER );
void setLabel( const QString &label );

// \since QGIS 2.5
/**
* Returns true if the category is currently enabled and should be rendered.
* \see setRenderState()
* \since QGIS 2.5
*/
bool renderState() const;

/**
* Sets whether the category is currently enabled and should be rendered.
* \see renderState()
* \since QGIS 2.5
*/
void setRenderState( bool render );

// debugging
Expand Down Expand Up @@ -233,9 +243,33 @@ class CORE_EXPORT QgsCategorizedSymbolRenderer : public QgsFeatureRenderer

void rebuildHash();

QgsSymbol *skipRender();
/**
* \deprecated No longer used, will be removed in QGIS 4.0
*/
Q_DECL_DEPRECATED QgsSymbol *skipRender() SIP_DEPRECATED;

/**
* Returns the matching symbol corresponding to an attribute \a value.
* \deprecated use variant which takes a second bool argument instead.
*/
Q_DECL_DEPRECATED QgsSymbol *symbolForValue( const QVariant &value ) SIP_DEPRECATED;

QgsSymbol *symbolForValue( const QVariant &value );
// TODO QGIS 4.0 - rename Python method to symbolForValue

/**
* Returns the matching symbol corresponding to an attribute \a value.
*
* Will return nullptr if no matching symbol was found for \a value, or
* if the category corresponding to \a value is currently disabled (see QgsRendererCategory::renderState()).
*
* If \a foundMatchingSymbol is specified then it will be set to true if
* a matching category was found. This can be used to differentiate between
* a nullptr returned as a result of no matching category vs a nullptr as a result
* of disabled categories.
*
* \note available in Python bindings as symbolForValue2
*/
QgsSymbol *symbolForValue( const QVariant &value, bool &foundMatchingSymbol SIP_OUT ) SIP_PYNAME( symbolForValue2 );

private:
#ifdef SIP_RUN
Expand Down

0 comments on commit 0acdcfa

Please sign in to comment.