Skip to content

Commit

Permalink
[FEATURE] constraints on widgets
Browse files Browse the repository at this point in the history
  • Loading branch information
pblottiere committed May 31, 2016
1 parent 4ae1b55 commit 020d20a
Show file tree
Hide file tree
Showing 29 changed files with 782 additions and 41 deletions.
16 changes: 16 additions & 0 deletions python/core/qgseditformconfig.sip
Expand Up @@ -476,6 +476,22 @@ class QgsEditFormConfig : QObject
*/
void setReadOnly( int idx, bool readOnly = true );

/**
* Returns the constraint expression of a specific field
* @param idx The index of the field
* @return the expression
* @note added in QGIS 2.16
*/
QString constraint( int idx ) const;

/**
* Set the constraint expression for a specific field
* @param idx the field index
* @param str the constraint expression
* @note added in QGIS 2.16
*/
void setConstraint( int idx, const QString& str );

/**
* Returns if the field at fieldidx should be treated as NOT NULL value
*/
Expand Down
35 changes: 35 additions & 0 deletions python/gui/editorwidgets/core/qgseditorwidgetwrapper.sip
Expand Up @@ -88,6 +88,21 @@ class QgsEditorWidgetWrapper : QgsWidgetWrapper
*/
virtual void showIndeterminateState();

/**
* Update constraint.
* @param featureContext the feature to use to evaluate the constraint
* @note added in QGIS 2.16
*/
void updateConstraint( const QgsFeature &featureContext );

/**
* Get the current constraint status.
* @return true if the constraint is valid or if there's not constraint,
* false otherwise
* @note added in QGIS 2.16
*/
bool isValidConstraint() const;

signals:
/**
* Emit this signal, whenever the value changed.
Expand All @@ -96,6 +111,13 @@ class QgsEditorWidgetWrapper : QgsWidgetWrapper
*/
void valueChanged( const QVariant& value );

/**
* @brief constraintStatusChanged
* @param constraint
* @param status
*/
void constraintStatusChanged( const QString& constraint, const QString& err, bool status );

public slots:
/**
* Will be called when the feature changes
Expand Down Expand Up @@ -162,4 +184,17 @@ class QgsEditorWidgetWrapper : QgsWidgetWrapper
* Will call the value() method to determine the emitted value
*/
void valueChanged();

protected:
/**
* This should update the widget with a visual cue if a constraint status
* changed.
*
* By default a stylesheet will be applied on the widget that changes the
* background color to red.
*
* This can be overwritten in subclasses to allow individual widgets to
* change the visual cue.
*/
virtual void updateConstraintWidgetStatus();
};
14 changes: 14 additions & 0 deletions python/gui/editorwidgets/qgsrelationreferencewidgetwrapper.sip
Expand Up @@ -21,4 +21,18 @@ class QgsRelationReferenceWidgetWrapper : QgsEditorWidgetWrapper
public slots:
virtual void setValue( const QVariant& value );
virtual void setEnabled( bool enabled );

protected:
/**
* This should update the widget with a visual cue if a constraint status
* changed.
*
* By default a stylesheet will be applied on the widget that changes the
* background color to red.
*
* This can be overwritten in subclasses to allow individual widgets to
* change the visual cue.
* @note added in QGIS 2.16
*/
void updateConstraintWidgetStatus();
};
12 changes: 12 additions & 0 deletions src/app/qgsattributetypedialog.cpp
Expand Up @@ -71,6 +71,8 @@ QgsAttributeTypeDialog::QgsAttributeTypeDialog( QgsVectorLayer *vl, int fieldIdx

QSettings settings;
restoreGeometry( settings.value( "/Windows/QgsAttributeTypeDialog/geometry" ).toByteArray() );

constraintExpression->setLayer( vl );
}

QgsAttributeTypeDialog::~QgsAttributeTypeDialog()
Expand Down Expand Up @@ -183,6 +185,16 @@ bool QgsAttributeTypeDialog::notNull() const
return notNullCheckBox->isChecked();
}

void QgsAttributeTypeDialog::setConstraint( const QString &str )
{
constraintExpression->setField( str );
}

QString QgsAttributeTypeDialog::constraint() const
{
return constraintExpression->asExpression();
}

