Skip to content

Commit

Permalink
[FEATURE] Conditional formatting for attribute table cells
Browse files Browse the repository at this point in the history
  • Loading branch information
NathanW2 committed Aug 20, 2015
1 parent 3ff3b02 commit faa32f3
Show file tree
Hide file tree
Showing 27 changed files with 1,764 additions and 66 deletions.
2 changes: 2 additions & 0 deletions python/core/core.sip
Expand Up @@ -22,6 +22,7 @@
%Include qgscontexthelp.sip
%Include qgscoordinatereferencesystem.sip
%Include qgscoordinatetransform.sip
%Include qgsconditionalstyle.sip
%Include qgscredentials.sip
%Include qgscrscache.sip
%Include qgsdatadefined.sip
Expand All @@ -40,6 +41,7 @@
%Include qgsfeatureiterator.sip
%Include qgsfeaturerequest.sip
%Include qgsfield.sip
%Include qgsfielduiproperties.sip
%Include qgsgeometryvalidator.sip
%Include qgsgeometrysimplifier.sip
%Include qgshistogram.sip
Expand Down
107 changes: 107 additions & 0 deletions python/core/qgsconditionalstyle.sip
@@ -0,0 +1,107 @@
/** \class QgsCondtionalStyle
* \ingroup core
* Conditional styling for a rule.
*/
class QgsConditionalStyle
{
%TypeHeaderCode
#include <qgsconditionalstyle.h>
%End
public:
QgsConditionalStyle();
QgsConditionalStyle( QString rule );

/**
* @brief Check if the rule matches using the given value and feature
* @param feature The feature to match the values from.
* @return True of the rule matches against the given feature
*/
bool matches( QVariant value, QgsFeature *feature = 0 );

/**
* @brief Render a preview icon of the rule.
* @return QPixmap preview of the style
*/
QPixmap renderPreview();

/**
* @brief Set the rule for the style. Rules should be of QgsExpression syntax.
* Special value of @value is replaced at run time with the check value
* @param value
*/
void setRule( QString value );

/**
* @brief Set the background color for the style
* @param value QColor for background color
*/
void setBackgroundColor( QColor value );

/**
* @brief Set the text color for the style
* @param value QColor for text color
*/
void setTextColor( QColor value );

/**
* @brief Set the font for the the style
* @param value QFont to be used for text
*/
void setFont( QFont value );

/**
* @brief Set the icon for the style. Icons are generated from symbols
* @param value QgsSymbolV2 to be used when generating the icon
*/
void setSymbol( QgsSymbolV2* value );

/**
* @brief The symbol used to generate the icon for the style
* @return The QgsSymbolV2 used for the icon
*/
QgsSymbolV2* symbol() const;

/**
* @brief The icon set for style
* @return A QPixmap that was set for the icon
*/
QPixmap icon() const;

/**
* @brief The text color set for style
* @return QColor for text color
*/
QColor textColor() const;

/**
* @brief The background color for style
* @return QColor for background color
*/
QColor backgroundColor() const;
/**
* @brief The font for the style
* @return QFont for the style
*/
QFont font() const;

/**
* @brief The condtion rule set for the style. Rule may contain variable @value
* to represent the current value
* @return QString of the current set rule
*/
QString rule() const;
/**
* @brief isValid Check if this rule is valid. A valid rule has one or more properties
* set.
* @return True if the rule is valid.
*/
bool isValid() const;

/** Reads vector conditional style specific state from layer Dom node.
*/
virtual bool readXml( const QDomNode& node );

/** Write vector conditional style specific state from layer Dom node.
*/
virtual bool writeXml( QDomNode & node, QDomDocument & doc );
};
45 changes: 45 additions & 0 deletions python/core/qgsfielduiproperties.sip
@@ -0,0 +1,45 @@
/** \class QgsFieldUIProperties
* \ingroup core
* Holds extra UI properties for a field.
*
* Currently this object holds informations about condtional styles but in future will hold
* things like field widgets, etc
*/
class QgsFieldUIProperties
{
%TypeHeaderCode
#include <qgsfielduiproperties.h>
%End
public:
QgsFieldUIProperties();

/**
* @brief Set the condtional styles for the field UI properties.
* @param styles
*/
void setConditionalStyles( QList<QgsConditionalStyle> styles );

/**
* @brief Returns the condtional styles set for the field UI properties
* @return A list of condtional styles that have been set.
*/
const QList<QgsConditionalStyle> getConditionalStyles();

/**
* @brief Find and return the matching style for the value and feature.
* If no match is found a invalid QgsCondtionalStyle is return.
*
* @return A condtional style that matches the value and feature.
* Check with QgsCondtionalStyle::isValid()
*/
const QgsConditionalStyle matchingConditionalStyle( QVariant value, QgsFeature* feature );

/** Reads field ui properties specific state from Dom node.
*/
virtual bool readXml( const QDomNode& node );

/** Write field ui properties specific state from Dom node.
*/
virtual bool writeXml( QDomNode & node, QDomDocument & doc );

};
1 change: 1 addition & 0 deletions python/core/qgsvectorlayer.sip
Expand Up @@ -2,6 +2,7 @@
typedef QList<int> QgsAttributeList;
typedef QSet<int> QgsAttributeIds;


