QGIS API Documentation 3.41.0-Master (45a0abf3bec)
Loading...
Searching...
No Matches
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 "moc_qgsconditionalstyle.cpp"
19#include "qgsexpression.h"
20#include "qgsfontutils.h"
21#include "qgssymbollayerutils.h"
23#include "qgsmarkersymbol.h"
24
26 : QObject( parent )
27{
28}
29
31{
32 switch ( strength )
33 {
35 {
36 QgsConditionalStyle hardConstraintFailureStyle;
37 hardConstraintFailureStyle.setBackgroundColor( QColor( 255, 152, 0 ) );
38 hardConstraintFailureStyle.setTextColor( QColor( 0, 0, 0 ) );
39 return hardConstraintFailureStyle;
40 }
41
43 {
44 QgsConditionalStyle softConstraintFailureStyle;
45 softConstraintFailureStyle.setBackgroundColor( QColor( 255, 191, 12 ) );
46 softConstraintFailureStyle.setTextColor( QColor( 0, 0, 0 ) );
47 return softConstraintFailureStyle;
48 }
49
51 return QgsConditionalStyle();
52 }
53
54 return QgsConditionalStyle();
55}
56
58{
59 return mRowStyles;
60}
61
63{
64 if ( styles == mRowStyles )
65 return;
66
67 mRowStyles = styles;
68 emit changed();
69}
70
71void QgsConditionalLayerStyles::setFieldStyles( const QString &fieldName, const QList<QgsConditionalStyle> &styles )
72{
73 if ( mFieldStyles.value( fieldName ) == styles )
74 return;
75
76 mFieldStyles.insert( fieldName, styles );
77 emit changed();
78}
79
80QList<QgsConditionalStyle> QgsConditionalLayerStyles::fieldStyles( const QString &fieldName ) const
81{
82 return mFieldStyles.value( fieldName );
83}
84
85bool QgsConditionalLayerStyles::writeXml( QDomNode &node, QDomDocument &doc, const QgsReadWriteContext &context ) const
86{
87 QDomElement stylesel = doc.createElement( QStringLiteral( "conditionalstyles" ) );
88
89 QDomElement rowel = doc.createElement( QStringLiteral( "rowstyles" ) );
90 const auto constMRowStyles = mRowStyles;
91 for ( const QgsConditionalStyle &style : constMRowStyles )
92 {
93 style.writeXml( rowel, doc, context );
94 }
95 stylesel.appendChild( rowel );
96
97 QDomElement fieldsel = doc.createElement( QStringLiteral( "fieldstyles" ) );
98 QHash<QString, QgsConditionalStyles>::const_iterator it = mFieldStyles.constBegin();
99 for ( ; it != mFieldStyles.constEnd(); ++it )
100 {
101 QDomElement fieldel = doc.createElement( QStringLiteral( "fieldstyle" ) );
102 fieldel.setAttribute( QStringLiteral( "fieldname" ), it.key() );
103 const QgsConditionalStyles styles = it.value();
104 const auto constStyles = styles;
105 for ( const QgsConditionalStyle &style : constStyles )
106 {
107 style.writeXml( fieldel, doc, context );
108 }
109 fieldsel.appendChild( fieldel );
110 }
111 stylesel.appendChild( fieldsel );
112
113 node.appendChild( stylesel );
114 return true;
115}
116
118{
119 for ( const QgsConditionalStyle &style : std::as_const( mRowStyles ) )
120 {
121 if ( QgsExpression( style.rule() ).needsGeometry() )
122 {
123 return true;
124 }
125 }
126 return false;
127}
128
129bool QgsConditionalLayerStyles::readXml( const QDomNode &node, const QgsReadWriteContext &context )
130{
131 mRowStyles.clear();
132 mFieldStyles.clear();
133
134 const QDomElement condel = node.firstChildElement( QStringLiteral( "conditionalstyles" ) );
135
136 const QDomElement rowstylesel = condel.firstChildElement( QStringLiteral( "rowstyles" ) );
137 QDomNodeList nodelist = rowstylesel.toElement().elementsByTagName( QStringLiteral( "style" ) );
138 for ( int i = 0; i < nodelist.count(); i++ )
139 {
140 const QDomElement styleElm = nodelist.at( i ).toElement();
142 style.readXml( styleElm, context );
143 mRowStyles.append( style );
144 }
145
146 const QDomElement fieldstylesel = condel.firstChildElement( QStringLiteral( "fieldstyles" ) );
147 nodelist = fieldstylesel.toElement().elementsByTagName( QStringLiteral( "fieldstyle" ) );
148 QList<QgsConditionalStyle> styles;
149 for ( int i = 0; i < nodelist.count(); i++ )
150 {
151 styles.clear();
152 const QDomElement fieldel = nodelist.at( i ).toElement();
153 const QString fieldName = fieldel.attribute( QStringLiteral( "fieldname" ) );
154 const QDomNodeList stylenodelist = fieldel.toElement().elementsByTagName( QStringLiteral( "style" ) );
155 styles.reserve( stylenodelist.count() );
156 for ( int j = 0; j < stylenodelist.count(); j++ )
157 {
158 const QDomElement styleElm = stylenodelist.at( j ).toElement();
160 style.readXml( styleElm, context );
161 styles.append( style );
162 }
163 mFieldStyles.insert( fieldName, styles );
164 }
165
166 return true;
167}
168
171
173{
174 setRule( rule );
175}
176
178
180 : mValid( other.mValid )
181 , mName( other.mName )
182 , mRule( other.mRule )
183 , mFont( other.mFont )
184 , mBackColor( other.mBackColor )
185 , mTextColor( other.mTextColor )
186 , mIcon( other.mIcon )
187{
188 if ( other.mSymbol )
189 mSymbol.reset( other.mSymbol->clone() );
190}
191
193{
194 mValid = other.mValid;
195 mRule = other.mRule;
196 mFont = other.mFont;
197 mBackColor = other.mBackColor;
198 mTextColor = other.mTextColor;
199 mIcon = other.mIcon;
200 mName = other.mName;
201 if ( other.mSymbol )
202 {
203 mSymbol.reset( other.mSymbol->clone() );
204 }
205 else
206 {
207 mSymbol.reset();
208 }
209 return ( *this );
210}
211
213{
214 if ( name().isEmpty() )
215 return rule();
216 else
217 return QStringLiteral( "%1 \n%2" ).arg( name(), rule() );
218}
219
221{
222 mValid = true;
223 if ( value )
224 {
225 mSymbol.reset( value->clone() );
226 mIcon = QgsSymbolLayerUtils::symbolPreviewPixmap( mSymbol.get(), QSize( 16, 16 ) );
227 }
228 else
229 {
230 mSymbol.reset();
231 }
232}
233
234bool QgsConditionalStyle::matches( const QVariant &value, QgsExpressionContext &context ) const
235{
236 QgsExpression exp( mRule );
237 context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "value" ), value, true ) );
238 return exp.evaluate( &context ).toBool();
239}
240
241QPixmap QgsConditionalStyle::renderPreview( const QSize &size ) const
242{
243 QPixmap pixmap( size.isValid() ? size.width() : 64, size.isValid() ? size.height() : 32 );
244 pixmap.fill( Qt::transparent );
245
246 QPainter painter( &pixmap );
247
248 if ( validBackgroundColor() )
249 painter.setBrush( mBackColor );
250
251 QRect rect = QRect( 0, 0, pixmap.width(), pixmap.height() );
252 painter.setPen( Qt::NoPen );
253 painter.drawRect( rect );
254 const QPixmap symbolIcon = icon();
255 if ( !symbolIcon.isNull() )
256 {
257 painter.drawPixmap( ( pixmap.width() / 3 - symbolIcon.width() ) / 2, ( pixmap.height() - symbolIcon.height() ) / 2, symbolIcon );
258 }
259
260 if ( validTextColor() )
261 painter.setPen( mTextColor );
262 else
263 painter.setPen( Qt::black );
264
265 painter.setRenderHint( QPainter::Antialiasing );
266 painter.setFont( font() );
267 rect = QRect( pixmap.width() / 3, 0, 2 * pixmap.width() / 3, pixmap.height() );
268 painter.drawText( rect, Qt::AlignCenter, QStringLiteral( "abc\n123" ) );
269 painter.end();
270 return pixmap;
271}
272
274{
275 return ( backgroundColor().isValid() && backgroundColor().alpha() != 0 );
276}
277
279{
280 return ( textColor().isValid() && textColor().alpha() != 0 );
281}
282
283QList<QgsConditionalStyle> QgsConditionalStyle::matchingConditionalStyles( const QList<QgsConditionalStyle> &styles, const QVariant &value, QgsExpressionContext &context )
284{
285 QList<QgsConditionalStyle> matchingstyles;
286 const auto constStyles = styles;
287 for ( const QgsConditionalStyle &style : constStyles )
288 {
289 if ( style.matches( value, context ) )
290 matchingstyles.append( style );
291 }
292 return matchingstyles;
293}
294
295QgsConditionalStyle QgsConditionalStyle::matchingConditionalStyle( const QList<QgsConditionalStyle> &styles, const QVariant &value, QgsExpressionContext &context )
296{
297 const auto constStyles = styles;
298 for ( const QgsConditionalStyle &style : constStyles )
299 {
300 if ( style.matches( value, context ) )
301 return style;
302 }
303 return QgsConditionalStyle();
304}
305
306QgsConditionalStyle QgsConditionalStyle::compressStyles( const QList<QgsConditionalStyle> &styles )
307{
309 for ( const QgsConditionalStyle &s : styles )
310 {
311 if ( !s.isValid() )
312 continue;
313
314 style.setFont( s.font() );
315 if ( s.backgroundColor().isValid() && s.backgroundColor().alpha() != 0 )
316 style.setBackgroundColor( s.backgroundColor() );
317 if ( s.textColor().isValid() && s.textColor().alpha() != 0 )
318 style.setTextColor( s.textColor() );
319 if ( auto *lSymbol = s.symbol() )
320 style.setSymbol( lSymbol );
321 }
322 return style;
323}
324
325bool QgsConditionalStyle::writeXml( QDomNode &node, QDomDocument &doc, const QgsReadWriteContext &context ) const
326{
327 QDomElement stylesel = doc.createElement( QStringLiteral( "style" ) );
328 stylesel.setAttribute( QStringLiteral( "rule" ), mRule );
329 stylesel.setAttribute( QStringLiteral( "name" ), mName );
330 if ( mBackColor.isValid() )
331 {
332 stylesel.setAttribute( QStringLiteral( "background_color" ), mBackColor.name() );
333 stylesel.setAttribute( QStringLiteral( "background_color_alpha" ), mBackColor.alpha() );
334 }
335 if ( mTextColor.isValid() )
336 {
337 stylesel.setAttribute( QStringLiteral( "text_color" ), mTextColor.name() );
338 stylesel.setAttribute( QStringLiteral( "text_color_alpha" ), mTextColor.alpha() );
339 }
340 const QDomElement labelFontElem = QgsFontUtils::toXmlElement( mFont, doc, QStringLiteral( "font" ) );
341 stylesel.appendChild( labelFontElem );
342 if ( mSymbol )
343 {
344 const QDomElement symbolElm = QgsSymbolLayerUtils::saveSymbol( QStringLiteral( "icon" ), mSymbol.get(), doc, context );
345 stylesel.appendChild( symbolElm );
346 }
347 node.appendChild( stylesel );
348 return true;
349}
350
352{
353 return mValid == other.mValid
354 && mName == other.mName
355 && mRule == other.mRule
356 && mFont == other.mFont
357 && mBackColor == other.mBackColor
358 && mTextColor == other.mTextColor
359 && static_cast< bool >( mSymbol ) == static_cast< bool >( other.mSymbol )
360 && ( ! mSymbol || QgsSymbolLayerUtils::symbolProperties( mSymbol.get() ) == QgsSymbolLayerUtils::symbolProperties( other.mSymbol.get() ) );
361}
362
364{
365 return !( *this == other );
366}
367
368bool QgsConditionalStyle::readXml( const QDomNode &node, const QgsReadWriteContext &context )
369{
370 const QDomElement styleElm = node.toElement();
371 setRule( styleElm.attribute( QStringLiteral( "rule" ) ) );
372 setName( styleElm.attribute( QStringLiteral( "name" ) ) );
373 if ( styleElm.hasAttribute( QStringLiteral( "background_color" ) ) )
374 {
375 QColor bColor = QColor( styleElm.attribute( QStringLiteral( "background_color" ) ) );
376 if ( styleElm.hasAttribute( QStringLiteral( "background_color_alpha" ) ) )
377 {
378 bColor.setAlpha( styleElm.attribute( QStringLiteral( "background_color_alpha" ) ).toInt() );
379 }
380 if ( bColor.alpha() == 0 )
381 setBackgroundColor( QColor() );
382 else
383 setBackgroundColor( bColor );
384 }
385 else
386 {
387 setBackgroundColor( QColor() );
388 }
389 if ( styleElm.hasAttribute( QStringLiteral( "text_color" ) ) )
390 {
391 QColor tColor = QColor( styleElm.attribute( QStringLiteral( "text_color" ) ) );
392 if ( styleElm.hasAttribute( QStringLiteral( "text_color_alpha" ) ) )
393 {
394 tColor.setAlpha( styleElm.attribute( QStringLiteral( "text_color_alpha" ) ).toInt() );
395 }
396 if ( tColor.alpha() == 0 )
397 setTextColor( QColor() );
398 else
399 setTextColor( tColor );
400 }
401 else
402 {
403 setTextColor( QColor() );
404 }
405 QgsFontUtils::setFromXmlChildNode( mFont, styleElm, QStringLiteral( "font" ) );
406 const QDomElement symbolElm = styleElm.firstChildElement( QStringLiteral( "symbol" ) );
407 if ( !symbolElm.isNull() )
408 {
409 QgsSymbol *symbol = QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( symbolElm, context );
410 setSymbol( symbol );
411 }
412 return true;
413}
414
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.
QgsConditionalStyle constraintFailureStyles(QgsFieldConstraints::ConstraintStrength strength)
Returns a style associated to a constraint failure.
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.
ConstraintStrength
Strength of constraints.
@ ConstraintStrengthNotSet
Constraint is not set.
@ ConstraintStrengthSoft
User is warned if constraint is violated but feature can still be accepted.
@ ConstraintStrengthHard
Constraint must be honored before feature can be accepted.
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, const QgsScreenProperties &screen=QgsScreenProperties())
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:231
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
QList< QgsConditionalStyle > QgsConditionalStyles
Single variable definition for use within a QgsExpressionContextScope.