QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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"
21 #include "qgsmarkersymbollayer.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 
42 void 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 
51 QList<QgsConditionalStyle> QgsConditionalLayerStyles::fieldStyles( const QString &fieldName ) const
52 {
53  return mFieldStyles.value( fieldName );
54 }
55 
56 bool 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 
101 bool 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 
204 bool 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 
211 QPixmap 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 
253 QList<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 
265 QgsConditionalStyle 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 
276 QgsConditionalStyle QgsConditionalStyle::compressStyles( const QList<QgsConditionalStyle> &styles )
277 {
278  QgsConditionalStyle style;
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 
295 bool 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 
338 bool 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 
QgsConditionalStyles
QList< QgsConditionalStyle > QgsConditionalStyles
Definition: qgsconditionalstyle.h:30
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:406
QgsConditionalStyle::matches
bool matches(const QVariant &value, QgsExpressionContext &context) const
Check if the rule matches using the given value and feature.
Definition: qgsconditionalstyle.cpp:204
qgsconditionalstyle.h
QgsConditionalStyle::symbol
QgsSymbol * symbol() const
The symbol used to generate the icon for the style.
Definition: qgsconditionalstyle.h:206
QgsConditionalStyle::isValid
bool isValid() const
isValid Check if this rule is valid.
Definition: qgsconditionalstyle.h:252
QgsConditionalStyle::operator=
QgsConditionalStyle & operator=(const QgsConditionalStyle &other)
Definition: qgsconditionalstyle.cpp:162
QgsReadWriteContext
The class is used as a container of context for various read/write operations on other objects.
Definition: qgsreadwritecontext.h:34
QgsExpressionContextScope::addVariable
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
Definition: qgsexpressioncontext.cpp:97
QgsConditionalStyle::setBackgroundColor
void setBackgroundColor(const QColor &value)
Set the background color for the style.
Definition: qgsconditionalstyle.h:164
QgsConditionalStyle::matchingConditionalStyle
static QgsConditionalStyle matchingConditionalStyle(const QList< QgsConditionalStyle > &styles, const QVariant &value, QgsExpressionContext &context)
Find and return the matching style for the value and feature.
Definition: qgsconditionalstyle.cpp:265
qgsexpression.h
QgsConditionalStyle::setTextColor
void setTextColor(const QColor &value)
Set the text color for the style.
Definition: qgsconditionalstyle.h:170
QgsConditionalStyle::writeXml
bool writeXml(QDomNode &node, QDomDocument &doc, const QgsReadWriteContext &context) const
Write vector conditional style specific state from layer Dom node.
Definition: qgsconditionalstyle.cpp:295
QgsConditionalLayerStyles::writeXml
bool writeXml(QDomNode &node, QDomDocument &doc, const QgsReadWriteContext &context) const
Writes the condition styles state to a DOM node.
Definition: qgsconditionalstyle.cpp:56
QgsExpressionContext::lastScope
QgsExpressionContextScope * lastScope()
Returns the last scope added to the context.
Definition: qgsexpressioncontext.cpp:377
qgssymbollayerutils.h
QgsConditionalStyle::renderPreview
QPixmap renderPreview(const QSize &size=QSize()) const
Render a preview icon of the rule, at the specified size.
Definition: qgsconditionalstyle.cpp:211
qgsmarkersymbollayer.h
QgsSymbol
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:92
qgsfontutils.h
QgsConditionalStyle
Conditional styling for a rule.
Definition: qgsconditionalstyle.h:120
QgsConditionalStyle::operator==
bool operator==(const QgsConditionalStyle &other) const
Definition: qgsconditionalstyle.cpp:321
QgsConditionalStyle::compressStyles
static QgsConditionalStyle compressStyles(const QList< QgsConditionalStyle > &styles)
Compress a list of styles into a single style.
Definition: qgsconditionalstyle.cpp:276
QgsSymbolLayerUtils::symbolProperties
static QString symbolProperties(QgsSymbol *symbol)
Returns a string representing the symbol.
Definition: qgssymbollayerutils.cpp:1454
QgsConditionalStyle::setRule
void setRule(const QString &value)
Set the rule for the style.
Definition: qgsconditionalstyle.h:158
QgsConditionalStyle::icon
QPixmap icon() const
The icon set for style generated from the set symbol.
Definition: qgsconditionalstyle.h:200
QgsConditionalStyle::~QgsConditionalStyle
~QgsConditionalStyle()
QgsConditionalStyle::rule
QString rule() const
The condition rule set for the style.
Definition: qgsconditionalstyle.h:245
QgsSymbolLayerUtils::symbolPreviewPixmap
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.
Definition: qgssymbollayerutils.cpp:876
QgsConditionalStyle::validBackgroundColor
bool validBackgroundColor() const
Check if the background color is valid for render.
Definition: qgsconditionalstyle.cpp:243
QgsConditionalStyle::backgroundColor
QColor backgroundColor() const
The background color for style.
Definition: qgsconditionalstyle.h:225
QgsConditionalStyle::setName
void setName(const QString &value)
Set the name of the style.
Definition: qgsconditionalstyle.h:151
QgsConditionalLayerStyles::setFieldStyles
void setFieldStyles(const QString &fieldName, const QList< QgsConditionalStyle > &styles)
Set the conditional styles for a field, with the specified fieldName.
Definition: qgsconditionalstyle.cpp:42
QgsConditionalStyle::QgsConditionalStyle
QgsConditionalStyle()
Definition: qgsconditionalstyle.cpp:139
QgsConditionalStyle::displayText
QString displayText() const
The name of the style.
Definition: qgsconditionalstyle.cpp:182
QgsConditionalStyle::matchingConditionalStyles
static QList< QgsConditionalStyle > matchingConditionalStyles(const QList< QgsConditionalStyle > &styles, const QVariant &value, QgsExpressionContext &context)
Find and return the matching styles for the value and feature.
Definition: qgsconditionalstyle.cpp:253
QgsConditionalLayerStyles::rulesNeedGeometry
bool rulesNeedGeometry() const
Returns true if at least one rule needs geometry.
Definition: qgsconditionalstyle.cpp:89
QgsExpression::evaluate
QVariant evaluate()
Evaluate the feature and return the result.
Definition: qgsexpression.cpp:350
QgsConditionalStyle::name
QString name() const
The name of the style.
Definition: qgsconditionalstyle.h:194
QgsConditionalStyle::font
QFont font() const
The font for the style.
Definition: qgsconditionalstyle.h:238
QgsConditionalLayerStyles::rowStyles
QgsConditionalStyles rowStyles() const
Returns a list of row styles associated with the layer.
Definition: qgsconditionalstyle.cpp:28
QgsSymbol::clone
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
QgsFontUtils::setFromXmlChildNode
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.
Definition: qgsfontutils.cpp:400
QgsFontUtils::toXmlElement
static QDomElement toXmlElement(const QFont &font, QDomDocument &document, const QString &elementName)
Returns a DOM element containing the properties of the font.
Definition: qgsfontutils.cpp:354
QgsConditionalLayerStyles::readXml
bool readXml(const QDomNode &node, const QgsReadWriteContext &context)
Reads the condition styles state from a DOM node.
Definition: qgsconditionalstyle.cpp:101
QgsConditionalStyle::textColor
QColor textColor() const
The text color set for style.
Definition: qgsconditionalstyle.h:212
qgsmarkersymbol.h
QgsConditionalStyle::setSymbol
void setSymbol(QgsSymbol *value)
Set the icon for the style.
Definition: qgsconditionalstyle.cpp:190
QgsExpression::needsGeometry
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
Definition: qgsexpression.cpp:270
QgsConditionalLayerStyles::setRowStyles
void setRowStyles(const QgsConditionalStyles &styles)
Sets the conditional styles that apply to full rows of data in the attribute table.
Definition: qgsconditionalstyle.cpp:33
QgsConditionalLayerStyles::fieldStyles
QList< QgsConditionalStyle > fieldStyles(const QString &fieldName) const
Returns the conditional styles set for the field with matching fieldName.
Definition: qgsconditionalstyle.cpp:51
QgsConditionalStyle::readXml
bool readXml(const QDomNode &node, const QgsReadWriteContext &context)
Reads vector conditional style specific state from layer Dom node.
Definition: qgsconditionalstyle.cpp:338
QgsConditionalStyle::operator!=
bool operator!=(const QgsConditionalStyle &other) const
Definition: qgsconditionalstyle.cpp:333
QgsExpressionContextScope::StaticVariable
Single variable definition for use within a QgsExpressionContextScope.
Definition: qgsexpressioncontext.h:120
QgsExpression
Class for parsing and evaluation of expressions (formerly called "search strings")....
Definition: qgsexpression.h:102
QgsConditionalStyle::validTextColor
bool validTextColor() const
Check if the text color is valid for render.
Definition: qgsconditionalstyle.cpp:248
QgsConditionalLayerStyles::changed
void changed()
Emitted when the conditional styles are changed.
QgsSymbolLayerUtils::saveSymbol
static QDomElement saveSymbol(const QString &symbolName, const QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
Definition: qgssymbollayerutils.cpp:1397
QgsConditionalLayerStyles::QgsConditionalLayerStyles
QgsConditionalLayerStyles(QObject *parent=nullptr)
Constructor for QgsConditionalLayerStyles, with the specified parent object.
Definition: qgsconditionalstyle.cpp:24
QgsConditionalStyle::setFont
void setFont(const QFont &value)
Set the font for the style.
Definition: qgsconditionalstyle.h:176