QGIS API Documentation  3.20.0-Odense (decaadbb31)
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  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 
89 bool QgsConditionalLayerStyles::readXml( const QDomNode &node, const QgsReadWriteContext &context )
90 {
91  QDomElement condel = node.firstChildElement( QStringLiteral( "conditionalstyles" ) );
92  mRowStyles.clear();
93  mFieldStyles.clear();
94  QDomElement rowstylesel = condel.firstChildElement( QStringLiteral( "rowstyles" ) );
95  QDomNodeList nodelist = rowstylesel.toElement().elementsByTagName( QStringLiteral( "style" ) );
96  for ( int i = 0; i < nodelist.count(); i++ )
97  {
98  QDomElement styleElm = nodelist.at( i ).toElement();
100  style.readXml( styleElm, context );
101  mRowStyles.append( style );
102  }
103 
104  QDomElement fieldstylesel = condel.firstChildElement( QStringLiteral( "fieldstyles" ) );
105  nodelist = fieldstylesel.toElement().elementsByTagName( QStringLiteral( "fieldstyle" ) );
106  QList<QgsConditionalStyle> styles;
107  for ( int i = 0; i < nodelist.count(); i++ )
108  {
109  styles.clear();
110  QDomElement fieldel = nodelist.at( i ).toElement();
111  QString fieldName = fieldel.attribute( QStringLiteral( "fieldname" ) );
112  QDomNodeList stylenodelist = fieldel.toElement().elementsByTagName( QStringLiteral( "style" ) );
113  styles.reserve( stylenodelist.count() );
114  for ( int j = 0; j < stylenodelist.count(); j++ )
115  {
116  QDomElement styleElm = stylenodelist.at( j ).toElement();
118  style.readXml( styleElm, context );
119  styles.append( style );
120  }
121  mFieldStyles.insert( fieldName, styles );
122  }
123 
124  return true;
125 }
126 
128 {}
129 
131 {
132  setRule( rule );
133 }
134 
136 
138  : mValid( other.mValid )
139  , mName( other.mName )
140  , mRule( other.mRule )
141  , mFont( other.mFont )
142  , mBackColor( other.mBackColor )
143  , mTextColor( other.mTextColor )
144  , mIcon( other.mIcon )
145 {
146  if ( other.mSymbol )
147  mSymbol.reset( other.mSymbol->clone() );
148 }
149 
151 {
152  mValid = other.mValid;
153  mRule = other.mRule;
154  mFont = other.mFont;
155  mBackColor = other.mBackColor;
156  mTextColor = other.mTextColor;
157  mIcon = other.mIcon;
158  mName = other.mName;
159  if ( other.mSymbol )
160  {
161  mSymbol.reset( other.mSymbol->clone() );
162  }
163  else
164  {
165  mSymbol.reset();
166  }
167  return ( *this );
168 }
169 
171 {
172  if ( name().isEmpty() )
173  return rule();
174  else
175  return QStringLiteral( "%1 \n%2" ).arg( name(), rule() );
176 }
177 
179 {
180  mValid = true;
181  if ( value )
182  {
183  mSymbol.reset( value->clone() );
184  mIcon = QgsSymbolLayerUtils::symbolPreviewPixmap( mSymbol.get(), QSize( 16, 16 ) );
185  }
186  else
187  {
188  mSymbol.reset();
189  }
190 }
191 
192 bool QgsConditionalStyle::matches( const QVariant &value, QgsExpressionContext &context ) const
193 {
194  QgsExpression exp( mRule );
195  context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "value" ), value, true ) );
196  return exp.evaluate( &context ).toBool();
197 }
198 
199 QPixmap QgsConditionalStyle::renderPreview( const QSize &size ) const
200 {
201  QPixmap pixmap( size.isValid() ? size.width() : 64, size.isValid() ? size.height() : 32 );
202  pixmap.fill( Qt::transparent );
203 
204  QPainter painter( &pixmap );
205 
206  if ( validBackgroundColor() )
207  painter.setBrush( mBackColor );
208 
209  QRect rect = QRect( 0, 0, pixmap.width(), pixmap.height() );
210  painter.setPen( Qt::NoPen );
211  painter.drawRect( rect );
212  const QPixmap symbolIcon = icon();
213  if ( !symbolIcon.isNull() )
214  {
215  painter.drawPixmap( ( pixmap.width() / 3 - symbolIcon.width() ) / 2, ( pixmap.height() - symbolIcon.height() ) / 2, symbolIcon );
216  }
217 
218  if ( validTextColor() )
219  painter.setPen( mTextColor );
220  else
221  painter.setPen( Qt::black );
222 
223  painter.setRenderHint( QPainter::Antialiasing );
224  painter.setFont( font() );
225  rect = QRect( pixmap.width() / 3, 0, 2 * pixmap.width() / 3, pixmap.height() );
226  painter.drawText( rect, Qt::AlignCenter, QStringLiteral( "abc\n123" ) );
227  painter.end();
228  return pixmap;
229 }
230 
232 {
233  return ( backgroundColor().isValid() && backgroundColor().alpha() != 0 );
234 }
235 
237 {
238  return ( textColor().isValid() && textColor().alpha() != 0 );
239 }
240 
241 QList<QgsConditionalStyle> QgsConditionalStyle::matchingConditionalStyles( const QList<QgsConditionalStyle> &styles, const QVariant &value, QgsExpressionContext &context )
242 {
243  QList<QgsConditionalStyle> matchingstyles;
244  const auto constStyles = styles;
245  for ( const QgsConditionalStyle &style : constStyles )
246  {
247  if ( style.matches( value, context ) )
248  matchingstyles.append( style );
249  }
250  return matchingstyles;
251 }
252 
253 QgsConditionalStyle QgsConditionalStyle::matchingConditionalStyle( const QList<QgsConditionalStyle> &styles, const QVariant &value, QgsExpressionContext &context )
254 {
255  const auto constStyles = styles;
256  for ( const QgsConditionalStyle &style : constStyles )
257  {
258  if ( style.matches( value, context ) )
259  return style;
260  }
261  return QgsConditionalStyle();
262 }
263 
264 QgsConditionalStyle QgsConditionalStyle::compressStyles( const QList<QgsConditionalStyle> &styles )
265 {
266  QgsConditionalStyle style;
267  for ( const QgsConditionalStyle &s : styles )
268  {
269  if ( !s.isValid() )
270  continue;
271 
272  style.setFont( s.font() );
273  if ( s.backgroundColor().isValid() && s.backgroundColor().alpha() != 0 )
274  style.setBackgroundColor( s.backgroundColor() );
275  if ( s.textColor().isValid() && s.textColor().alpha() != 0 )
276  style.setTextColor( s.textColor() );
277  if ( auto *lSymbol = s.symbol() )
278  style.setSymbol( lSymbol );
279  }
280  return style;
281 }
282 
283 bool QgsConditionalStyle::writeXml( QDomNode &node, QDomDocument &doc, const QgsReadWriteContext &context ) const
284 {
285  QDomElement stylesel = doc.createElement( QStringLiteral( "style" ) );
286  stylesel.setAttribute( QStringLiteral( "rule" ), mRule );
287  stylesel.setAttribute( QStringLiteral( "name" ), mName );
288  if ( mBackColor.isValid() )
289  {
290  stylesel.setAttribute( QStringLiteral( "background_color" ), mBackColor.name() );
291  stylesel.setAttribute( QStringLiteral( "background_color_alpha" ), mBackColor.alpha() );
292  }
293  if ( mTextColor.isValid() )
294  {
295  stylesel.setAttribute( QStringLiteral( "text_color" ), mTextColor.name() );
296  stylesel.setAttribute( QStringLiteral( "text_color_alpha" ), mTextColor.alpha() );
297  }
298  QDomElement labelFontElem = QgsFontUtils::toXmlElement( mFont, doc, QStringLiteral( "font" ) );
299  stylesel.appendChild( labelFontElem );
300  if ( mSymbol )
301  {
302  QDomElement symbolElm = QgsSymbolLayerUtils::saveSymbol( QStringLiteral( "icon" ), mSymbol.get(), doc, context );
303  stylesel.appendChild( symbolElm );
304  }
305  node.appendChild( stylesel );
306  return true;
307 }
308 
310 {
311  return mValid == other.mValid
312  && mName == other.mName
313  && mRule == other.mRule
314  && mFont == other.mFont
315  && mBackColor == other.mBackColor
316  && mTextColor == other.mTextColor
317  && static_cast< bool >( mSymbol ) == static_cast< bool >( other.mSymbol )
318  && ( ! mSymbol || QgsSymbolLayerUtils::symbolProperties( mSymbol.get() ) == QgsSymbolLayerUtils::symbolProperties( other.mSymbol.get() ) );
319 }
320 
322 {
323  return !( *this == other );
324 }
325 
326 bool QgsConditionalStyle::readXml( const QDomNode &node, const QgsReadWriteContext &context )
327 {
328  QDomElement styleElm = node.toElement();
329  setRule( styleElm.attribute( QStringLiteral( "rule" ) ) );
330  setName( styleElm.attribute( QStringLiteral( "name" ) ) );
331  if ( styleElm.hasAttribute( QStringLiteral( "background_color" ) ) )
332  {
333  QColor bColor = QColor( styleElm.attribute( QStringLiteral( "background_color" ) ) );
334  if ( styleElm.hasAttribute( QStringLiteral( "background_color_alpha" ) ) )
335  {
336  bColor.setAlpha( styleElm.attribute( QStringLiteral( "background_color_alpha" ) ).toInt() );
337  }
338  if ( bColor.alpha() == 0 )
339  setBackgroundColor( QColor() );
340  else
341  setBackgroundColor( bColor );
342  }
343  else
344  {
345  setBackgroundColor( QColor() );
346  }
347  if ( styleElm.hasAttribute( QStringLiteral( "text_color" ) ) )
348  {
349  QColor tColor = QColor( styleElm.attribute( QStringLiteral( "text_color" ) ) );
350  if ( styleElm.hasAttribute( QStringLiteral( "text_color_alpha" ) ) )
351  {
352  tColor.setAlpha( styleElm.attribute( QStringLiteral( "text_color_alpha" ) ).toInt() );
353  }
354  if ( tColor.alpha() == 0 )
355  setTextColor( QColor() );
356  else
357  setTextColor( tColor );
358  }
359  else
360  {
361  setTextColor( QColor() );
362  }
363  QgsFontUtils::setFromXmlChildNode( mFont, styleElm, QStringLiteral( "font" ) );
364  QDomElement symbolElm = styleElm.firstChildElement( QStringLiteral( "symbol" ) );
365  if ( !symbolElm.isNull() )
366  {
367  QgsSymbol *symbol = QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( symbolElm, context );
368  setSymbol( symbol );
369  }
370  return true;
371 }
372 
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:38
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
QList< QgsConditionalStyle > QgsConditionalStyles
Single variable definition for use within a QgsExpressionContextScope.