QGIS API Documentation 3.99.0-Master (2fe06baccd8)
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 "qgsconditionalstyle.h"
16
17#include "qgsexpression.h"
18#include "qgsfontutils.h"
19#include "qgsmarkersymbol.h"
21#include "qgssymbollayerutils.h"
22
23#include <QPainter>
24
25#include "moc_qgsconditionalstyle.cpp"
26
28 : QObject( parent )
29{
30}
31
33{
34 switch ( strength )
35 {
37 {
38 QgsConditionalStyle hardConstraintFailureStyle;
39 hardConstraintFailureStyle.setBackgroundColor( QColor( 255, 152, 0 ) );
40 hardConstraintFailureStyle.setTextColor( QColor( 0, 0, 0 ) );
41 return hardConstraintFailureStyle;
42 }
43
45 {
46 QgsConditionalStyle softConstraintFailureStyle;
47 softConstraintFailureStyle.setBackgroundColor( QColor( 255, 191, 12 ) );
48 softConstraintFailureStyle.setTextColor( QColor( 0, 0, 0 ) );
49 return softConstraintFailureStyle;
50 }
51
53 return QgsConditionalStyle();
54 }
55
56 return QgsConditionalStyle();
57}
58
60{
61 return mRowStyles;
62}
63
65{
66 if ( styles == mRowStyles )
67 return;
68
69 mRowStyles = styles;
70 emit changed();
71}
72
73void QgsConditionalLayerStyles::setFieldStyles( const QString &fieldName, const QList<QgsConditionalStyle> &styles )
74{
75 if ( mFieldStyles.value( fieldName ) == styles )
76 return;
77
78 mFieldStyles.insert( fieldName, styles );
79 emit changed();
80}
81
82QList<QgsConditionalStyle> QgsConditionalLayerStyles::fieldStyles( const QString &fieldName ) const
83{
84 return mFieldStyles.value( fieldName );
85}
86
87bool QgsConditionalLayerStyles::writeXml( QDomNode &node, QDomDocument &doc, const QgsReadWriteContext &context ) const
88{
89 QDomElement stylesel = doc.createElement( QStringLiteral( "conditionalstyles" ) );
90
91 QDomElement rowel = doc.createElement( QStringLiteral( "rowstyles" ) );
92 const auto constMRowStyles = mRowStyles;
93 for ( const QgsConditionalStyle &style : constMRowStyles )
94 {
95 style.writeXml( rowel, doc, context );
96 }
97 stylesel.appendChild( rowel );
98
99 QDomElement fieldsel = doc.createElement( QStringLiteral( "fieldstyles" ) );
100 QHash<QString, QgsConditionalStyles>::const_iterator it = mFieldStyles.constBegin();
101 for ( ; it != mFieldStyles.constEnd(); ++it )
102 {
103 QDomElement fieldel = doc.createElement( QStringLiteral( "fieldstyle" ) );
104 fieldel.setAttribute( QStringLiteral( "fieldname" ), it.key() );
105 const QgsConditionalStyles styles = it.value();
106 const auto constStyles = styles;
107 for ( const QgsConditionalStyle &style : constStyles )
108 {
109 style.writeXml( fieldel, doc, context );
110 }
111 fieldsel.appendChild( fieldel );
112 }
113 stylesel.appendChild( fieldsel );
114
115 node.appendChild( stylesel );
116 return true;
117}
118
120{
121 for ( const QgsConditionalStyle &style : std::as_const( mRowStyles ) )
122 {
123 if ( QgsExpression( style.rule() ).needsGeometry() )
124 {
125 return true;
126 }
127 }
128 return false;
129}
130
131bool QgsConditionalLayerStyles::readXml( const QDomNode &node, const QgsReadWriteContext &context )
132{
133 mRowStyles.clear();
134 mFieldStyles.clear();
135
136 const QDomElement condel = node.firstChildElement( QStringLiteral( "conditionalstyles" ) );
137
138 const QDomElement rowstylesel = condel.firstChildElement( QStringLiteral( "rowstyles" ) );
139 QDomNodeList nodelist = rowstylesel.toElement().elementsByTagName( QStringLiteral( "style" ) );
140 for ( int i = 0; i < nodelist.count(); i++ )
141 {
142 const QDomElement styleElm = nodelist.at( i ).toElement();
144 style.readXml( styleElm, context );
145 mRowStyles.append( style );
146 }
147
148 const QDomElement fieldstylesel = condel.firstChildElement( QStringLiteral( "fieldstyles" ) );
149 nodelist = fieldstylesel.toElement().elementsByTagName( QStringLiteral( "fieldstyle" ) );
150 QList<QgsConditionalStyle> styles;
151 for ( int i = 0; i < nodelist.count(); i++ )
152 {
153 styles.clear();
154 const QDomElement fieldel = nodelist.at( i ).toElement();
155 const QString fieldName = fieldel.attribute( QStringLiteral( "fieldname" ) );
156 const QDomNodeList stylenodelist = fieldel.toElement().elementsByTagName( QStringLiteral( "style" ) );
157 styles.reserve( stylenodelist.count() );
158 for ( int j = 0; j < stylenodelist.count(); j++ )
159 {
160 const QDomElement styleElm = stylenodelist.at( j ).toElement();
162 style.readXml( styleElm, context );
163 styles.append( style );
164 }
165 mFieldStyles.insert( fieldName, styles );
166 }
167
168 return true;
169}
170
173
175{
176 setRule( rule );
177}
178
180
182//****** IMPORTANT! editing this? make sure you update the move constructor too! *****
183 : mValid( other.mValid )
184 , mName( other.mName )
185 , mRule( other.mRule )
186 , mFont( other.mFont )
187 , mBackColor( other.mBackColor )
188 , mTextColor( other.mTextColor )
189 , mIcon( other.mIcon )
190 //****** IMPORTANT! editing this? make sure you update the move constructor too! *****
191{
192 if ( other.mSymbol )
193 mSymbol.reset( other.mSymbol->clone() );
194}
195
197 : mValid( other.mValid )
198 , mName( std::move( other.mName ) )
199 , mRule( std::move( other.mRule ) )
200 , mSymbol( std::move( other.mSymbol ) )
201 , mFont( std::move( other.mFont ) )
202 , mBackColor( std::move( other.mBackColor ) )
203 , mTextColor( std::move( other.mTextColor ) )
204 , mIcon( std::move( other.mIcon ) )
205{
206
207}
208
210{
211 if ( &other == this )
212 return *this;
213
214 //****** IMPORTANT! editing this? make sure you update the move assignment operator too! *****
215 mValid = other.mValid;
216 mRule = other.mRule;
217 mFont = other.mFont;
218 mBackColor = other.mBackColor;
219 mTextColor = other.mTextColor;
220 mIcon = other.mIcon;
221 mName = other.mName;
222 if ( other.mSymbol )
223 {
224 mSymbol.reset( other.mSymbol->clone() );
225 }
226 else
227 {
228 mSymbol.reset();
229 }
230 //****** IMPORTANT! editing this? make sure you update the move assignment operator too! *****
231 return ( *this );
232}
233
235{
236 if ( &other == this )
237 return *this;
238
239 mValid = other.mValid;
240 mRule = std::move( other.mRule );
241 mFont = std::move( other.mFont );
242 mBackColor = std::move( other.mBackColor );
243 mTextColor = std::move( other.mTextColor );
244 mIcon = std::move( other.mIcon );
245 mName = std::move( other.mName );
246 mSymbol = std::move( other.mSymbol );
247 return ( *this );
248}
249
251{
252 if ( name().isEmpty() )
253 return rule();
254 else
255 return QStringLiteral( "%1 \n%2" ).arg( name(), rule() );
256}
257
259{
260 mValid = true;
261 if ( value )
262 {
263 mSymbol.reset( value->clone() );
264 mIcon = QgsSymbolLayerUtils::symbolPreviewPixmap( mSymbol.get(), QSize( 16, 16 ) );
265 }
266 else
267 {
268 mSymbol.reset();
269 }
270}
271
272bool QgsConditionalStyle::matches( const QVariant &value, QgsExpressionContext &context ) const
273{
274 QgsExpression exp( mRule );
275 context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "value" ), value, true ) );
276 return exp.evaluate( &context ).toBool();
277}
278
279QPixmap QgsConditionalStyle::renderPreview( const QSize &size ) const
280{
281 QPixmap pixmap( size.isValid() ? size.width() : 64, size.isValid() ? size.height() : 32 );
282 pixmap.fill( Qt::transparent );
283
284 QPainter painter( &pixmap );
285
286 if ( validBackgroundColor() )
287 painter.setBrush( mBackColor );
288
289 QRect rect = QRect( 0, 0, pixmap.width(), pixmap.height() );
290 painter.setPen( Qt::NoPen );
291 painter.drawRect( rect );
292 const QPixmap symbolIcon = icon();
293 if ( !symbolIcon.isNull() )
294 {
295 painter.drawPixmap( ( pixmap.width() / 3 - symbolIcon.width() ) / 2, ( pixmap.height() - symbolIcon.height() ) / 2, symbolIcon );
296 }
297
298 if ( validTextColor() )
299 painter.setPen( mTextColor );
300 else
301 painter.setPen( Qt::black );
302
303 painter.setRenderHint( QPainter::Antialiasing );
304 painter.setFont( font() );
305 rect = QRect( pixmap.width() / 3, 0, 2 * pixmap.width() / 3, pixmap.height() );
306 painter.drawText( rect, Qt::AlignCenter, QStringLiteral( "abc\n123" ) );
307 painter.end();
308 return pixmap;
309}
310
312{
313 return ( backgroundColor().isValid() && backgroundColor().alpha() != 0 );
314}
315
317{
318 return ( textColor().isValid() && textColor().alpha() != 0 );
319}
320
321QList<QgsConditionalStyle> QgsConditionalStyle::matchingConditionalStyles( const QList<QgsConditionalStyle> &styles, const QVariant &value, QgsExpressionContext &context )
322{
323 QList<QgsConditionalStyle> matchingstyles;
324 const auto constStyles = styles;
325 for ( const QgsConditionalStyle &style : constStyles )
326 {
327 if ( style.matches( value, context ) )
328 matchingstyles.append( style );
329 }
330 return matchingstyles;
331}
332
333QgsConditionalStyle QgsConditionalStyle::matchingConditionalStyle( const QList<QgsConditionalStyle> &styles, const QVariant &value, QgsExpressionContext &context )
334{
335 const auto constStyles = styles;
336 for ( const QgsConditionalStyle &style : constStyles )
337 {
338 if ( style.matches( value, context ) )
339 return style;
340 }
341 return QgsConditionalStyle();
342}
343
344QgsConditionalStyle QgsConditionalStyle::compressStyles( const QList<QgsConditionalStyle> &styles )
345{
347 for ( const QgsConditionalStyle &s : styles )
348 {
349 if ( !s.isValid() )
350 continue;
351
352 style.setFont( s.font() );
353 if ( s.backgroundColor().isValid() && s.backgroundColor().alpha() != 0 )
354 style.setBackgroundColor( s.backgroundColor() );
355 if ( s.textColor().isValid() && s.textColor().alpha() != 0 )
356 style.setTextColor( s.textColor() );
357 if ( auto *lSymbol = s.symbol() )
358 style.setSymbol( lSymbol );
359 }
360 return style;
361}
362
363bool QgsConditionalStyle::writeXml( QDomNode &node, QDomDocument &doc, const QgsReadWriteContext &context ) const
364{
365 QDomElement stylesel = doc.createElement( QStringLiteral( "style" ) );
366 stylesel.setAttribute( QStringLiteral( "rule" ), mRule );
367 stylesel.setAttribute( QStringLiteral( "name" ), mName );
368 if ( mBackColor.isValid() )
369 {
370 stylesel.setAttribute( QStringLiteral( "background_color" ), mBackColor.name() );
371 stylesel.setAttribute( QStringLiteral( "background_color_alpha" ), mBackColor.alpha() );
372 }
373 if ( mTextColor.isValid() )
374 {
375 stylesel.setAttribute( QStringLiteral( "text_color" ), mTextColor.name() );
376 stylesel.setAttribute( QStringLiteral( "text_color_alpha" ), mTextColor.alpha() );
377 }
378 const QDomElement labelFontElem = QgsFontUtils::toXmlElement( mFont, doc, QStringLiteral( "font" ) );
379 stylesel.appendChild( labelFontElem );
380 if ( mSymbol )
381 {
382 const QDomElement symbolElm = QgsSymbolLayerUtils::saveSymbol( QStringLiteral( "icon" ), mSymbol.get(), doc, context );
383 stylesel.appendChild( symbolElm );
384 }
385 node.appendChild( stylesel );
386 return true;
387}
388
390{
391 return mValid == other.mValid
392 && mName == other.mName
393 && mRule == other.mRule
394 && mFont == other.mFont
395 && mBackColor == other.mBackColor
396 && mTextColor == other.mTextColor
397 && static_cast< bool >( mSymbol ) == static_cast< bool >( other.mSymbol )
398 && ( ! mSymbol || QgsSymbolLayerUtils::symbolProperties( mSymbol.get() ) == QgsSymbolLayerUtils::symbolProperties( other.mSymbol.get() ) );
399}
400
402{
403 return !( *this == other );
404}
405
406bool QgsConditionalStyle::readXml( const QDomNode &node, const QgsReadWriteContext &context )
407{
408 const QDomElement styleElm = node.toElement();
409 setRule( styleElm.attribute( QStringLiteral( "rule" ) ) );
410 setName( styleElm.attribute( QStringLiteral( "name" ) ) );
411 if ( styleElm.hasAttribute( QStringLiteral( "background_color" ) ) )
412 {
413 QColor bColor = QColor( styleElm.attribute( QStringLiteral( "background_color" ) ) );
414 if ( styleElm.hasAttribute( QStringLiteral( "background_color_alpha" ) ) )
415 {
416 bColor.setAlpha( styleElm.attribute( QStringLiteral( "background_color_alpha" ) ).toInt() );
417 }
418 if ( bColor.alpha() == 0 )
419 setBackgroundColor( QColor() );
420 else
421 setBackgroundColor( bColor );
422 }
423 else
424 {
425 setBackgroundColor( QColor() );
426 }
427 if ( styleElm.hasAttribute( QStringLiteral( "text_color" ) ) )
428 {
429 QColor tColor = QColor( styleElm.attribute( QStringLiteral( "text_color" ) ) );
430 if ( styleElm.hasAttribute( QStringLiteral( "text_color_alpha" ) ) )
431 {
432 tColor.setAlpha( styleElm.attribute( QStringLiteral( "text_color_alpha" ) ).toInt() );
433 }
434 if ( tColor.alpha() == 0 )
435 setTextColor( QColor() );
436 else
437 setTextColor( tColor );
438 }
439 else
440 {
441 setTextColor( QColor() );
442 }
443 QgsFontUtils::setFromXmlChildNode( mFont, styleElm, QStringLiteral( "font" ) );
444 const QDomElement symbolElm = styleElm.firstChildElement( QStringLiteral( "symbol" ) );
445 if ( !symbolElm.isNull() )
446 {
447 std::unique_ptr< QgsSymbol > symbol = QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( symbolElm, context );
448 setSymbol( symbol.release() );
449 }
450 return true;
451}
452
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.
Handles 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.
A container for the context for various read/write operations on objects.
static std::unique_ptr< QgsSymbol > loadSymbol(const QDomElement &element, const QgsReadWriteContext &context)
Attempts to load a symbol from a DOM element.
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.