class QgsAttributeEditorElement : QObject
{
%TypeHeaderCode
Expand Down
6 changes: 6 additions & 0 deletions src/app/qgsattributetabledialog.cpp
Expand Up @@ -133,6 +133,7 @@ QgsAttributeTableDialog::QgsAttributeTableDialog( QgsVectorLayer *theLayer, QWid
connect( mFilterActionMapper, SIGNAL( mapped( QObject* ) ), SLOT( filterColumnChanged( QObject* ) ) );
connect( mFilterQuery, SIGNAL( returnPressed() ), SLOT( filterQueryAccepted() ) );
connect( mActionApplyFilter, SIGNAL( triggered() ), SLOT( filterQueryAccepted() ) );
connect( mSetStyles, SIGNAL( pressed() ), SLOT( openConditionalStyles() ) );

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

void QgsAttributeTableDialog::openConditionalStyles()
{
mMainView->openConditionalStyles();
}

void QgsAttributeTableDialog::setFilterExpression( QString filterString )
{
if ( mCurrentSearchWidgetWrapper == 0 || !mCurrentSearchWidgetWrapper->applyDirectly() )
Expand Down
2 changes: 2 additions & 0 deletions src/app/qgsattributetabledialog.h
Expand Up @@ -152,6 +152,7 @@ class APP_EXPORT QgsAttributeTableDialog : public QDialog, private Ui::QgsAttrib
void filterEdited();
void filterQueryChanged( const QString& query );
void filterQueryAccepted();
void openConditionalStyles();

/**
* update window title
Expand Down Expand Up @@ -206,6 +207,7 @@ class APP_EXPORT QgsAttributeTableDialog : public QDialog, private Ui::QgsAttrib
QDockWidget* mDock;
QgsDistanceArea* myDa;


QMenu* mFilterColumnsMenu;
QSignalMapper* mFilterActionMapper;

Expand Down
4 changes: 4 additions & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -79,6 +79,7 @@ SET(QGIS_CORE_SRCS
qgscontexthelp_texts.cpp
qgscoordinatereferencesystem.cpp
qgscoordinatetransform.cpp
qgsconditionalstyle.cpp
qgscredentials.cpp
qgsdartmeasurement.cpp
qgscrscache.cpp
Expand All @@ -100,6 +101,7 @@ SET(QGIS_CORE_SRCS
qgsfeaturerequest.cpp
qgsfeaturestore.cpp
qgsfield.cpp
qgsfielduiproperties.cpp
qgsfontutils.cpp
qgsgeometrycache.cpp
qgsgeometrysimplifier.cpp
Expand Down Expand Up @@ -516,6 +518,7 @@ SET(QGIS_CORE_HDRS
qgscolorschemeregistry.h
qgsconnectionpool.h
qgscontexthelp.h
qgsconditionalstyle.h
qgscoordinatereferencesystem.h
qgscrscache.h
qgscsexception.h
Expand All @@ -541,6 +544,7 @@ SET(QGIS_CORE_HDRS
qgsfeaturerequest.h
qgsfeaturestore.h
qgsfield.h
qgsfielduiproperties.h
qgsfield_p.h
qgsfontutils.h
qgsgeometrycache.h
Expand Down
144 changes: 144 additions & 0 deletions src/core/qgsconditionalstyle.cpp
@@ -0,0 +1,144 @@
#include <QPainter>

#include "qgsconditionalstyle.h"
#include "qgsexpression.h"
#include "qgsfontutils.h"
#include "qgssymbollayerv2utils.h"
#include "qgsmarkersymbollayerv2.h"

QgsConditionalStyle::QgsConditionalStyle()
: mValid( false )
, mSymbol( 0 )
{}

QgsConditionalStyle::QgsConditionalStyle( QString rule )
: mValid( false )
, mSymbol( 0 )
{
setRule( rule );
}

QgsConditionalStyle::QgsConditionalStyle( const QgsConditionalStyle &other )
: mValid( other.mValid )
, mRule( other.mRule )
, mFont( other.mFont )
, mBackColor( other.mBackColor )
, mTextColor( other.mTextColor )
, mIcon( other.mIcon )
{
if ( other.mSymbol.data() )
mSymbol.reset( other.mSymbol->clone() );
}

QgsConditionalStyle& QgsConditionalStyle::operator=( const QgsConditionalStyle & other )
{
mValid = other.mValid;
mRule = other.mRule;
mFont = other.mFont;
mBackColor = other.mBackColor;
mTextColor = other.mTextColor;
mIcon = other.mIcon;
if ( other.mSymbol.data() )
{
mSymbol.reset( other.mSymbol->clone() );
}
else
{
mSymbol.reset();
}
return ( *this );
}

QgsConditionalStyle::~QgsConditionalStyle()
{
}

void QgsConditionalStyle::setSymbol( QgsSymbolV2* value )
{
mValid = true;
if ( value )
{
mSymbol.reset( value->clone() );
mIcon = QgsSymbolLayerV2Utils::symbolPreviewPixmap( mSymbol.data(), QSize( 16, 16 ) );
}
else
{
mSymbol.reset();
}
}

bool QgsConditionalStyle::matches( QVariant value, QgsFeature *feature )
{
// TODO Replace with expression context
QgsExpression exp( QString( mRule ).replace( "@value", value.toString() ) );
if ( feature )
{
return exp.evaluate( feature, *feature->fields() ).toBool();
}
{
return exp.evaluate().toBool();
}
}

QPixmap QgsConditionalStyle::renderPreview()
{
QPixmap pixmap( 64, 32 );
QPainter painter( &pixmap );

if ( mBackColor.isValid() )
painter.setBrush( mBackColor );
else
painter.setBrush( QColor( Qt::white ) );

QRect rect = QRect( 0, 0, 64, 32 );
painter.setPen( Qt::NoPen );
painter.drawRect( rect );
painter.drawPixmap( 8, 8, icon() );

if ( mTextColor.isValid() )
painter.setPen( mTextColor );
else
painter.setPen( Qt::black );

painter.setRenderHint( QPainter::Antialiasing );
painter.setRenderHint( QPainter::HighQualityAntialiasing );
painter.setFont( font() );
rect = QRect( 32, 0, 32, 32 );
painter.drawText( rect, Qt::AlignCenter, "abc\n123" );
painter.end();
return pixmap;
}

bool QgsConditionalStyle::writeXml( QDomNode &node, QDomDocument &doc )
{
QDomElement stylesel = doc.createElement( "style" );
stylesel.setAttribute( "rule", mRule );
stylesel.setAttribute( "background_color", mBackColor.name() );
stylesel.setAttribute( "text_color", mTextColor.name() );
QDomElement labelFontElem = QgsFontUtils::toXmlElement( mFont, doc, "font" );
stylesel.appendChild( labelFontElem );
if ( ! mSymbol.isNull() )
{
QDomElement symbolElm = QgsSymbolLayerV2Utils::saveSymbol( "icon", mSymbol.data(), doc );
stylesel.appendChild( symbolElm );
}
node.appendChild( stylesel );
return true;
}

bool QgsConditionalStyle::readXml( const QDomNode &node )
{
QDomElement styleElm = node.toElement();
setRule( styleElm.attribute( "rule" ) );
setBackgroundColor( QColor( styleElm.attribute( "background_color" ) ) );
setTextColor( QColor( styleElm.attribute( "text_color" ) ) );
QgsFontUtils::setFromXmlChildNode( mFont, styleElm, "font" );
QDomElement symbolElm = styleElm.firstChildElement( "symbol" );
if ( !symbolElm.isNull() )
{
QgsSymbolV2* symbol = QgsSymbolLayerV2Utils::loadSymbol<QgsMarkerSymbolV2>( symbolElm );
setSymbol( symbol );
}
return true;
}

0 comments on commit faa32f3

Please sign in to comment.