QGIS API Documentation 3.27.0-Master (f261cc1f8b)
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
89bool QgsConditionalLayerStyles::readXml( const QDomNode &node, const QgsReadWriteContext &context )
90{
91 const QDomElement condel = node.firstChildElement( QStringLiteral( "conditionalstyles" ) );
92 mRowStyles.clear();
93 mFieldStyles.clear();
94 const QDomElement rowstylesel = condel.firstChildElement( QStringLiteral( "rowstyles" ) );
95 QDomNodeList nodelist = rowstylesel.toElement().elementsByTagName( QStringLiteral( "style" ) );
96 for ( int i = 0; i < nodelist.count(); i++ )
97 {
98 const QDomElement styleElm = nodelist.at( i ).toElement();
100 style.readXml( styleElm, context );
101 mRowStyles.append( style );
102 }
103
104 const QDomElement fieldstylesel = condel.firstChildElement( QStringLiteral( "fieldstyles" ) );
105 nodelist = fieldstylesel.toElement().elementsByTagName( QStringLiteral( "fieldstyle" ) );
106 QList<QgsConditionalStyle> styles;
107 for ( int i = 0; i < nodelist.count(); i++ )
108 {
109 styles.clear();
110 const QDomElement fieldel = nodelist.at( i ).toElement();
111 const QString fieldName = fieldel.attribute( QStringLiteral( "fieldname" ) );
112 const QDomNodeList stylenodelist = fieldel.toElement().elementsByTagName( QStringLiteral( "style" ) );
113 styles.reserve( stylenodelist.count() );
114 for ( int j = 0; j < stylenodelist.count(); j++ )
115 {
116 const QDomElement styleElm = stylenodelist.at( j ).toElement();
118 style.readXml( styleElm, context );
119 styles.append( style );
120 }
121 mFieldStyles.insert( fieldName, styles );
122 }
123
124 return true;
125}
126
128{}
129
131{
132 setRule( rule );
133}
134
136
138 : mValid( other.mValid )
139 , mName( other.mName )
140 , mRule( other.mRule )
141 , mFont( other.mFont )
142 , mBackColor( other.mBackColor )
143 , mTextColor( other.mTextColor )
144 , mIcon( other.mIcon )
145{
146 if ( other.mSymbol )
147 mSymbol.reset( other.mSymbol->clone() );
148}
149
151{
152 mValid = other.mValid;
153 mRule = other.mRule;
154 mFont = other.mFont;
155 mBackColor = other.mBackColor;
156 mTextColor = other.mTextColor;
157 mIcon = other.mIcon;
158 mName = other.mName;
159 if ( other.mSymbol )
160 {
161 mSymbol.reset( other.mSymbol->clone() );
162 }
163 else
164 {
165 mSymbol.reset();
166 }
167 return ( *this );
168}
169
171{
172 if ( name().isEmpty() )
173 return rule();
174 else
175 return QStringLiteral( "%1 \n%2" ).arg( name(), rule() );
176}
177
179{
180 mValid = true;
181 if ( value )
182 {
183 mSymbol.reset( value->clone() );
184 mIcon = QgsSymbolLayerUtils::symbolPreviewPixmap( mSymbol.get(), QSize( 16, 16 ) );
185 }
186 else
187 {
188 mSymbol.reset();
189 }
190}
191
192bool QgsConditionalStyle::matches( const QVariant &value, QgsExpressionContext &context ) const
193{
194 QgsExpression exp( mRule );
195 context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "value" ), value, true ) );
196 return exp.evaluate( &context ).toBool();
197}
198
199QPixmap QgsConditionalStyle::renderPreview( const QSize &size ) const
200{
201 QPixmap pixmap( size.isValid() ? size.width() : 64, size.isValid() ? size.height() : 32 );
202 pixmap.fill( Qt::transparent );
203
204 QPainter painter( &pixmap );
205
206 if ( validBackgroundColor() )
207 painter.setBrush( mBackColor );
208
209 QRect rect = QRect( 0, 0, pixmap.width(), pixmap.height() );
210 painter.setPen( Qt::NoPen );
211 painter.drawRect( rect );
212 const QPixmap symbolIcon = icon();
213 if ( !symbolIcon.isNull() )
214 {
215 painter.drawPixmap( ( pixmap.width() / 3 - symbolIcon.width() ) / 2, ( pixmap.height() - symbolIcon.height() ) / 2, symbolIcon );
216 }
217
218 if ( validTextColor() )
219 painter.setPen( mTextColor );
220 else
221 painter.setPen( Qt::black );
222
223 painter.setRenderHint( QPainter::Antialiasing );
224 painter.setFont( font() );
225 rect = QRect( pixmap.width() / 3, 0, 2 * pixmap.width() / 3, pixmap.height() );
226 painter.drawText( rect, Qt::AlignCenter, QStringLiteral( "abc\n123" ) );
227 painter.end();
228 return pixmap;
229}
230
232{
233 return ( backgroundColor().isValid() && backgroundColor().alpha() != 0 );
234}
235
237{
238 return ( textColor().isValid() && textColor().alpha() != 0 );
239}
240
241QList<QgsConditionalStyle> QgsConditionalStyle::matchingConditionalStyles( const QList<QgsConditionalStyle> &styles, const QVariant &value, QgsExpressionContext &context )
242{
243 QList<QgsConditionalStyle> matchingstyles;
244 const auto constStyles = styles;
245 for ( const QgsConditionalStyle &style : constStyles )
246 {
247 if ( style.matches( value, context ) )
248 matchingstyles.append( style );
249 }
250 return matchingstyles;
251}
252
253QgsConditionalStyle QgsConditionalStyle::matchingConditionalStyle( const QList<QgsConditionalStyle> &styles, const QVariant &value, QgsExpressionContext &context )
254{
255 const auto constStyles = styles;
256 for ( const QgsConditionalStyle &style : constStyles )
257 {
258 if ( style.matches( value, context ) )
259 return style;
260 }
261 return QgsConditionalStyle();
262}
263
264QgsConditionalStyle QgsConditionalStyle::compressStyles( const QList<QgsConditionalStyle> &styles )
265{
267 for ( const QgsConditionalStyle &s : styles )
268 {
269 if ( !s.isValid() )
270 continue;
271
272 style.setFont( s.font() );
273 if ( s.backgroundColor().isValid() && s.backgroundColor().alpha() != 0 )
274 style.setBackgroundColor( s.backgroundColor() );
275 if ( s.textColor().isValid() && s.textColor().alpha() != 0 )
276 style.setTextColor( s.textColor() );
277 if ( auto *lSymbol = s.symbol() )
278 style.setSymbol( lSymbol );
279 }
280 return style;
281}
282
283bool QgsConditionalStyle::writeXml( QDomNode &node, QDomDocument &doc, const QgsReadWriteContext &context ) const
284{
285 QDomElement stylesel = doc.createElement( QStringLiteral( "style" ) );
286 stylesel.setAttribute( QStringLiteral( "rule" ), mRule );
287 stylesel.setAttribute( QStringLiteral( "name" ), mName );
288 if ( mBackColor.isValid() )
289 {
290 stylesel.setAttribute( QStringLiteral( "background_color" ), mBackColor.name() );
291 stylesel.setAttribute( QStringLiteral( "background_color_alpha" ), mBackColor.alpha() );
292 }
293 if ( mTextColor.isValid() )
294 {
295 stylesel.setAttribute( QStringLiteral( "text_color" ), mTextColor.name() );
296 stylesel.setAttribute( QStringLiteral( "text_color_alpha" ), mTextColor.alpha() );
297 }
298 const QDomElement labelFontElem = QgsFontUtils::toXmlElement( mFont, doc, QStringLiteral( "font" ) );
299 stylesel.appendChild( labelFontElem );
300 if ( mSymbol )
301 {
302 const QDomElement symbolElm = QgsSymbolLayerUtils::saveSymbol( QStringLiteral( "icon" ), mSymbol.get(), doc, context );
303 stylesel.appendChild( symbolElm );
304 }
305 node.appendChild( stylesel );
306 return true;
307}
308
310{
311 return mValid == other.mValid
312 && mName == other.mName
313 && mRule == other.mRule
314 && mFont == other.mFont
315 && mBackColor == other.mBackColor
316 && mTextColor == other.mTextColor
317 && static_cast< bool >( mSymbol ) == static_cast< bool >( other.mSymbol )
318 && ( ! mSymbol || QgsSymbolLayerUtils::symbolProperties( mSymbol.get() ) == QgsSymbolLayerUtils::symbolProperties( other.mSymbol.get() ) );
319}
320
322{
323 return !( *this == other );
324}
325
326bool QgsConditionalStyle::readXml( const QDomNode &node, const QgsReadWriteContext &context )
327{
328 const QDomElement styleElm = node.toElement();
329 setRule( styleElm.attribute( QStringLiteral( "rule" ) ) );
330 setName( styleElm.attribute( QStringLiteral( "name" ) ) );
331 if ( styleElm.hasAttribute( QStringLiteral( "background_color" ) ) )
332 {
333 QColor bColor = QColor( styleElm.attribute( QStringLiteral( "background_color" ) ) );
334 if ( styleElm.hasAttribute( QStringLiteral( "background_color_alpha" ) ) )
335 {
336 bColor.setAlpha( styleElm.attribute( QStringLiteral( "background_color_alpha" ) ).toInt() );
337 }
338 if ( bColor.alpha() == 0 )
339 setBackgroundColor( QColor() );
340 else
341 setBackgroundColor( bColor );
342 }
343 else
344 {
345 setBackgroundColor( QColor() );
346 }
347 if ( styleElm.hasAttribute( QStringLiteral( "text_color" ) ) )
348 {
349 QColor tColor = QColor( styleElm.attribute( QStringLiteral( "text_color" ) ) );
350 if ( styleElm.hasAttribute( QStringLiteral( "text_color_alpha" ) ) )
351 {
352 tColor.setAlpha( styleElm.attribute( QStringLiteral( "text_color_alpha" ) ).toInt() );
353 }
354 if ( tColor.alpha() == 0 )
355 setTextColor( QColor() );
356 else
357 setTextColor( tColor );
358 }
359 else
360 {
361 setTextColor( QColor() );
362 }
363 QgsFontUtils::setFromXmlChildNode( mFont, styleElm, QStringLiteral( "font" ) );
364 const QDomElement symbolElm = styleElm.firstChildElement( QStringLiteral( "symbol" ) );
365 if ( !symbolElm.isNull() )
366 {
367 QgsSymbol *symbol = QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( symbolElm, context );
368 setSymbol( symbol );
369 }
370 return true;
371}
372
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.
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").
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.