void QgsAttributeTypeDialog::setFieldEditable( bool editable )
{
isFieldEditableCheckBox->setChecked( editable );
Expand Down
12 changes: 12 additions & 0 deletions src/app/qgsattributetypedialog.h
Expand Up @@ -94,6 +94,18 @@ class APP_EXPORT QgsAttributeTypeDialog: public QDialog, private Ui::QgsAttribut
*/
bool notNull() const;

/**
* Getter for the constraint expression
* @note added in QGIS 2.16
*/
QString constraint() const;

/**
* Setter for the constraint expression
* @note added in QGIS 2.16
*/
void setConstraint( const QString &str );

private slots:
/**
* Slot to handle change of index in combobox to select correct page
Expand Down
4 changes: 4 additions & 0 deletions src/app/qgsfieldsproperties.cpp
Expand Up @@ -528,6 +528,7 @@ void QgsFieldsProperties::attributeTypeDialog()
attributeTypeDialog.setFieldEditable( cfg.mEditable );
attributeTypeDialog.setLabelOnTop( cfg.mLabelOnTop );
attributeTypeDialog.setNotNull( cfg.mNotNull );
attributeTypeDialog.setConstraint( cfg.mConstraint );

attributeTypeDialog.setWidgetV2Config( cfg.mEditorWidgetV2Config );
attributeTypeDialog.setWidgetV2Type( cfg.mEditorWidgetV2Type );
Expand All @@ -538,6 +539,7 @@ void QgsFieldsProperties::attributeTypeDialog()
cfg.mEditable = attributeTypeDialog.fieldEditable();
cfg.mLabelOnTop = attributeTypeDialog.labelOnTop();
cfg.mNotNull = attributeTypeDialog.notNull();
cfg.mConstraint = attributeTypeDialog.constraint();

cfg.mEditorWidgetV2Type = attributeTypeDialog.editorWidgetV2Type();
cfg.mEditorWidgetV2Config = attributeTypeDialog.editorWidgetV2Config();
Expand Down Expand Up @@ -911,6 +913,7 @@ void QgsFieldsProperties::apply()
mLayer->editFormConfig()->setReadOnly( i, !cfg.mEditable );
mLayer->editFormConfig()->setLabelOnTop( i, cfg.mLabelOnTop );
mLayer->editFormConfig()->setNotNull( i, cfg.mNotNull );
mLayer->editFormConfig()->setConstraint( i, cfg.mConstraint );

mLayer->editFormConfig()->setWidgetType( idx, cfg.mEditorWidgetV2Type );
mLayer->editFormConfig()->setWidgetConfig( idx, cfg.mEditorWidgetV2Config );
Expand Down Expand Up @@ -990,6 +993,7 @@ QgsFieldsProperties::FieldConfig::FieldConfig( QgsVectorLayer* layer, int idx )
&& layer->fields().fieldOrigin( idx ) != QgsFields::OriginExpression;
mLabelOnTop = layer->editFormConfig()->labelOnTop( idx );
mNotNull = layer->editFormConfig()->notNull( idx );
mConstraint = layer->editFormConfig()->constraint( idx );
mEditorWidgetV2Type = layer->editFormConfig()->widgetType( idx );
mEditorWidgetV2Config = layer->editFormConfig()->widgetConfig( idx );

Expand Down
1 change: 1 addition & 0 deletions src/app/qgsfieldsproperties.h
Expand Up @@ -93,6 +93,7 @@ class APP_EXPORT QgsFieldsProperties : public QWidget, private Ui_QgsFieldsPrope
bool mEditableEnabled;
bool mLabelOnTop;
bool mNotNull;
QString mConstraint;
QPushButton* mButton;
QString mEditorWidgetV2Type;
QMap<QString, QVariant> mEditorWidgetV2Config;
Expand Down
16 changes: 16 additions & 0 deletions src/core/qgseditformconfig.cpp
Expand Up @@ -119,6 +119,22 @@ bool QgsEditFormConfig::labelOnTop( int idx ) const
return false;
}

QString QgsEditFormConfig::constraint( int idx ) const
{
QString expr = "";

if ( idx >= 0 && idx < mFields.count() )
expr = mConstraints.value( mFields.at( idx ).name(), "" );

return expr;
}

void QgsEditFormConfig::setConstraint( int idx, const QString& str )
{
if ( idx >= 0 && idx < mFields.count() )
mConstraints[ mFields.at( idx ).name()] = str;
}

bool QgsEditFormConfig::notNull( int idx ) const
{
if ( idx >= 0 && idx < mFields.count() )
Expand Down
17 changes: 17 additions & 0 deletions src/core/qgseditformconfig.h
Expand Up @@ -512,6 +512,22 @@ class CORE_EXPORT QgsEditFormConfig : public QObject
*/
void setReadOnly( int idx, bool readOnly = true );

/**
* Returns the constraint expression of a specific field
* @param idx The index of the field
* @return the expression
* @note added in QGIS 2.16
*/
QString constraint( int idx ) const;

/**
* Set the constraint expression for a specific field
* @param idx the field index
* @param str the constraint expression
* @note added in QGIS 2.16
*/
void setConstraint( int idx, const QString& str );

/**
* Returns if the field at fieldidx should be treated as NOT NULL value
*/
Expand Down Expand Up @@ -640,6 +656,7 @@ class CORE_EXPORT QgsEditFormConfig : public QObject
/** Map that stores the tab for attributes in the edit form. Key is the tab order and value the tab name*/
QList< TabData > mTabs;

QMap< QString, QString> mConstraints;
QMap< QString, bool> mFieldEditables;
QMap< QString, bool> mLabelOnTop;
QMap< QString, bool> mNotNull;
Expand Down
3 changes: 3 additions & 0 deletions src/gui/editorwidgets/core/qgseditorwidgetregistry.cpp
Expand Up @@ -252,6 +252,8 @@ void QgsEditorWidgetRegistry::readMapLayer( QgsMapLayer* mapLayer, const QDomEle
vectorLayer->editFormConfig()->setReadOnly( idx, ewv2CfgElem.attribute( "fieldEditable", "1" ) != "1" );
vectorLayer->editFormConfig()->setLabelOnTop( idx, ewv2CfgElem.attribute( "labelOnTop", "0" ) == "1" );
vectorLayer->editFormConfig()->setNotNull( idx, ewv2CfgElem.attribute( "notNull", "0" ) == "1" );
vectorLayer->editFormConfig()->setConstraint( idx, ewv2CfgElem.attribute( "constraint", "" ) );

vectorLayer->editFormConfig()->setWidgetConfig( idx, cfg );
}
else
Expand Down Expand Up @@ -309,6 +311,7 @@ void QgsEditorWidgetRegistry::writeMapLayer( QgsMapLayer* mapLayer, QDomElement&
ewv2CfgElem.setAttribute( "fieldEditable", !vectorLayer->editFormConfig()->readOnly( idx ) );
ewv2CfgElem.setAttribute( "labelOnTop", vectorLayer->editFormConfig()->labelOnTop( idx ) );
ewv2CfgElem.setAttribute( "notNull", vectorLayer->editFormConfig()->notNull( idx ) );
ewv2CfgElem.setAttribute( "constraint", vectorLayer->editFormConfig()->constraint( idx ) );

mWidgetFactories[widgetType]->writeConfig( vectorLayer->editFormConfig()->widgetConfig( idx ), ewv2CfgElem, doc, vectorLayer, idx );

Expand Down
70 changes: 56 additions & 14 deletions src/gui/editorwidgets/core/qgseditorwidgetwrapper.cpp
Expand Up @@ -22,9 +22,9 @@

QgsEditorWidgetWrapper::QgsEditorWidgetWrapper( QgsVectorLayer* vl, int fieldIdx, QWidget* editor, QWidget* parent )
: QgsWidgetWrapper( vl, editor, parent )
, mValidConstraint( true )
, mFieldIdx( fieldIdx )
{
connect( this, SIGNAL( valueChanged( QVariant ) ), this, SLOT( onValueChanged( QVariant ) ) );
}

int QgsEditorWidgetWrapper::fieldIdx() const
Expand Down Expand Up @@ -61,8 +61,8 @@ void QgsEditorWidgetWrapper::setEnabled( bool enabled )

void QgsEditorWidgetWrapper::setFeature( const QgsFeature& feature )
{
mFeature = feature;
setValue( feature.attribute( mFieldIdx ) );
onValueChanged( value() );
}

void QgsEditorWidgetWrapper::valueChanged( const QString& value )
Expand Down Expand Up @@ -95,27 +95,69 @@ void QgsEditorWidgetWrapper::valueChanged()
emit valueChanged( value() );
}

void QgsEditorWidgetWrapper::updateConstraintsOk( bool constraintStatus )
void QgsEditorWidgetWrapper::updateConstraintWidgetStatus()
{
if ( constraintStatus )
{
if ( mValidConstraint )
widget()->setStyleSheet( "" );
}
else
{
widget()->setStyleSheet( "QWidget{ background-color: '#dd7777': }" );
}
widget()->setStyleSheet( "background-color: #dd7777;" );
}

void QgsEditorWidgetWrapper::onValueChanged( const QVariant& value )
void QgsEditorWidgetWrapper::updateConstraint( const QgsFeature &ft )
{
bool toEmit( false );
QString errStr( "predicate is True" );
QString expression = layer()->editFormConfig()->constraint( mFieldIdx );
QVariant value = ft.attribute( mFieldIdx );

if ( ! expression.isEmpty() )
{
QgsExpressionContext context =
QgsExpressionContextUtils::createFeatureBasedContext( ft, *ft.fields() );

context.setFeature( ft );
QgsExpression expr( expression );

mValidConstraint = expr.evaluate( &context ).toBool();

if ( expr.hasParserError() )
errStr = expr.parserErrorString();
else if ( expr.hasEvalError() )
errStr = expr.evalErrorString();
else if ( ! mValidConstraint )
errStr = "predicate is False";

toEmit = true;
}
else
mValidConstraint = true;

if ( layer()->editFormConfig()->notNull( mFieldIdx ) )
{
if ( value.isNull() != mIsNull )
if ( !expression.isEmpty() )
{
updateConstraintsOk( value.isNull() );
emit constraintStatusChanged( "NotNull", !value.isNull() );
mIsNull = value.isNull();
QString fieldName = ft.fields()->field( mFieldIdx ).name();
expression = "( " + expression + " ) AND ( " + fieldName + " IS NOT NULL)";
}
else
expression = "NotNull";

mValidConstraint = mValidConstraint && !value.isNull();

if ( value.isNull() )
errStr = "predicate is False";

toEmit = true;
}

if ( toEmit )
{
updateConstraintWidgetStatus();
emit constraintStatusChanged( expression, errStr, mValidConstraint );
}
}

bool QgsEditorWidgetWrapper::isValidConstraint() const
{
return mValidConstraint;
}

0 comments on commit 020d20a

Please sign in to comment.