Skip to content

Commit faa32f3

Browse files
committed
[FEATURE] Conditional formatting for attribute table cells
1 parent 3ff3b02 commit faa32f3

27 files changed

+1764
-66
lines changed

python/core/core.sip

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
%Include qgscontexthelp.sip
2323
%Include qgscoordinatereferencesystem.sip
2424
%Include qgscoordinatetransform.sip
25+
%Include qgsconditionalstyle.sip
2526
%Include qgscredentials.sip
2627
%Include qgscrscache.sip
2728
%Include qgsdatadefined.sip
@@ -40,6 +41,7 @@
4041
%Include qgsfeatureiterator.sip
4142
%Include qgsfeaturerequest.sip
4243
%Include qgsfield.sip
44+
%Include qgsfielduiproperties.sip
4345
%Include qgsgeometryvalidator.sip
4446
%Include qgsgeometrysimplifier.sip
4547
%Include qgshistogram.sip

python/core/qgsconditionalstyle.sip

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/** \class QgsCondtionalStyle
2+
* \ingroup core
3+
* Conditional styling for a rule.
4+
*/
5+
class QgsConditionalStyle
6+
{
7+
%TypeHeaderCode
8+
#include <qgsconditionalstyle.h>
9+
%End
10+
public:
11+
QgsConditionalStyle();
12+
QgsConditionalStyle( QString rule );
13+
14+
/**
15+
* @brief Check if the rule matches using the given value and feature
16+
* @param feature The feature to match the values from.
17+
* @return True of the rule matches against the given feature
18+
*/
19+
bool matches( QVariant value, QgsFeature *feature = 0 );
20+
21+
/**
22+
* @brief Render a preview icon of the rule.
23+
* @return QPixmap preview of the style
24+
*/
25+
QPixmap renderPreview();
26+
27+
/**
28+
* @brief Set the rule for the style. Rules should be of QgsExpression syntax.
29+
* Special value of @value is replaced at run time with the check value
30+
* @param value
31+
*/
32+
void setRule( QString value );
33+
34+
/**
35+
* @brief Set the background color for the style
36+
* @param value QColor for background color
37+
*/
38+
void setBackgroundColor( QColor value );
39+
40+
/**
41+
* @brief Set the text color for the style
42+
* @param value QColor for text color
43+
*/
44+
void setTextColor( QColor value );
45+
46+
/**
47+
* @brief Set the font for the the style
48+
* @param value QFont to be used for text
49+
*/
50+
void setFont( QFont value );
51+
52+
/**
53+
* @brief Set the icon for the style. Icons are generated from symbols
54+
* @param value QgsSymbolV2 to be used when generating the icon
55+
*/
56+
void setSymbol( QgsSymbolV2* value );
57+
58+
/**
59+
* @brief The symbol used to generate the icon for the style
60+
* @return The QgsSymbolV2 used for the icon
61+
*/
62+
QgsSymbolV2* symbol() const;
63+
64+
/**
65+
* @brief The icon set for style
66+
* @return A QPixmap that was set for the icon
67+
*/
68+
QPixmap icon() const;
69+
70+
/**
71+
* @brief The text color set for style
72+
* @return QColor for text color
73+
*/
74+
QColor textColor() const;
75+
76+
/**
77+
* @brief The background color for style
78+
* @return QColor for background color
79+
*/
80+
QColor backgroundColor() const;
81+
/**
82+
* @brief The font for the style
83+
* @return QFont for the style
84+
*/
85+
QFont font() const;
86+
87+
/**
88+
* @brief The condtion rule set for the style. Rule may contain variable @value
89+
* to represent the current value
90+
* @return QString of the current set rule
91+
*/
92+
QString rule() const;
93+
/**
94+
* @brief isValid Check if this rule is valid. A valid rule has one or more properties
95+
* set.
96+
* @return True if the rule is valid.
97+
*/
98+
bool isValid() const;
99+
100+
/** Reads vector conditional style specific state from layer Dom node.
101+
*/
102+
virtual bool readXml( const QDomNode& node );
103+
104+
/** Write vector conditional style specific state from layer Dom node.
105+
*/
106+
virtual bool writeXml( QDomNode & node, QDomDocument & doc );
107+
};

python/core/qgsfielduiproperties.sip

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/** \class QgsFieldUIProperties
2+
* \ingroup core
3+
* Holds extra UI properties for a field.
4+
*
5+
* Currently this object holds informations about condtional styles but in future will hold
6+
* things like field widgets, etc
7+
*/
8+
class QgsFieldUIProperties
9+
{
10+
%TypeHeaderCode
11+
#include <qgsfielduiproperties.h>
12+
%End
13+
public:
14+
QgsFieldUIProperties();
15+
16+
/**
17+
* @brief Set the condtional styles for the field UI properties.
18+
* @param styles
19+
*/
20+
void setConditionalStyles( QList<QgsConditionalStyle> styles );
21+
22+
/**
23+
* @brief Returns the condtional styles set for the field UI properties
24+
* @return A list of condtional styles that have been set.
25+
*/
26+
const QList<QgsConditionalStyle> getConditionalStyles();
27+
28+
/**
29+
* @brief Find and return the matching style for the value and feature.
30+
* If no match is found a invalid QgsCondtionalStyle is return.
31+
*
32+
* @return A condtional style that matches the value and feature.
33+
* Check with QgsCondtionalStyle::isValid()
34+
*/
35+
const QgsConditionalStyle matchingConditionalStyle( QVariant value, QgsFeature* feature );
36+
37+
/** Reads field ui properties specific state from Dom node.
38+
*/
39+
virtual bool readXml( const QDomNode& node );
40+
41+
/** Write field ui properties specific state from Dom node.
42+
*/
43+
virtual bool writeXml( QDomNode & node, QDomDocument & doc );
44+
45+
};

