QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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 
24  : QObject( parent )
25 {}
26 
28 {
29  return mRowStyles;
30 }
31 
33 {
34  if ( styles == mRowStyles )
35  return;
36 
37  mRowStyles = styles;
38  emit changed();
39 }
40 
41 void QgsConditionalLayerStyles::setFieldStyles( const QString &fieldName, const QList<QgsConditionalStyle> &styles )
42 {
43  if ( mFieldStyles.value( fieldName ) == styles )
44  return;
45 
46  mFieldStyles.insert( fieldName, styles );
47  emit changed();
48 }
49 
50 QList<QgsConditionalStyle> QgsConditionalLayerStyles::fieldStyles( const QString &fieldName ) const
51 {
52  return mFieldStyles.value( fieldName );
53 }
54 
55 bool QgsConditionalLayerStyles::writeXml( QDomNode &node, QDomDocument &doc, const QgsReadWriteContext &context ) const
56 {
57  QDomElement stylesel = doc.createElement( QStringLiteral( "conditionalstyles" ) );
58  QDomElement rowel = doc.createElement( QStringLiteral( "rowstyles" ) );
59  const auto constMRowStyles = mRowStyles;
60  for ( const QgsConditionalStyle &style : constMRowStyles )
61  {
62  style.writeXml( rowel, doc, context );
63  }
64 
65  stylesel.appendChild( rowel );
66 
67  QDomElement fieldsel = doc.createElement( QStringLiteral( "fieldstyles" ) );
68  QHash<QString, QgsConditionalStyles>::const_iterator it = mFieldStyles.constBegin();
69  for ( ; it != mFieldStyles.constEnd(); ++it )
70  {
71  QDomElement fieldel = doc.createElement( QStringLiteral( "fieldstyle" ) );
72  fieldel.setAttribute( QStringLiteral( "fieldname" ), it.key() );
73  QgsConditionalStyles styles = it.value();
74  const auto constStyles = styles;
75  for ( const QgsConditionalStyle &style : constStyles )
76  {
77  style.writeXml( fieldel, doc, context );
78  }
79  fieldsel.appendChild( fieldel );
80  }
81 
82  stylesel.appendChild( fieldsel );
83 
84  node.appendChild( stylesel );
85  return true;
86 }
87 
88 bool QgsConditionalLayerStyles::readXml( const QDomNode &node, const QgsReadWriteContext &context )
89 {
90  QDomElement condel = node.firstChildElement( QStringLiteral( "conditionalstyles" ) );
91  mRowStyles.clear();
92  mFieldStyles.clear();
93  QDomElement rowstylesel = condel.firstChildElement( QStringLiteral( "rowstyles" ) );
94  QDomNodeList nodelist = rowstylesel.toElement().elementsByTagName( QStringLiteral( "style" ) );
95  for ( int i = 0; i < nodelist.count(); i++ )
96  {
97  QDomElement styleElm = nodelist.at( i ).toElement();
99  style.readXml( styleElm, context );
100  mRowStyles.append( style );
101  }
102 
103  QDomElement fieldstylesel = condel.firstChildElement( QStringLiteral( "fieldstyles" ) );
104  nodelist = fieldstylesel.toElement().elementsByTagName( QStringLiteral( "fieldstyle" ) );
105  QList<QgsConditionalStyle> styles;
106  for ( int i = 0; i < nodelist.count(); i++ )
107  {
108  styles.clear();
109  QDomElement fieldel = nodelist.at( i ).toElement();
110  QString fieldName = fieldel.attribute( QStringLiteral( "fieldname" ) );
111  QDomNodeList stylenodelist = fieldel.toElement().elementsByTagName( QStringLiteral( "style" ) );
112  styles.reserve( stylenodelist.count() );
113  for ( int j = 0; j < stylenodelist.count(); j++ )
114  {
115  QDomElement styleElm = stylenodelist.at( j ).toElement();
117  style.readXml( styleElm, context );
118  styles.append( style );
119  }
120  mFieldStyles.insert( fieldName, styles );
121  }
122 
123  return true;
124 }
125 
127 {}
128 
130 {
131  setRule( rule );
132 }
133 
135  : mValid( other.mValid )
136  , mName( other.mName )
137  , mRule( other.mRule )
138  , mFont( other.mFont )
139  , mBackColor( other.mBackColor )
140  , mTextColor( other.mTextColor )
141  , mIcon( other.mIcon )
142 {
143  if ( other.mSymbol )
144  mSymbol.reset( other.mSymbol->clone() );
145 }
146 
148 {
149  mValid = other.mValid;
150  mRule = other.mRule;
151  mFont = other.mFont;
152  mBackColor = other.mBackColor;
153  mTextColor = other.mTextColor;
154  mIcon = other.mIcon;
155  mName = other.mName;
156  if ( other.mSymbol )
157  {
158  mSymbol.reset( other.mSymbol->clone() );
159  }
160  else
161  {
162  mSymbol.reset();
163  }
164  return ( *this );
165 }
166 
168 {
169  if ( name().isEmpty() )
170  return rule();
171  else
172  return QStringLiteral( "%1 \n%2" ).arg( name(), rule() );
173 }
174 
176 {
177  mValid = true;
178  if ( value )
179  {
180  mSymbol.reset( value->clone() );
181  mIcon = QgsSymbolLayerUtils::symbolPreviewPixmap( mSymbol.get(), QSize( 16, 16 ) );
182  }
183  else
184  {
185  mSymbol.reset();
186  }
187 }
188 
189 bool QgsConditionalStyle::matches( const QVariant &value, QgsExpressionContext &context ) const
190 {
191  QgsExpression exp( mRule );
192  context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "value" ), value, true ) );
193  return exp.evaluate( &context ).toBool();
194 }
195 
196 QPixmap QgsConditionalStyle::renderPreview( const QSize &size ) const
197 {
198  QPixmap pixmap( size.isValid() ? size.width() : 64, size.isValid() ? size.height() : 32 );
199  pixmap.fill( Qt::transparent );
200 
201  QPainter painter( &pixmap );
202 
203  if ( validBackgroundColor() )
204  painter.setBrush( mBackColor );
205 
206  QRect rect = QRect( 0, 0, pixmap.width(), pixmap.height() );
207  painter.setPen( Qt::NoPen );
208  painter.drawRect( rect );
209  const QPixmap symbolIcon = icon();
210  if ( !symbolIcon.isNull() )
211  {
212  painter.drawPixmap( ( pixmap.width() / 3 - symbolIcon.width() ) / 2, ( pixmap.height() - symbolIcon.height() ) / 2, symbolIcon );
213  }
214 
215  if ( validTextColor() )
216  painter.setPen( mTextColor );
217  else
218  painter.setPen( Qt::black );
219 
220  painter.setRenderHint( QPainter::Antialiasing );
221  painter.setFont( font() );
222  rect = QRect( pixmap.width() / 3, 0, 2 * pixmap.width() / 3, pixmap.height() );
223  painter.drawText( rect, Qt::AlignCenter, QStringLiteral( "abc\n123" ) );
224  painter.end();
225  return pixmap;
226 }
227 
229 {
230  return ( backgroundColor().isValid() && backgroundColor().alpha() != 0 );
231 }
232 
234 {
235  return ( textColor().isValid() && textColor().alpha() != 0 );
236 }
237 
238 QList<QgsConditionalStyle> QgsConditionalStyle::matchingConditionalStyles( const QList<QgsConditionalStyle> &styles, const QVariant &value, QgsExpressionContext &context )
239 {
240  QList<QgsConditionalStyle> matchingstyles;
241  const auto constStyles = styles;
242  for ( const QgsConditionalStyle &style : constStyles )
243  {
244  if ( style.matches( value, context ) )
245  matchingstyles.append( style );
246  }
247  return matchingstyles;
248 }
249 
250 QgsConditionalStyle QgsConditionalStyle::matchingConditionalStyle( const QList<QgsConditionalStyle> &styles, const QVariant &value, QgsExpressionContext &context )
251 {
252  const auto constStyles = styles;
253  for ( const QgsConditionalStyle &style : constStyles )
254  {
255  if ( style.matches( value, context ) )
256  return style;
257  }
258  return QgsConditionalStyle();
259 }
260 
261 QgsConditionalStyle QgsConditionalStyle::compressStyles( const QList<QgsConditionalStyle> &styles )
262 {
263  QgsConditionalStyle style;
264  for ( const QgsConditionalStyle &s : styles )
265  {
266  if ( !s.isValid() )
267  continue;
268 
269  style.setFont( s.font() );
270  if ( s.backgroundColor().isValid() && s.backgroundColor().alpha() != 0 )
271  style.setBackgroundColor( s.backgroundColor() );
272  if ( s.textColor().isValid() && s.textColor().alpha() != 0 )
273  style.setTextColor( s.textColor() );
274  if ( auto *lSymbol = s.symbol() )
275  style.setSymbol( lSymbol );
276  }
277  return style;
278 }
279 
280 bool QgsConditionalStyle::writeXml( QDomNode &node, QDomDocument &doc, const QgsReadWriteContext &context ) const
281 {
282  QDomElement stylesel = doc.createElement( QStringLiteral( "style" ) );
283  stylesel.setAttribute( QStringLiteral( "rule" ), mRule );
284  stylesel.setAttribute( QStringLiteral( "name" ), mName );
285  if ( mBackColor.isValid() )
286  {
287  stylesel.setAttribute( QStringLiteral( "background_color" ), mBackColor.name() );
288  stylesel.setAttribute( QStringLiteral( "background_color_alpha" ), mBackColor.alpha() );
289  }
290  if ( mTextColor.isValid() )
291  {
292  stylesel.setAttribute( QStringLiteral( "text_color" ), mTextColor.name() );
293  stylesel.setAttribute( QStringLiteral( "text_color_alpha" ), mTextColor.alpha() );
294  }
295  QDomElement labelFontElem = QgsFontUtils::toXmlElement( mFont, doc, QStringLiteral( "font" ) );
296  stylesel.appendChild( labelFontElem );
297  if ( mSymbol )
298  {
299  QDomElement symbolElm = QgsSymbolLayerUtils::saveSymbol( QStringLiteral( "icon" ), mSymbol.get(), doc, context );
300  stylesel.appendChild( symbolElm );
301  }
302  node.appendChild( stylesel );
303  return true;
304 }
305 
307 {
308  return mValid == other.mValid
309  && mName == other.mName
310  && mRule == other.mRule
311  && mFont == other.mFont
312  && mBackColor == other.mBackColor
313  && mTextColor == other.mTextColor
314  && static_cast< bool >( mSymbol ) == static_cast< bool >( other.mSymbol )
315  && ( ! mSymbol || QgsSymbolLayerUtils::symbolProperties( mSymbol.get() ) == QgsSymbolLayerUtils::symbolProperties( other.mSymbol.get() ) );
316 }
317 
319 {
320  return !( *this == other );
321 }
322 
323 bool QgsConditionalStyle::readXml( const QDomNode &node, const QgsReadWriteContext &context )
324 {
325  QDomElement styleElm = node.toElement();
326  setRule( styleElm.attribute( QStringLiteral( "rule" ) ) );
327  setName( styleElm.attribute( QStringLiteral( "name" ) ) );
328  if ( styleElm.hasAttribute( QStringLiteral( "background_color" ) ) )
329  {
330  QColor bColor = QColor( styleElm.attribute( QStringLiteral( "background_color" ) ) );
331  if ( styleElm.hasAttribute( QStringLiteral( "background_color_alpha" ) ) )
332  {
333  bColor.setAlpha( styleElm.attribute( QStringLiteral( "background_color_alpha" ) ).toInt() );
334  }
335  if ( bColor.alpha() == 0 )
336  setBackgroundColor( QColor() );
337  else
338  setBackgroundColor( bColor );
339  }
340  else
341  {
342  setBackgroundColor( QColor() );
343  }
344  if ( styleElm.hasAttribute( QStringLiteral( "text_color" ) ) )
345  {
346  QColor tColor = QColor( styleElm.attribute( QStringLiteral( "text_color" ) ) );
347  if ( styleElm.hasAttribute( QStringLiteral( "text_color_alpha" ) ) )
348  {
349  tColor.setAlpha( styleElm.attribute( QStringLiteral( "text_color_alpha" ) ).toInt() );
350  }
351  if ( tColor.alpha() == 0 )
352  setTextColor( QColor() );
353  else
354  setTextColor( tColor );
355  }
356  else
357  {
358  setTextColor( QColor() );
359  }
360  QgsFontUtils::setFromXmlChildNode( mFont, styleElm, QStringLiteral( "font" ) );
361  QDomElement symbolElm = styleElm.firstChildElement( QStringLiteral( "symbol" ) );
362  if ( !symbolElm.isNull() )
363  {
364  QgsSymbol *symbol = QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( symbolElm, context );
365  setSymbol( symbol );
366  }
367  return true;
368 }
369 
QgsConditionalStyles
QList< QgsConditionalStyle > QgsConditionalStyles
Definition: qgsconditionalstyle.h:28
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:370
QgsConditionalStyle::matches
bool matches(const QVariant &value, QgsExpressionContext &context) const
Check if the rule matches using the given value and feature.
Definition: qgsconditionalstyle.cpp:189
qgsconditionalstyle.h
QgsConditionalStyle::symbol
QgsSymbol * symbol() const
The symbol used to generate the icon for the style.
Definition: qgsconditionalstyle.h:197
QgsConditionalStyle::isValid
bool isValid() const
isValid Check if this rule is valid.
Definition: qgsconditionalstyle.h:243
QgsConditionalStyle::operator=
QgsConditionalStyle & operator=(const QgsConditionalStyle &other)
Definition: qgsconditionalstyle.cpp:147
QgsReadWriteContext
The class is used as a container of context for various read/write operations on other objects.
Definition: qgsreadwritecontext.h:35
QgsExpressionContextScope::addVariable
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
Definition: qgsexpressioncontext.cpp:93
QgsConditionalStyle::setBackgroundColor
void setBackgroundColor(const QColor &value)
Set the background color for the style.
Definition: qgsconditionalstyle.h:155
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:250
qgsexpression.h
QgsConditionalStyle::setTextColor
void setTextColor(const QColor &value)
Set the text color for the style.
Definition: qgsconditionalstyle.h:161
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:280
QgsConditionalLayerStyles::writeXml
bool writeXml(QDomNode &node, QDomDocument &doc, const QgsReadWriteContext &context) const
Writes the condition styles state to a DOM node.
Definition: qgsconditionalstyle.cpp:55
QgsExpressionContext::lastScope
QgsExpressionContextScope * lastScope()
Returns the last scope added to the context.
Definition: qgsexpressioncontext.cpp:373
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:196
qgsmarkersymbollayer.h
QgsSymbol
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:64
qgsfontutils.h
QgsConditionalStyle
Conditional styling for a rule.
Definition: qgsconditionalstyle.h:113
QgsConditionalStyle::operator==
bool operator==(const QgsConditionalStyle &other) const
Definition: qgsconditionalstyle.cpp:306
QgsConditionalStyle::compressStyles
static QgsConditionalStyle compressStyles(const QList< QgsConditionalStyle > &styles)
Compress a list of styles into a single style.
Definition: qgsconditionalstyle.cpp:261
QgsSymbolLayerUtils::symbolProperties
static QString symbolProperties(QgsSymbol *symbol)
Returns a string representing the symbol.
Definition: qgssymbollayerutils.cpp:1228
QgsConditionalStyle::setRule
void setRule(const QString &value)
Set the rule for the style.
Definition: qgsconditionalstyle.h:149
QgsConditionalStyle::icon
QPixmap icon() const
The icon set for style generated from the set symbol.
Definition: qgsconditionalstyle.h:191
QgsConditionalStyle::rule
QString rule() const
The condition rule set for the style.
Definition: qgsconditionalstyle.h:236
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:767
QgsConditionalStyle::validBackgroundColor
bool validBackgroundColor() const
Check if the background color is valid for render.
Definition: qgsconditionalstyle.cpp:228
QgsConditionalStyle::backgroundColor
QColor backgroundColor() const
The background color for style.
Definition: qgsconditionalstyle.h:216
QgsConditionalStyle::setName
void setName(const QString &value)
Set the name of the style.
Definition: qgsconditionalstyle.h:142
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:41
QgsConditionalStyle::QgsConditionalStyle
QgsConditionalStyle()
Definition: qgsconditionalstyle.cpp:126
QgsConditionalStyle::displayText
QString displayText() const
The name of the style.
Definition: qgsconditionalstyle.cpp:167
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:238
QgsExpression::evaluate
QVariant evaluate()
Evaluate the feature and return the result.
Definition: qgsexpression.cpp:346
QgsConditionalStyle::name
QString name() const
The name of the style.
Definition: qgsconditionalstyle.h:185
QgsConditionalStyle::font
QFont font() const
The font for the style.
Definition: qgsconditionalstyle.h:229
QgsConditionalLayerStyles::rowStyles
QgsConditionalStyles rowStyles() const
Returns a list of row styles associated with the layer.
Definition: qgsconditionalstyle.cpp:27
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:348
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:324
QgsConditionalLayerStyles::readXml
bool readXml(const QDomNode &node, const QgsReadWriteContext &context)
Reads the condition styles state from a DOM node.
Definition: qgsconditionalstyle.cpp:88
QgsConditionalStyle::textColor
QColor textColor() const
The text color set for style.
Definition: qgsconditionalstyle.h:203
QgsConditionalStyle::setSymbol
void setSymbol(QgsSymbol *value)
Set the icon for the style.
Definition: qgsconditionalstyle.cpp:175
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:32
QgsConditionalLayerStyles::fieldStyles
QList< QgsConditionalStyle > fieldStyles(const QString &fieldName) const
Returns the conditional styles set for the field with matching fieldName.
Definition: qgsconditionalstyle.cpp:50
QgsConditionalStyle::readXml
bool readXml(const QDomNode &node, const QgsReadWriteContext &context)
Reads vector conditional style specific state from layer Dom node.
Definition: qgsconditionalstyle.cpp:323
QgsConditionalStyle::operator!=
bool operator!=(const QgsConditionalStyle &other) const
Definition: qgsconditionalstyle.cpp:318
QgsExpressionContextScope::StaticVariable
Single variable definition for use within a QgsExpressionContextScope.
Definition: qgsexpressioncontext.h:119
QgsExpression
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:105
QgsConditionalStyle::validTextColor
bool validTextColor() const
Check if the text color is valid for render.
Definition: qgsconditionalstyle.cpp:233
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:1182
QgsConditionalLayerStyles::QgsConditionalLayerStyles
QgsConditionalLayerStyles(QObject *parent=nullptr)
Constructor for QgsConditionalLayerStyles, with the specified parent object.
Definition: qgsconditionalstyle.cpp:23
QgsConditionalStyle::setFont
void setFont(const QFont &value)
Set the font for the style.
Definition: qgsconditionalstyle.h:167