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