Skip to content

Commit

Permalink
Address comment, optimize and hard-code conditional styling of constr…
Browse files Browse the repository at this point in the history
…aints
  • Loading branch information
nirvn committed Jan 13, 2023
1 parent 917883f commit 1025006
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 131 deletions.
2 changes: 2 additions & 0 deletions images/themes/default/mActionOpenTableInvalid.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 5 additions & 17 deletions python/core/auto_generated/qgsconditionalstyle.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@



typedef QList<QgsConditionalStyle> QgsConditionalStyles;

typedef QList<QgsConditionalStyle> QgsConditionalStyles;

class QgsConditionalLayerStyles : QObject
{
Expand Down Expand Up @@ -45,23 +45,11 @@ Each row will check be checked against each rule.
.. versionadded:: 2.12
%End

QgsConditionalStyles constraintStyles() const;
QgsConditionalStyle constraintFailureStyles( QgsFieldConstraints::ConstraintStrength strength );
%Docstring
Returns a list of row styles associated with layer constraints.

.. seealso:: :py:func:`setConstraintStyles`

.. versionadded:: 3.30
%End

void setConstraintStyles( const QgsConditionalStyles &styles );
%Docstring
Sets the conditional ``styles`` for fields constraint.

The field name is inserted into a @value variable to conduct
expressionchecks.
Returns a style associated to a constraint failure.

.. seealso:: :py:func:`constraintStyles`
:param strength: the type of constraint

.. versionadded:: 3.30
%End
Expand All @@ -70,7 +58,7 @@ expressionchecks.
%Docstring
Set the conditional ``styles`` for a field, with the specified ``fieldName``.

The field value is inserted into a @value variable to conduct
The field value is inserted into a 'value' variable to conduct
expression checks.

.. seealso:: :py:func:`fieldStyles`
Expand Down
10 changes: 10 additions & 0 deletions python/core/auto_generated/vector/qgsvectorlayerutils.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,16 @@ be unique within regard to ``existingValues``.
The optional seed value can be used as a basis for generated values.

.. versionadded:: 3.6
%End

static bool attributeHasConstraints( const QgsVectorLayer *layer, int attributeIndex );
%Docstring
Returns ``True`` if a feature attribute has active constraints.

:param layer: the vector layer from which field constraints will be checked for
:param attributeIndex: the attribute index

.. versionadded:: 3.30
%End

static bool validateAttribute( const QgsVectorLayer *layer, const QgsFeature &feature, int attributeIndex, QStringList &errors /Out/,
Expand Down
26 changes: 19 additions & 7 deletions src/core/layout/qgslayoutitemattributetable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "qgslayoututils.h"
#include "qgsfeatureiterator.h"
#include "qgsvectorlayer.h"
#include "qgsvectorlayerutils.h"
#include "qgslayoutframe.h"
#include "qgsproject.h"
#include "qgsrelationmanager.h"
Expand Down Expand Up @@ -575,16 +576,27 @@ bool QgsLayoutItemAttributeTable::getTableContents( QgsLayoutTableContents &cont

if ( mUseConditionalStyling )
{
const QString fieldName = layer->fields().at( idx ).name();

QList<QgsConditionalStyle> styles = conditionalStyles->constraintStyles();
styles = QgsConditionalStyle::matchingConditionalStyles( styles, fieldName, context );
const QgsConditionalStyle constraintStyle = QgsConditionalStyle::compressStyles( styles );
QgsConditionalStyle constraintstyle;
if ( QgsVectorLayerUtils::attributeHasConstraints( layer, idx ) )
{
QStringList errors;
if ( !QgsVectorLayerUtils::validateAttribute( layer, f, idx, errors, QgsFieldConstraints::ConstraintStrengthHard ) )
{
constraintstyle = layer->conditionalStyles()->constraintFailureStyles( QgsFieldConstraints::ConstraintStrengthHard );
}
else
{
if ( !QgsVectorLayerUtils::validateAttribute( layer, f, idx, errors, QgsFieldConstraints::ConstraintStrengthSoft ) )
{
constraintstyle = layer->conditionalStyles()->constraintFailureStyles( QgsFieldConstraints::ConstraintStrengthSoft );
}
}
}

styles = conditionalStyles->fieldStyles( fieldName );
QList<QgsConditionalStyle> styles = conditionalStyles->fieldStyles( layer->fields().at( idx ).name() );
styles = QgsConditionalStyle::matchingConditionalStyles( styles, val, context );
styles.insert( 0, rowStyle );
styles.insert( 0, constraintStyle );
styles.insert( 0, constraintstyle );
style = QgsConditionalStyle::compressStyles( styles );
}

Expand Down
56 changes: 26 additions & 30 deletions src/core/qgsconditionalstyle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,33 @@ QgsConditionalLayerStyles::QgsConditionalLayerStyles( QObject *parent )
{
}

QgsConditionalStyles QgsConditionalLayerStyles::constraintStyles() const
QgsConditionalStyle QgsConditionalLayerStyles::constraintFailureStyles( QgsFieldConstraints::ConstraintStrength strength )
{
return mConstraintStyles;
}

void QgsConditionalLayerStyles::setConstraintStyles( const QgsConditionalStyles &styles )
{
if ( styles == mConstraintStyles )
return;
switch ( strength )
{
case QgsFieldConstraints::ConstraintStrengthHard:
if ( !mHardConstraintFailureStyle )
{
mHardConstraintFailureStyle = std::make_unique<QgsConditionalStyle>();
mHardConstraintFailureStyle->setBackgroundColor( QColor( 255, 152, 0 ) );
mHardConstraintFailureStyle->setTextColor( QColor( 0, 0, 0 ) );
}
return *mHardConstraintFailureStyle.get();

case QgsFieldConstraints::ConstraintStrengthSoft:
if ( !mSoftConstraintFailureStyle )
{
mSoftConstraintFailureStyle = std::make_unique<QgsConditionalStyle>();
mSoftConstraintFailureStyle->setBackgroundColor( QColor( 255, 191, 12 ) );
mSoftConstraintFailureStyle->setTextColor( QColor( 0, 0, 0 ) );
}
return *mSoftConstraintFailureStyle.get();

case QgsFieldConstraints::ConstraintStrengthNotSet:
return QgsConditionalStyle();
}

mConstraintStyles = styles;
emit changed();
return QgsConditionalStyle();
}

QgsConditionalStyles QgsConditionalLayerStyles::rowStyles() const
Expand Down Expand Up @@ -72,14 +87,6 @@ bool QgsConditionalLayerStyles::writeXml( QDomNode &node, QDomDocument &doc, con
{
QDomElement stylesel = doc.createElement( QStringLiteral( "conditionalstyles" ) );

QDomElement constraintel = doc.createElement( QStringLiteral( "constraintstyles" ) );
const auto constMConstraintStyles = mConstraintStyles;
for ( const QgsConditionalStyle &style : constMConstraintStyles )
{
style.writeXml( constraintel, doc, context );
}
stylesel.appendChild( constraintel );

QDomElement rowel = doc.createElement( QStringLiteral( "rowstyles" ) );
const auto constMRowStyles = mRowStyles;
for ( const QgsConditionalStyle &style : constMRowStyles )
Expand Down Expand Up @@ -122,24 +129,13 @@ bool QgsConditionalLayerStyles::rulesNeedGeometry() const

bool QgsConditionalLayerStyles::readXml( const QDomNode &node, const QgsReadWriteContext &context )
{
mConstraintStyles.clear();
mRowStyles.clear();
mFieldStyles.clear();

const QDomElement condel = node.firstChildElement( QStringLiteral( "conditionalstyles" ) );

const QDomElement constraintstylesel = condel.firstChildElement( QStringLiteral( "constraintstyles" ) );
QDomNodeList nodelist = constraintstylesel.toElement().elementsByTagName( QStringLiteral( "style" ) );
for ( int i = 0; i < nodelist.count(); i++ )
{
const QDomElement styleElm = nodelist.at( i ).toElement();
QgsConditionalStyle style = QgsConditionalStyle();
style.readXml( styleElm, context );
mConstraintStyles.append( style );
}

const QDomElement rowstylesel = condel.firstChildElement( QStringLiteral( "rowstyles" ) );
nodelist = rowstylesel.toElement().elementsByTagName( QStringLiteral( "style" ) );
QDomNodeList nodelist = rowstylesel.toElement().elementsByTagName( QStringLiteral( "style" ) );
for ( int i = 0; i < nodelist.count(); i++ )
{
const QDomElement styleElm = nodelist.at( i ).toElement();
Expand Down
26 changes: 8 additions & 18 deletions src/core/qgsconditionalstyle.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#define QGSCONDITIONALSTYLE_H

#include "qgis_core.h"
#include "qgsfield.h"

#include <QObject>
#include <QFont>
#include <QColor>
Expand All @@ -32,7 +34,6 @@ class QgsSymbol;

typedef QList<QgsConditionalStyle> QgsConditionalStyles;


/**
* \ingroup core
* \brief The QgsConditionalLayerStyles class holds conditional style information
Expand Down Expand Up @@ -66,28 +67,16 @@ class CORE_EXPORT QgsConditionalLayerStyles : public QObject
void setRowStyles( const QgsConditionalStyles &styles );

/**
* Returns a list of row styles associated with layer constraints.
*
* \see setConstraintStyles()
* \since QGIS 3.30
*/
QgsConditionalStyles constraintStyles() const;

/**
* Sets the conditional \a styles for fields constraint.
*
* The field name is inserted into a @value variable to conduct
* expressionchecks.
*
* \see constraintStyles()
* Returns a style associated to a constraint failure.
* \param strength the type of constraint
* \since QGIS 3.30
*/
void setConstraintStyles( const QgsConditionalStyles &styles );
QgsConditionalStyle constraintFailureStyles( QgsFieldConstraints::ConstraintStrength strength );

/**
* Set the conditional \a styles for a field, with the specified \a fieldName.
*
* The field value is inserted into a @value variable to conduct
* The field value is inserted into a 'value' variable to conduct
* expression checks.
*
* \see fieldStyles()
Expand Down Expand Up @@ -133,7 +122,8 @@ class CORE_EXPORT QgsConditionalLayerStyles : public QObject
private:
QHash<QString, QgsConditionalStyles> mFieldStyles;
QgsConditionalStyles mRowStyles;
QgsConditionalStyles mConstraintStyles;
std::unique_ptr<QgsConditionalStyle> mHardConstraintFailureStyle;
std::unique_ptr<QgsConditionalStyle> mSoftConstraintFailureStyle;
};

/**
Expand Down
14 changes: 14 additions & 0 deletions src/core/vector/qgsvectorlayerutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,20 @@ QVariant QgsVectorLayerUtils::createUniqueValueFromCache( const QgsVectorLayer *

}

bool QgsVectorLayerUtils::attributeHasConstraints( const QgsVectorLayer *layer, int attributeIndex )
{
if ( !layer )
return false;

if ( attributeIndex < 0 || attributeIndex >= layer->fields().count() )
return false;

const QgsFieldConstraints constraints = layer->fields().at( attributeIndex ).constraints();
return ( constraints.constraints() & QgsFieldConstraints::ConstraintNotNull ||
constraints.constraints() & QgsFieldConstraints::ConstraintUnique ||
constraints.constraints() & QgsFieldConstraints::ConstraintExpression );
}

bool QgsVectorLayerUtils::validateAttribute( const QgsVectorLayer *layer, const QgsFeature &feature, int attributeIndex, QStringList &errors,
QgsFieldConstraints::ConstraintStrength strength, QgsFieldConstraints::ConstraintOrigin origin )
{
Expand Down
8 changes: 8 additions & 0 deletions src/core/vector/qgsvectorlayerutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,14 @@ class CORE_EXPORT QgsVectorLayerUtils
*/
static QVariant createUniqueValueFromCache( const QgsVectorLayer *layer, int fieldIndex, const QSet<QVariant> &existingValues, const QVariant &seed = QVariant() );

/**
* Returns TRUE if a feature attribute has active constraints.
* \param layer the vector layer from which field constraints will be checked for
* \param attributeIndex the attribute index
* \since QGIS 3.30
*/
static bool attributeHasConstraints( const QgsVectorLayer *layer, int attributeIndex );

/**
* Tests a feature attribute value to check whether it passes all constraints which are present on the corresponding field.
* Returns TRUE if the attribute value is valid for the field. Any constraint failures will be reported in the errors argument.
Expand Down
29 changes: 27 additions & 2 deletions src/gui/attributetable/qgsattributetablemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,7 @@ void QgsAttributeTableModel::fieldConditionalStyleChanged( const QString &fieldN
if ( fieldName.isNull() )
{
mRowStylesMap.clear();
mConstraintStylesMap.clear();
emit dataChanged( index( 0, 0 ), index( rowCount() - 1, columnCount() - 1 ) );
return;
}
Expand Down Expand Up @@ -772,8 +773,31 @@ QVariant QgsAttributeTableModel::data( const QModelIndex &index, int role ) cons
}
const QgsConditionalStyle rowstyle = QgsConditionalStyle::compressStyles( styles );

styles = QgsConditionalStyle::matchingConditionalStyles( mLayer->conditionalStyles()->constraintStyles(), field.name(), mExpressionContext );
const QgsConditionalStyle constraintstyle = QgsConditionalStyle::compressStyles( styles );
QgsConditionalStyle constraintstyle;
if ( QgsVectorLayerUtils::attributeHasConstraints( mLayer, fieldId ) )
{
if ( mConstraintStylesMap.contains( mFeat.id() ) &&
mConstraintStylesMap[mFeat.id()].contains( fieldId ) )
{
constraintstyle = mConstraintStylesMap[mFeat.id()][fieldId];
}
else
{
QStringList errors;
if ( !QgsVectorLayerUtils::validateAttribute( mLayer, mFeat, fieldId, errors, QgsFieldConstraints::ConstraintStrengthHard ) )
{
constraintstyle = mLayer->conditionalStyles()->constraintFailureStyles( QgsFieldConstraints::ConstraintStrengthHard );
}
else
{
if ( !QgsVectorLayerUtils::validateAttribute( mLayer, mFeat, fieldId, errors, QgsFieldConstraints::ConstraintStrengthSoft ) )
{
constraintstyle = mLayer->conditionalStyles()->constraintFailureStyles( QgsFieldConstraints::ConstraintStrengthSoft );
}
}
mConstraintStylesMap[mFeat.id()].insert( fieldId, constraintstyle );
}
}

styles = mLayer->conditionalStyles()->fieldStyles( field.name() );
styles = QgsConditionalStyle::matchingConditionalStyles( styles, val, mExpressionContext );
Expand Down Expand Up @@ -821,6 +845,7 @@ bool QgsAttributeTableModel::setData( const QModelIndex &index, const QVariant &
return false;

mRowStylesMap.remove( mFeat.id() );
mConstraintStylesMap.remove( mFeat.id() );

if ( !mLayer->isModified() )
return false;
Expand Down
1 change: 1 addition & 0 deletions src/gui/attributetable/qgsattributetablemodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
QHash<QgsFeatureId, int> mIdRowMap;
QHash<int, QgsFeatureId> mRowIdMap;
mutable QHash<QgsFeatureId, QList<QgsConditionalStyle> > mRowStylesMap;
mutable QHash<QgsFeatureId, QHash<int, QgsConditionalStyle> > mConstraintStylesMap;

mutable QgsExpressionContext mExpressionContext;

Expand Down
Loading

0 comments on commit 1025006

Please sign in to comment.