QGIS API Documentation 3.27.0-Master (95e00c50d2)
qgsconditionalstyle.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsconditionalstyle.cpp
3 ---------------------
4 begin : August 2015
5 copyright : (C) 2015 by Nathan Woodrow
6 email : woodrow dot nathan at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15#include <QPainter>
16
17#include "qgsconditionalstyle.h"
18#include "qgsexpression.h"
19#include "qgsfontutils.h"
20#include "qgssymbollayerutils.h"
22#include "qgsmarkersymbol.h"
23
25 : QObject( parent )
26{}
27
29{
30 return mRowStyles;
31}
32
34{
35 if ( styles == mRowStyles )
36 return;
37
38 mRowStyles = styles;
39 emit changed();
40}
41
42void QgsConditionalLayerStyles::setFieldStyles( const QString &fieldName, const QList<QgsConditionalStyle> &styles )
43{
44 if ( mFieldStyles.value( fieldName ) == styles )
45 return;
46
47 mFieldStyles.insert( fieldName, styles );
48 emit changed();
49}
50
51QList<QgsConditionalStyle> QgsConditionalLayerStyles::fieldStyles( const QString &fieldName ) const
52{
53 return mFieldStyles.value( fieldName );
54}
55
56bool QgsConditionalLayerStyles::writeXml( QDomNode &node, QDomDocument &doc, const QgsReadWriteContext &context ) const
57{
58 QDomElement stylesel = doc.createElement( QStringLiteral( "conditionalstyles" ) );
59 QDomElement rowel = doc.createElement( QStringLiteral( "rowstyles" ) );
60 const auto constMRowStyles = mRowStyles;
61 for ( const QgsConditionalStyle &style : constMRowStyles )
62 {
63 style.writeXml( rowel, doc, context );
64 }
65
66 stylesel.appendChild( rowel );
67
68 QDomElement fieldsel = doc.createElement( QStringLiteral( "fieldstyles" ) );
69 QHash<QString, QgsConditionalStyles>::const_iterator it = mFieldStyles.constBegin();
70 for ( ; it != mFieldStyles.constEnd(); ++it )
71 {
72 QDomElement fieldel = doc.createElement( QStringLiteral( "fieldstyle" ) );
73 fieldel.setAttribute( QStringLiteral( "fieldname" ), it.key() );
74 const QgsConditionalStyles styles = it.value();
75 const auto constStyles = styles;
76 for ( const QgsConditionalStyle &style : constStyles )
77 {
78 style.writeXml( fieldel, doc, context );
79 }
80 fieldsel.appendChild( fieldel );
81 }
82
83 stylesel.appendChild( fieldsel );
84
85 node.appendChild( stylesel );
86 return true;
87}
88
90{
91 for ( const QgsConditionalStyle &style : std::as_const( mRowStyles ) )
92 {
93 if ( QgsExpression( style.rule() ).needsGeometry() )
94 {
95 return true;
96 }
97 }
98 return false;
99}
100
101bool QgsConditionalLayerStyles::readXml( const QDomNode &node, const QgsReadWriteContext &context )
102{
103 const QDomElement condel = node.firstChildElement( QStringLiteral( "conditionalstyles" ) );
104 mRowStyles.clear();
105 mFieldStyles.clear();
106 const QDomElement rowstylesel = condel.firstChildElement( QStringLiteral( "rowstyles" ) );
107 QDomNodeList nodelist = rowstylesel.toElement().elementsByTagName( QStringLiteral( "style" ) );
108 for ( int i = 0; i < nodelist.count(); i++ )
109 {
110 const QDomElement styleElm = nodelist.at( i ).toElement();
112 style.readXml( styleElm, context );
113 mRowStyles.append( style );
114 }
115
116 const QDomElement fieldstylesel = condel.firstChildElement( QStringLiteral( "fieldstyles" ) );
117 nodelist = fieldstylesel.toElement().elementsByTagName( QStringLiteral( "fieldstyle" ) );
118 QList<QgsConditionalStyle> styles;
119 for ( int i = 0; i < nodelist.count(); i++ )
120 {
121 styles.clear();
122 const QDomElement fieldel = nodelist.at( i ).toElement();
123 const QString fieldName = fieldel.attribute( QStringLiteral( "fieldname" ) );
124 const QDomNodeList stylenodelist = fieldel.toElement().elementsByTagName( QStringLiteral( "style" ) );
125 styles.reserve( stylenodelist.count() );
126 for ( int j = 0; j < stylenodelist.count(); j++ )
127 {
128 const QDomElement styleElm = stylenodelist.at( j ).toElement();
130 style.readXml( styleElm, context );
131 styles.append( style );
132 }
133 mFieldStyles.insert( fieldName, styles );
134 }
135
136 return true;
137}
138
140{}
141
143{
144 setRule( rule );
145}
146
148
150 : mValid( other.mValid )
151 , mName( other.mName )
152 , mRule( other.mRule )
153 , mFont( other.mFont )
154 , mBackColor( other.mBackColor )
155 , mTextColor( other.mTextColor )
156 , mIcon( other.mIcon )
157{
158 if ( other.mSymbol )
159 mSymbol.reset( other.mSymbol->clone() );
160}
161
163{
164 mValid = other.mValid;
165 mRule = other.mRule;
166 mFont = other.mFont;
167 mBackColor = other.mBackColor;
168 mTextColor = other.mTextColor;
169 mIcon = other.mIcon;
170 mName = other.mName;
171 if ( other.mSymbol )
172 {
173 mSymbol.reset( other.mSymbol->clone() );
174 }
175 else
176 {
177 mSymbol.reset();
178 }
179 return ( *this );
180}
181
183{
184 if ( name().isEmpty() )
185 return rule();
186 else
187 return QStringLiteral( "%1 \n%2" ).arg( name(), rule() );
188}
189
191{
192 mValid = true;
193 if ( value )
194 {
195 mSymbol.reset( value->clone() );
196 mIcon = QgsSymbolLayerUtils::symbolPreviewPixmap( mSymbol.get(), QSize( 16, 16 ) );
197 }
198 else
199 {
200 mSymbol.reset();
201 }
202}
203
204bool QgsConditionalStyle::matches( const QVariant &value, QgsExpressionContext &context ) const
205{
206 QgsExpression exp( mRule );
207 context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "value" ), value, true ) );
208 return exp.evaluate( &context ).toBool();
209}
210
211QPixmap QgsConditionalStyle::renderPreview( const QSize &size ) const
212{
213 QPixmap pixmap( size.isValid() ? size.width() : 64, size.isValid() ? size.height() : 32 );
214 pixmap.fill( Qt::transparent );
215
216 QPainter painter( &pixmap );
217
218 if ( validBackgroundColor() )
219 painter.setBrush( mBackColor );
220
221 QRect rect = QRect( 0, 0, pixmap.width(), pixmap.height() );
222 painter.setPen( Qt::NoPen );
223 painter.drawRect( rect );
224 const QPixmap symbolIcon = icon();
225 if ( !symbolIcon.isNull() )
226 {
227 painter.drawPixmap( ( pixmap.width() / 3 - symbolIcon.width() ) / 2, ( pixmap.height() - symbolIcon.height() ) / 2, symbolIcon );
228 }
229
230 if ( validTextColor() )
231 painter.setPen( mTextColor );
232 else
233 painter.setPen( Qt::black );
234
235 painter.setRenderHint( QPainter::Antialiasing );
236 painter.setFont( font() );
237 rect = QRect( pixmap.width() / 3, 0, 2 * pixmap.width() / 3, pixmap.height() );
238 painter.drawText( rect, Qt::AlignCenter, QStringLiteral( "abc\n123" ) );
239 painter.end();
240 return pixmap;
241}
242
244{
245 return ( backgroundColor().isValid() && backgroundColor().alpha() != 0 );
246}
247
249{
250 return ( textColor().isValid() && textColor().alpha() != 0 );
251}
252
253QList<QgsConditionalStyle> QgsConditionalStyle::matchingConditionalStyles( const QList<QgsConditionalStyle> &styles, const QVariant &value, QgsExpressionContext &context )
254{
255 QList<QgsConditionalStyle> matchingstyles;
256 const auto constStyles = styles;
257 for ( const QgsConditionalStyle &style : constStyles )
258 {
259 if ( style.matches( value, context ) )
260 matchingstyles.append( style );
261 }
262 return matchingstyles;
263}
264
265QgsConditionalStyle QgsConditionalStyle::matchingConditionalStyle( const QList<QgsConditionalStyle> &styles, const QVariant &value, QgsExpressionContext &context )
266{
267 const auto constStyles = styles;
268 for ( const QgsConditionalStyle &style : constStyles )
269 {
270 if ( style.matches( value, context ) )
271 return style;
272 }
273 return QgsConditionalStyle();
274}
275
276QgsConditionalStyle QgsConditionalStyle::compressStyles( const QList<QgsConditionalStyle> &styles )
277{
279 for ( const QgsConditionalStyle &s : styles )
280 {
281 if ( !s.isValid() )
282 continue;
283
284 style.setFont( s.font() );
285 if ( s.backgroundColor().isValid() && s.backgroundColor().alpha() != 0 )
286 style.setBackgroundColor( s.backgroundColor() );
287 if ( s.textColor().isValid() && s.textColor().alpha() != 0 )
288 style.setTextColor( s.textColor() );
289 if ( auto *lSymbol = s.symbol() )
290 style.setSymbol( lSymbol );
291 }
292 return style;
293}
294
295bool QgsConditionalStyle::writeXml( QDomNode &node, QDomDocument &doc, const QgsReadWriteContext &context ) const
296{
297 QDomElement stylesel = doc.createElement( QStringLiteral( "style" ) );
298 stylesel.setAttribute( QStringLiteral( "rule" ), mRule );
299 stylesel.setAttribute( QStringLiteral( "name" ), mName );
300 if ( mBackColor.isValid() )
301 {
302 stylesel.setAttribute( QStringLiteral( "background_color" ), mBackColor.name() );
303 stylesel.setAttribute( QStringLiteral( "background_color_alpha" ), mBackColor.alpha() );
304 }
305 if ( mTextColor.isValid() )
306 {
307 stylesel.setAttribute( QStringLiteral( "text_color" ), mTextColor.name() );
308 stylesel.setAttribute( QStringLiteral( "text_color_alpha" ), mTextColor.alpha() );
309 }
310 const QDomElement labelFontElem = QgsFontUtils::toXmlElement( mFont, doc, QStringLiteral( "font" ) );
311 stylesel.appendChild( labelFontElem );
312 if ( mSymbol )
313 {
314 const QDomElement symbolElm = QgsSymbolLayerUtils::saveSymbol( QStringLiteral( "icon" ), mSymbol.get(), doc, context );
315 stylesel.appendChild( symbolElm );
316 }
317 node.appendChild( stylesel );
318 return true;
319}
320
322{
323 return mValid == other.mValid
324 && mName == other.mName
325 && mRule == other.mRule
326 && mFont == other.mFont
327 && mBackColor == other.mBackColor
328 && mTextColor == other.mTextColor
329 && static_cast< bool >( mSymbol ) == static_cast< bool >( other.mSymbol )
330 && ( ! mSymbol || QgsSymbolLayerUtils::symbolProperties( mSymbol.get() ) == QgsSymbolLayerUtils::symbolProperties( other.mSymbol.get() ) );
331}
332
334{
335 return !( *this == other );
336}
337
338bool QgsConditionalStyle::readXml( const QDomNode &node, const QgsReadWriteContext &context )
339{
340 const QDomElement styleElm = node.toElement();
341 setRule( styleElm.attribute( QStringLiteral( "rule" ) ) );
342 setName( styleElm.attribute( QStringLiteral( "name" ) ) );
343 if ( styleElm.hasAttribute( QStringLiteral( "background_color" ) ) )
344 {
345 QColor bColor = QColor( styleElm.attribute( QStringLiteral( "background_color" ) ) );
346 if ( styleElm.hasAttribute( QStringLiteral( "background_color_alpha" ) ) )
347 {
348 bColor.setAlpha( styleElm.attribute( QStringLiteral( "background_color_alpha" ) ).toInt() );
349 }
350 if ( bColor.alpha() == 0 )
351 setBackgroundColor( QColor() );
352 else
353 setBackgroundColor( bColor );
354 }
355 else
356 {
357 setBackgroundColor( QColor() );
358 }
359 if ( styleElm.hasAttribute( QStringLiteral( "text_color" ) ) )
360 {
361 QColor tColor = QColor( styleElm.attribute( QStringLiteral( "text_color" ) ) );
362 if ( styleElm.hasAttribute( QStringLiteral( "text_color_alpha" ) ) )
363 {
364 tColor.setAlpha( styleElm.attribute( QStringLiteral( "text_color_alpha" ) ).toInt() );
365 }
366 if ( tColor.alpha() == 0 )
367 setTextColor( QColor() );
368 else
369 setTextColor( tColor );
370 }
371 else
372 {
373 setTextColor( QColor() );
374 }
375 QgsFontUtils::setFromXmlChildNode( mFont, styleElm, QStringLiteral( "font" ) );
376 const QDomElement symbolElm = styleElm.firstChildElement( QStringLiteral( "symbol" ) );
377 if ( !symbolElm.isNull() )
378 {
379 QgsSymbol *symbol = QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( symbolElm, context );
380 setSymbol( symbol );
381 }
382 return true;
383}
384
void changed()
Emitted when the conditional styles are changed.
bool readXml(const QDomNode &node, const QgsReadWriteContext &context)
Reads the condition styles state from a DOM node.
QgsConditionalStyles rowStyles() const
Returns a list of row styles associated with the layer.
QgsConditionalLayerStyles(QObject *parent=nullptr)
Constructor for QgsConditionalLayerStyles, with the specified parent object.
bool writeXml(QDomNode &node, QDomDocument &doc, const QgsReadWriteContext &context) const
Writes the condition styles state to a DOM node.
QList< QgsConditionalStyle > fieldStyles(const QString &fieldName) const
Returns the conditional styles set for the field with matching fieldName.
bool rulesNeedGeometry() const
Returns true if at least one rule needs geometry.
void setFieldStyles(const QString &fieldName, const QList< QgsConditionalStyle > &styles)
Set the conditional styles for a field, with the specified fieldName.
void setRowStyles(const QgsConditionalStyles &styles)
Sets the conditional styles that apply to full rows of data in the attribute table.
Conditional styling for a rule.
bool readXml(const QDomNode &node, const QgsReadWriteContext &context)
Reads vector conditional style specific state from layer Dom node.
bool operator==(const QgsConditionalStyle &other) const
QString displayText() const
The name of the style.
QString name() const
The name of the style.
static QgsConditionalStyle compressStyles(const QList< QgsConditionalStyle > &styles)
Compress a list of styles into a single style.
bool operator!=(const QgsConditionalStyle &other) const
void setSymbol(QgsSymbol *value)
Set the icon for the style.
void setName(const QString &value)
Set the name of the style.
QPixmap renderPreview(const QSize &size=QSize()) const
Render a preview icon of the rule, at the specified size.
bool matches(const QVariant &value, QgsExpressionContext &context) const
Check if the rule matches using the given value and feature.
QgsConditionalStyle & operator=(const QgsConditionalStyle &other)
void setTextColor(const QColor &value)
Set the text color for the style.
static QList< QgsConditionalStyle > matchingConditionalStyles(const QList< QgsConditionalStyle > &styles, const QVariant &value, QgsExpressionContext &context)
Find and return the matching styles for the value and feature.
void setRule(const QString &value)
Set the rule for the style.
void setBackgroundColor(const QColor &value)
Set the background color for the style.
void setFont(const QFont &value)
Set the font for the style.
QColor backgroundColor() const
The background color for style.
QColor textColor() const
The text color set for style.
QString rule() const
The condition rule set for the style.
QFont font() const
The font for the style.
bool writeXml(QDomNode &node, QDomDocument &doc, const QgsReadWriteContext &context) const
Write vector conditional style specific state from layer Dom node.
bool validTextColor() const
Check if the text color is valid for render.
QgsSymbol * symbol() const
The symbol used to generate the icon for the style.
bool isValid() const
isValid Check if this rule is valid.
static QgsConditionalStyle matchingConditionalStyle(const QList< QgsConditionalStyle > &styles, const QVariant &value, QgsExpressionContext &context)
Find and return the matching style for the value and feature.
QPixmap icon() const
The icon set for style generated from the set symbol.
bool validBackgroundColor() const
Check if the background color is valid for render.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsExpressionContextScope * lastScope()
Returns the last scope added to the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QVariant evaluate()
Evaluate the feature and return the result.
static bool setFromXmlChildNode(QFont &font, const QDomElement &element, const QString &childNode)
Sets the properties of a font to match the properties stored in an XML child node.
static QDomElement toXmlElement(const QFont &font, QDomDocument &document, const QString &elementName)
Returns a DOM element containing the properties of the font.
The class is used as a container of context for various read/write operations on other objects.
static QPixmap symbolPreviewPixmap(const QgsSymbol *symbol, QSize size, int padding=0, QgsRenderContext *customContext=nullptr, bool selected=false, const QgsExpressionContext *expressionContext=nullptr, const QgsLegendPatchShape *shape=nullptr)
Returns a pixmap preview for a color ramp.
static QDomElement saveSymbol(const QString &symbolName, const QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
static QString symbolProperties(QgsSymbol *symbol)
Returns a string representing the symbol.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:93
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
QList< QgsConditionalStyle > QgsConditionalStyles
Single variable definition for use within a QgsExpressionContextScope.