QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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.setRenderHint( QPainter::HighQualityAntialiasing );
222  painter.setFont( font() );
223  rect = QRect( pixmap.width() / 3, 0, 2 * pixmap.width() / 3, pixmap.height() );
224  painter.drawText( rect, Qt::AlignCenter, QStringLiteral( "abc\n123" ) );
225  painter.end();
226  return pixmap;
227 }
228 
230 {
231  return ( backgroundColor().isValid() && backgroundColor().alpha() != 0 );
232 }
233 
235 {
236  return ( textColor().isValid() && textColor().alpha() != 0 );
237 }
238 
239 QList<QgsConditionalStyle> QgsConditionalStyle::matchingConditionalStyles( const QList<QgsConditionalStyle> &styles, const QVariant &value, QgsExpressionContext &context )
240 {
241  QList<QgsConditionalStyle> matchingstyles;
242  const auto constStyles = styles;
243  for ( const QgsConditionalStyle &style : constStyles )
244  {
245  if ( style.matches( value, context ) )
246  matchingstyles.append( style );
247  }
248  return matchingstyles;
249 }
250 
251 QgsConditionalStyle QgsConditionalStyle::matchingConditionalStyle( const QList<QgsConditionalStyle> &styles, const QVariant &value, QgsExpressionContext &context )
252 {
253  const auto constStyles = styles;
254  for ( const QgsConditionalStyle &style : constStyles )
255  {
256  if ( style.matches( value, context ) )
257  return style;
258  }
259  return QgsConditionalStyle();
260 }
261 
262 QgsConditionalStyle QgsConditionalStyle::compressStyles( const QList<QgsConditionalStyle> &styles )
263 {
264  QgsConditionalStyle style;
265  for ( const QgsConditionalStyle &s : styles )
266  {
267  if ( !s.isValid() )
268  continue;
269 
270  style.setFont( s.font() );
271  if ( s.backgroundColor().isValid() && s.backgroundColor().alpha() != 0 )
272  style.setBackgroundColor( s.backgroundColor() );
273  if ( s.textColor().isValid() && s.textColor().alpha() != 0 )
274  style.setTextColor( s.textColor() );
275  if ( s.symbol() )
276  style.setSymbol( s.symbol() );
277  }
278  return style;
279 }
280 
281 bool QgsConditionalStyle::writeXml( QDomNode &node, QDomDocument &doc, const QgsReadWriteContext &context ) const
282 {
283  QDomElement stylesel = doc.createElement( QStringLiteral( "style" ) );
284  stylesel.setAttribute( QStringLiteral( "rule" ), mRule );
285  stylesel.setAttribute( QStringLiteral( "name" ), mName );
286  if ( mBackColor.isValid() )
287  {
288  stylesel.setAttribute( QStringLiteral( "background_color" ), mBackColor.name() );
289  stylesel.setAttribute( QStringLiteral( "background_color_alpha" ), mBackColor.alpha() );
290  }
291  if ( mTextColor.isValid() )
292  {
293  stylesel.setAttribute( QStringLiteral( "text_color" ), mTextColor.name() );
294  stylesel.setAttribute( QStringLiteral( "text_color_alpha" ), mTextColor.alpha() );
295  }
296  QDomElement labelFontElem = QgsFontUtils::toXmlElement( mFont, doc, QStringLiteral( "font" ) );
297  stylesel.appendChild( labelFontElem );
298  if ( mSymbol )
299  {
300  QDomElement symbolElm = QgsSymbolLayerUtils::saveSymbol( QStringLiteral( "icon" ), mSymbol.get(), doc, context );
301  stylesel.appendChild( symbolElm );
302  }
303  node.appendChild( stylesel );
304  return true;
305 }
306 
308 {
309  return mValid == other.mValid
310  && mName == other.mName
311  && mRule == other.mRule
312  && mFont == other.mFont
313  && mBackColor == other.mBackColor
314  && mTextColor == other.mTextColor
315  && static_cast< bool >( mSymbol ) == static_cast< bool >( other.mSymbol )
316  && ( ! mSymbol || QgsSymbolLayerUtils::symbolProperties( mSymbol.get() ) == QgsSymbolLayerUtils::symbolProperties( other.mSymbol.get() ) );
317 }
318 
320 {
321  return !( *this == other );
322 }
323 
324 bool QgsConditionalStyle::readXml( const QDomNode &node, const QgsReadWriteContext &context )
325 {
326  QDomElement styleElm = node.toElement();
327  setRule( styleElm.attribute( QStringLiteral( "rule" ) ) );
328  setName( styleElm.attribute( QStringLiteral( "name" ) ) );
329  if ( styleElm.hasAttribute( QStringLiteral( "background_color" ) ) )
330  {
331  QColor bColor = QColor( styleElm.attribute( QStringLiteral( "background_color" ) ) );
332  if ( styleElm.hasAttribute( QStringLiteral( "background_color_alpha" ) ) )
333  {
334  bColor.setAlpha( styleElm.attribute( QStringLiteral( "background_color_alpha" ) ).toInt() );
335  }
336  if ( bColor.alpha() == 0 )
337  setBackgroundColor( QColor() );
338  else
339  setBackgroundColor( bColor );
340  }
341  else
342  {
343  setBackgroundColor( QColor() );
344  }
345  if ( styleElm.hasAttribute( QStringLiteral( "text_color" ) ) )
346  {
347  QColor tColor = QColor( styleElm.attribute( QStringLiteral( "text_color" ) ) );
348  if ( styleElm.hasAttribute( QStringLiteral( "text_color_alpha" ) ) )
349  {
350  tColor.setAlpha( styleElm.attribute( QStringLiteral( "text_color_alpha" ) ).toInt() );
351  }
352  if ( tColor.alpha() == 0 )
353  setTextColor( QColor() );
354  else
355  setTextColor( tColor );
356  }
357  else
358  {
359  setTextColor( QColor() );
360  }
361  QgsFontUtils::setFromXmlChildNode( mFont, styleElm, QStringLiteral( "font" ) );
362  QDomElement symbolElm = styleElm.firstChildElement( QStringLiteral( "symbol" ) );
363  if ( !symbolElm.isNull() )
364  {
365  QgsSymbol *symbol = QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( symbolElm, context );
366  setSymbol( symbol );
367  }
368  return true;
369 }
370 
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:369
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
Definition: qgsreadwritecontext.h:34
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:251
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:281
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
Definition: qgssymbol.h:63
qgsfontutils.h
QgsConditionalStyle
Definition: qgsconditionalstyle.h:112
QgsConditionalStyle::operator==
bool operator==(const QgsConditionalStyle &other) const
Definition: qgsconditionalstyle.cpp:307
QgsConditionalStyle::compressStyles
static QgsConditionalStyle compressStyles(const QList< QgsConditionalStyle > &styles)
Compress a list of styles into a single style.
Definition: qgsconditionalstyle.cpp:262
QgsSymbolLayerUtils::symbolProperties
static QString symbolProperties(QgsSymbol *symbol)
Returns a string representing the symbol.
Definition: qgssymbollayerutils.cpp:1226
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:229
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:239
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:324
QgsConditionalStyle::operator!=
bool operator!=(const QgsConditionalStyle &other) const
Definition: qgsconditionalstyle.cpp:319
QgsExpressionContextScope::StaticVariable
Single variable definition for use within a QgsExpressionContextScope.
Definition: qgsexpressioncontext.h:118
QgsExpression
Definition: qgsexpression.h:113
QgsConditionalStyle::validTextColor
bool validTextColor() const
Check if the text color is valid for render.
Definition: qgsconditionalstyle.cpp:234
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:1180
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