QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 
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.
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.
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.
QgsSymbol * symbol() const
The symbol used to generate the icon for the style.
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.
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").
QVariant evaluate()
Evaluate the feature and return the result.
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)
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:65
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
QList< QgsConditionalStyle > QgsConditionalStyles
Single variable definition for use within a QgsExpressionContextScope.