Skip to content
Permalink
Browse files

[FEATURE] constraints on widgets

  • Loading branch information
pblottiere committed May 9, 2016
1 parent 4ae1b55 commit 020d20a968de9d38f5a622383c7404d8777e8088
Showing with 782 additions and 41 deletions.
  1. +16 −0 python/core/qgseditformconfig.sip
  2. +35 −0 python/gui/editorwidgets/core/qgseditorwidgetwrapper.sip
  3. +14 −0 python/gui/editorwidgets/qgsrelationreferencewidgetwrapper.sip
  4. +12 −0 src/app/qgsattributetypedialog.cpp
  5. +12 −0 src/app/qgsattributetypedialog.h
  6. +4 −0 src/app/qgsfieldsproperties.cpp
  7. +1 −0 src/app/qgsfieldsproperties.h
  8. +16 −0 src/core/qgseditformconfig.cpp
  9. +17 −0 src/core/qgseditformconfig.h
  10. +3 −0 src/gui/editorwidgets/core/qgseditorwidgetregistry.cpp
  11. +56 −14 src/gui/editorwidgets/core/qgseditorwidgetwrapper.cpp
  12. +24 −10 src/gui/editorwidgets/core/qgseditorwidgetwrapper.h
  13. +5 −0 src/gui/editorwidgets/qgscolorwidgetwrapper.cpp
  14. +2 −0 src/gui/editorwidgets/qgscolorwidgetwrapper.h
  15. +17 −1 src/gui/editorwidgets/qgsexternalresourcewidgetwrapper.cpp
  16. +2 −0 src/gui/editorwidgets/qgsexternalresourcewidgetwrapper.h
  17. +13 −0 src/gui/editorwidgets/qgsfilenamewidgetwrapper.cpp
  18. +2 −0 src/gui/editorwidgets/qgsfilenamewidgetwrapper.h
  19. +13 −0 src/gui/editorwidgets/qgsphotowidgetwrapper.cpp
  20. +2 −0 src/gui/editorwidgets/qgsphotowidgetwrapper.h
  21. +11 −0 src/gui/editorwidgets/qgsrelationreferencewidgetwrapper.cpp
  22. +14 −0 src/gui/editorwidgets/qgsrelationreferencewidgetwrapper.h
  23. +13 −0 src/gui/editorwidgets/qgswebviewwidgetwrapper.cpp
  24. +2 −0 src/gui/editorwidgets/qgswebviewwidgetwrapper.h
  25. +184 −7 src/gui/qgsattributeform.cpp
  26. +14 −1 src/gui/qgsattributeform.h
  27. +29 −5 src/ui/qgsattributetypeedit.ui
  28. +4 −3 tests/src/gui/CMakeLists.txt
  29. +245 −0 tests/src/gui/testqgsattributeform.cpp
@@ -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
*/
@@ -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.
@@ -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
@@ -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();
};
@@ -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();
};
@@ -71,6 +71,8 @@ QgsAttributeTypeDialog::QgsAttributeTypeDialog( QgsVectorLayer *vl, int fieldIdx

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

constraintExpression->setLayer( vl );
}

QgsAttributeTypeDialog::~QgsAttributeTypeDialog()
@@ -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 );
@@ -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
@@ -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 );
@@ -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();
@@ -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 );
@@ -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 );

@@ -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;
@@ -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() )
@@ -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
*/
@@ -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;
@@ -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
@@ -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 );

@@ -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
@@ -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 )
@@ -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.
You can’t perform that action at this time.