python/core/qgsvectorlayer.sip

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
typedef QList<int> QgsAttributeList;
33
typedef QSet<int> QgsAttributeIds;
44

5+
56
class QgsAttributeEditorElement : QObject
67
{
78
%TypeHeaderCode

src/app/qgsattributetabledialog.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ QgsAttributeTableDialog::QgsAttributeTableDialog( QgsVectorLayer *theLayer, QWid
133133
connect( mFilterActionMapper, SIGNAL( mapped( QObject* ) ), SLOT( filterColumnChanged( QObject* ) ) );
134134
connect( mFilterQuery, SIGNAL( returnPressed() ), SLOT( filterQueryAccepted() ) );
135135
connect( mActionApplyFilter, SIGNAL( triggered() ), SLOT( filterQueryAccepted() ) );
136+
connect( mSetStyles, SIGNAL( pressed() ), SLOT( openConditionalStyles() ) );
136137

137138
// info from layer to table
138139
connect( mLayer, SIGNAL( editingStarted() ), this, SLOT( editingToggled() ) );
@@ -729,6 +730,11 @@ void QgsAttributeTableDialog::filterQueryAccepted()
729730
filterQueryChanged( mFilterQuery->text() );
730731
}
731732

733+
void QgsAttributeTableDialog::openConditionalStyles()
734+
{
735+
mMainView->openConditionalStyles();
736+
}
737+
732738
void QgsAttributeTableDialog::setFilterExpression( QString filterString )
733739
{
734740
if ( mCurrentSearchWidgetWrapper == 0 || !mCurrentSearchWidgetWrapper->applyDirectly() )

src/app/qgsattributetabledialog.h

+2
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ class APP_EXPORT QgsAttributeTableDialog : public QDialog, private Ui::QgsAttrib
152152
void filterEdited();
153153
void filterQueryChanged( const QString& query );
154154
void filterQueryAccepted();
155+
void openConditionalStyles();
155156

156157
/**
157158
* update window title
@@ -206,6 +207,7 @@ class APP_EXPORT QgsAttributeTableDialog : public QDialog, private Ui::QgsAttrib
206207
QDockWidget* mDock;
207208
QgsDistanceArea* myDa;
208209

210+
209211
QMenu* mFilterColumnsMenu;
210212
QSignalMapper* mFilterActionMapper;
211213

src/core/CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ SET(QGIS_CORE_SRCS
7979
qgscontexthelp_texts.cpp
8080
qgscoordinatereferencesystem.cpp
8181
qgscoordinatetransform.cpp
82+
qgsconditionalstyle.cpp
8283
qgscredentials.cpp
8384
qgsdartmeasurement.cpp
8485
qgscrscache.cpp
@@ -100,6 +101,7 @@ SET(QGIS_CORE_SRCS
100101
qgsfeaturerequest.cpp
101102
qgsfeaturestore.cpp
102103
qgsfield.cpp
104+
qgsfielduiproperties.cpp
103105
qgsfontutils.cpp
104106
qgsgeometrycache.cpp
105107
qgsgeometrysimplifier.cpp
@@ -516,6 +518,7 @@ SET(QGIS_CORE_HDRS
516518
qgscolorschemeregistry.h
517519
qgsconnectionpool.h
518520
qgscontexthelp.h
521+
qgsconditionalstyle.h
519522
qgscoordinatereferencesystem.h
520523
qgscrscache.h
521524
qgscsexception.h
@@ -541,6 +544,7 @@ SET(QGIS_CORE_HDRS
541544
qgsfeaturerequest.h
542545
qgsfeaturestore.h
543546
qgsfield.h
547+
qgsfielduiproperties.h
544548
qgsfield_p.h
545549
qgsfontutils.h
546550
qgsgeometrycache.h

src/core/qgsconditionalstyle.cpp

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
#include <QPainter>
2+
3+
#include "qgsconditionalstyle.h"
4+
#include "qgsexpression.h"
5+
#include "qgsfontutils.h"
6+
#include "qgssymbollayerv2utils.h"
7+
#include "qgsmarkersymbollayerv2.h"
8+
9+
QgsConditionalStyle::QgsConditionalStyle()
10+
: mValid( false )
11+
, mSymbol( 0 )
12+
{}
13+
14+
QgsConditionalStyle::QgsConditionalStyle( QString rule )
15+
: mValid( false )
16+
, mSymbol( 0 )
17+
{
18+
setRule( rule );
19+
}
20+
21+
QgsConditionalStyle::QgsConditionalStyle( const QgsConditionalStyle &other )
22+
: mValid( other.mValid )
23+
, mRule( other.mRule )
24+
, mFont( other.mFont )
25+
, mBackColor( other.mBackColor )
26+
, mTextColor( other.mTextColor )
27+
, mIcon( other.mIcon )
28+
{
29+
if ( other.mSymbol.data() )
30+
mSymbol.reset( other.mSymbol->clone() );
31+
}
32+
33+
QgsConditionalStyle& QgsConditionalStyle::operator=( const QgsConditionalStyle & other )
34+
{
35+
mValid = other.mValid;
36+
mRule = other.mRule;
37+
mFont = other.mFont;
38+
mBackColor = other.mBackColor;
39+
mTextColor = other.mTextColor;
40+
mIcon = other.mIcon;
41+
if ( other.mSymbol.data() )
42+
{
43+
mSymbol.reset( other.mSymbol->clone() );
44+
}
45+
else
46+
{
47+
mSymbol.reset();
48+
}
49+
return ( *this );
50+
}
51+
52+
QgsConditionalStyle::~QgsConditionalStyle()
53+
{
54+
}
55+
56+
void QgsConditionalStyle::setSymbol( QgsSymbolV2* value )
57+
{
58+
mValid = true;
59+
if ( value )
60+
{
61+
mSymbol.reset( value->clone() );
62+
mIcon = QgsSymbolLayerV2Utils::symbolPreviewPixmap( mSymbol.data(), QSize( 16, 16 ) );
63+
}
64+
else
65+
{
66+
mSymbol.reset();
67+
}
68+
}
69+
70+
bool QgsConditionalStyle::matches( QVariant value, QgsFeature *feature )
71+
{
72+
// TODO Replace with expression context
73+
QgsExpression exp( QString( mRule ).replace( "@value", value.toString() ) );
74+
if ( feature )
75+
{
76+
return exp.evaluate( feature, *feature->fields() ).toBool();
77+
}
78+
{
79+
return exp.evaluate().toBool();
80+
}
81+
}
82+
83+
QPixmap QgsConditionalStyle::renderPreview()
84+
{
85+
QPixmap pixmap( 64, 32 );
86+
QPainter painter( &pixmap );
87+
88+
if ( mBackColor.isValid() )
89+
painter.setBrush( mBackColor );
90+
else
91+
painter.setBrush( QColor( Qt::white ) );
92+
93+
QRect rect = QRect( 0, 0, 64, 32 );
94+
painter.setPen( Qt::NoPen );
95+
painter.drawRect( rect );
96+
painter.drawPixmap( 8, 8, icon() );
97+
98+
if ( mTextColor.isValid() )
99+
painter.setPen( mTextColor );
100+
else
101+
painter.setPen( Qt::black );
102+
103+
painter.setRenderHint( QPainter::Antialiasing );
104+
painter.setRenderHint( QPainter::HighQualityAntialiasing );
105+
painter.setFont( font() );
106+
rect = QRect( 32, 0, 32, 32 );
107+
painter.drawText( rect, Qt::AlignCenter, "abc\n123" );
108+
painter.end();
109+
return pixmap;
110+
}
111+
112+
bool QgsConditionalStyle::writeXml( QDomNode &node, QDomDocument &doc )
113+
{
114+
QDomElement stylesel = doc.createElement( "style" );
115+
stylesel.setAttribute( "rule", mRule );
116+
stylesel.setAttribute( "background_color", mBackColor.name() );
117+
stylesel.setAttribute( "text_color", mTextColor.name() );
118+
QDomElement labelFontElem = QgsFontUtils::toXmlElement( mFont, doc, "font" );
119+
stylesel.appendChild( labelFontElem );
120+
if ( ! mSymbol.isNull() )
121+
{
122+
QDomElement symbolElm = QgsSymbolLayerV2Utils::saveSymbol( "icon", mSymbol.data(), doc );
123+
stylesel.appendChild( symbolElm );
124+
}
125+
node.appendChild( stylesel );
126+
return true;
127+
}
128+
129+
bool QgsConditionalStyle::readXml( const QDomNode &node )
130+
{
131+
QDomElement styleElm = node.toElement();
132+
setRule( styleElm.attribute( "rule" ) );
133+
setBackgroundColor( QColor( styleElm.attribute( "background_color" ) ) );
134+
setTextColor( QColor( styleElm.attribute( "text_color" ) ) );
135+
QgsFontUtils::setFromXmlChildNode( mFont, styleElm, "font" );
136+
QDomElement symbolElm = styleElm.firstChildElement( "symbol" );
137+
if ( !symbolElm.isNull() )
138+
{
139+
QgsSymbolV2* symbol = QgsSymbolLayerV2Utils::loadSymbol<QgsMarkerSymbolV2>( symbolElm );
140+
setSymbol( symbol );
141+
}
142+
return true;
143+
}
144+

0 commit comments

Comments
 (0)