mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
Merge pull request #3079 from pblottiere/constraints
[FEATURE] widgets constraints
This commit is contained in:
commit
2b1560c5be
@ -474,7 +474,48 @@ class QgsEditFormConfig : QObject
|
||||
/**
|
||||
* If set to false, the widget at the given index will be read-only.
|
||||
*/
|
||||
void setReadOnly(int idx, bool readOnly );
|
||||
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 expression( 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 setExpression( int idx, const QString& str );
|
||||
|
||||
/**
|
||||
* Returns the constraint expression description of a specific filed.
|
||||
* @param idx The index of the field
|
||||
* @return the expression description
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
QString expressionDescription( int idx ) const;
|
||||
|
||||
/**
|
||||
* Set the constraint expression description for a specific field.
|
||||
* @param idx The index of the field
|
||||
* @param descr The description of the expression
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
void setExpressionDescription( int idx, const QString &descr );
|
||||
|
||||
/**
|
||||
* Returns if the field at fieldidx should be treated as NOT NULL value
|
||||
*/
|
||||
bool notNull( int fieldidx) const;
|
||||
/**
|
||||
* Set if the field at fieldidx should be treated as NOT NULL value
|
||||
*/
|
||||
void setNotNull( int idx, bool notnull = true );
|
||||
|
||||
/**
|
||||
* If this returns true, the widget at the given index will receive its label on the previous line
|
||||
|
@ -165,7 +165,7 @@ class QgsField
|
||||
/* Raise an exception if the arguments couldn't be parsed. */
|
||||
sipNoMethod(sipParseErr, sipName_QgsField, sipName_convertCompatible, doc_QgsField_convertCompatible);
|
||||
|
||||
return NULL;
|
||||
return nullptr;
|
||||
%End
|
||||
|
||||
//! Allows direct construction of QVariants from fields.
|
||||
|
@ -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,16 @@ class QgsEditorWidgetWrapper : QgsWidgetWrapper
|
||||
*/
|
||||
void valueChanged( const QVariant& value );
|
||||
|
||||
/**
|
||||
* Emit this signal when the constraint status changed.
|
||||
* @brief constraintStatusChanged
|
||||
* @param constraint represented as a string
|
||||
* @param desc is the constraint description
|
||||
* @param err the error represented as a string. Empty if none.
|
||||
* @param status
|
||||
*/
|
||||
void constraintStatusChanged( const QString& constraint, const QString& err, bool status );
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* Will be called when the feature changes
|
||||
@ -162,4 +187,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()
|
||||
@ -163,16 +165,46 @@ void QgsAttributeTypeDialog::setWidgetV2Config( const QgsEditorWidgetConfig& con
|
||||
mWidgetV2Config = config;
|
||||
}
|
||||
|
||||
bool QgsAttributeTypeDialog::fieldEditable()
|
||||
bool QgsAttributeTypeDialog::fieldEditable() const
|
||||
{
|
||||
return isFieldEditableCheckBox->isChecked();
|
||||
}
|
||||
|
||||
bool QgsAttributeTypeDialog::labelOnTop()
|
||||
void QgsAttributeTypeDialog::setNotNull( bool notNull )
|
||||
{
|
||||
notNullCheckBox->setChecked( notNull );
|
||||
}
|
||||
|
||||
bool QgsAttributeTypeDialog::labelOnTop() const
|
||||
{
|
||||
return labelOnTopCheckBox->isChecked();
|
||||
}
|
||||
|
||||
void QgsAttributeTypeDialog::setExpressionDescription( const QString &desc )
|
||||
{
|
||||
constraintExpressionDescription->setText( desc );
|
||||
}
|
||||
|
||||
QString QgsAttributeTypeDialog::expressionDescription()
|
||||
{
|
||||
return constraintExpressionDescription->text();
|
||||
}
|
||||
|
||||
bool QgsAttributeTypeDialog::notNull() const
|
||||
{
|
||||
return notNullCheckBox->isChecked();
|
||||
}
|
||||
|
||||
void QgsAttributeTypeDialog::setExpression( const QString &str )
|
||||
{
|
||||
constraintExpression->setField( str );
|
||||
}
|
||||
|
||||
QString QgsAttributeTypeDialog::expression() const
|
||||
{
|
||||
return constraintExpression->asExpression();
|
||||
}
|
||||
|
||||
void QgsAttributeTypeDialog::setFieldEditable( bool editable )
|
||||
{
|
||||
isFieldEditableCheckBox->setChecked( editable );
|
||||
|
@ -69,6 +69,11 @@ class APP_EXPORT QgsAttributeTypeDialog: public QDialog, private Ui::QgsAttribut
|
||||
*/
|
||||
void setLabelOnTop( bool onTop );
|
||||
|
||||
/**
|
||||
* Getter for checkbox for label on top of field
|
||||
*/
|
||||
bool labelOnTop() const;
|
||||
|
||||
/**
|
||||
* Setter for checkbox for editable state of field
|
||||
*/
|
||||
@ -77,12 +82,43 @@ class APP_EXPORT QgsAttributeTypeDialog: public QDialog, private Ui::QgsAttribut
|
||||
/**
|
||||
* Getter for checkbox for editable state of field
|
||||
*/
|
||||
bool fieldEditable();
|
||||
bool fieldEditable() const;
|
||||
|
||||
/**
|
||||
* Getter for checkbox for label on top of field
|
||||
* Setter for checkbox for not null
|
||||
*/
|
||||
bool labelOnTop();
|
||||
void setNotNull( bool notNull );
|
||||
|
||||
/**
|
||||
* Getter for checkbox for not null
|
||||
*/
|
||||
bool notNull() const;
|
||||
|
||||
/*
|
||||
* Setter for constraint expression description
|
||||
* @param desc the expression description
|
||||
* @note added in QGIS 2.16
|
||||
**/
|
||||
void setExpressionDescription( const QString &desc );
|
||||
|
||||
/*
|
||||
* Getter for constraint expression description
|
||||
* @return the expression description
|
||||
* @note added in QGIS 2.16
|
||||
**/
|
||||
QString expressionDescription();
|
||||
|
||||
/**
|
||||
* Getter for the constraint expression
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
QString expression() const;
|
||||
|
||||
/**
|
||||
* Setter for the constraint expression
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
void setExpression( const QString &str );
|
||||
|
||||
private slots:
|
||||
/**
|
||||
|
@ -527,6 +527,9 @@ void QgsFieldsProperties::attributeTypeDialog()
|
||||
|
||||
attributeTypeDialog.setFieldEditable( cfg.mEditable );
|
||||
attributeTypeDialog.setLabelOnTop( cfg.mLabelOnTop );
|
||||
attributeTypeDialog.setNotNull( cfg.mNotNull );
|
||||
attributeTypeDialog.setExpression( cfg.mConstraint );
|
||||
attributeTypeDialog.setExpressionDescription( cfg.mConstraintDescription );
|
||||
|
||||
attributeTypeDialog.setWidgetV2Config( cfg.mEditorWidgetV2Config );
|
||||
attributeTypeDialog.setWidgetV2Type( cfg.mEditorWidgetV2Type );
|
||||
@ -536,6 +539,9 @@ void QgsFieldsProperties::attributeTypeDialog()
|
||||
|
||||
cfg.mEditable = attributeTypeDialog.fieldEditable();
|
||||
cfg.mLabelOnTop = attributeTypeDialog.labelOnTop();
|
||||
cfg.mNotNull = attributeTypeDialog.notNull();
|
||||
cfg.mConstraintDescription = attributeTypeDialog.expressionDescription();
|
||||
cfg.mConstraint = attributeTypeDialog.expression();
|
||||
|
||||
cfg.mEditorWidgetV2Type = attributeTypeDialog.editorWidgetV2Type();
|
||||
cfg.mEditorWidgetV2Config = attributeTypeDialog.editorWidgetV2Config();
|
||||
@ -908,6 +914,9 @@ void QgsFieldsProperties::apply()
|
||||
|
||||
mLayer->editFormConfig()->setReadOnly( i, !cfg.mEditable );
|
||||
mLayer->editFormConfig()->setLabelOnTop( i, cfg.mLabelOnTop );
|
||||
mLayer->editFormConfig()->setNotNull( i, cfg.mNotNull );
|
||||
mLayer->editFormConfig()->setExpressionDescription( i, cfg.mConstraintDescription );
|
||||
mLayer->editFormConfig()->setExpression( i, cfg.mConstraint );
|
||||
|
||||
mLayer->editFormConfig()->setWidgetType( idx, cfg.mEditorWidgetV2Type );
|
||||
mLayer->editFormConfig()->setWidgetConfig( idx, cfg.mEditorWidgetV2Config );
|
||||
@ -974,6 +983,8 @@ QgsFieldsProperties::FieldConfig::FieldConfig()
|
||||
: mEditable( true )
|
||||
, mEditableEnabled( true )
|
||||
, mLabelOnTop( false )
|
||||
, mNotNull( false )
|
||||
, mConstraintDescription( QString() )
|
||||
, mButton( nullptr )
|
||||
{
|
||||
}
|
||||
@ -985,6 +996,9 @@ QgsFieldsProperties::FieldConfig::FieldConfig( QgsVectorLayer* layer, int idx )
|
||||
mEditableEnabled = layer->fields().fieldOrigin( idx ) != QgsFields::OriginJoin
|
||||
&& layer->fields().fieldOrigin( idx ) != QgsFields::OriginExpression;
|
||||
mLabelOnTop = layer->editFormConfig()->labelOnTop( idx );
|
||||
mNotNull = layer->editFormConfig()->notNull( idx );
|
||||
mConstraint = layer->editFormConfig()->expression( idx );
|
||||
mConstraintDescription = layer->editFormConfig()->expressionDescription( idx );
|
||||
mEditorWidgetV2Type = layer->editFormConfig()->widgetType( idx );
|
||||
mEditorWidgetV2Config = layer->editFormConfig()->widgetConfig( idx );
|
||||
|
||||
|
@ -92,6 +92,9 @@ class APP_EXPORT QgsFieldsProperties : public QWidget, private Ui_QgsFieldsPrope
|
||||
bool mEditable;
|
||||
bool mEditableEnabled;
|
||||
bool mLabelOnTop;
|
||||
bool mNotNull;
|
||||
QString mConstraint;
|
||||
QString mConstraintDescription;
|
||||
QPushButton* mButton;
|
||||
QString mEditorWidgetV2Type;
|
||||
QMap<QString, QVariant> mEditorWidgetV2Config;
|
||||
|
@ -119,6 +119,46 @@ bool QgsEditFormConfig::labelOnTop( int idx ) const
|
||||
return false;
|
||||
}
|
||||
|
||||
QString QgsEditFormConfig::expression( int idx ) const
|
||||
{
|
||||
QString expr;
|
||||
|
||||
if ( idx >= 0 && idx < mFields.count() )
|
||||
expr = mConstraints.value( mFields.at( idx ).name(), QString() );
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
void QgsEditFormConfig::setExpression( int idx, const QString& str )
|
||||
{
|
||||
if ( idx >= 0 && idx < mFields.count() )
|
||||
mConstraints[ mFields.at( idx ).name()] = str;
|
||||
}
|
||||
|
||||
QString QgsEditFormConfig::expressionDescription( int idx ) const
|
||||
{
|
||||
QString description;
|
||||
|
||||
if ( idx >= 0 && idx < mFields.count() )
|
||||
description = mConstraintsDescription[ mFields.at( idx ).name()];
|
||||
|
||||
return description;
|
||||
}
|
||||
|
||||
void QgsEditFormConfig::setExpressionDescription( int idx, const QString &descr )
|
||||
{
|
||||
if ( idx >= 0 && idx < mFields.count() )
|
||||
mConstraintsDescription[ mFields.at( idx ).name()] = descr;
|
||||
}
|
||||
|
||||
bool QgsEditFormConfig::notNull( int idx ) const
|
||||
{
|
||||
if ( idx >= 0 && idx < mFields.count() )
|
||||
return mNotNull.value( mFields.at( idx ).name(), false );
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void QgsEditFormConfig::setReadOnly( int idx, bool readOnly )
|
||||
{
|
||||
if ( idx >= 0 && idx < mFields.count() )
|
||||
@ -131,6 +171,12 @@ void QgsEditFormConfig::setLabelOnTop( int idx, bool onTop )
|
||||
mLabelOnTop[ mFields.at( idx ).name()] = onTop;
|
||||
}
|
||||
|
||||
void QgsEditFormConfig::setNotNull( int idx, bool notnull )
|
||||
{
|
||||
if ( idx >= 0 && idx < mFields.count() )
|
||||
mNotNull[ mFields.at( idx ).name()] = notnull;
|
||||
}
|
||||
|
||||
void QgsEditFormConfig::readXml( const QDomNode& node )
|
||||
{
|
||||
QDomNode editFormNode = node.namedItem( "editform" );
|
||||
@ -280,7 +326,6 @@ void QgsEditFormConfig::writeXml( QDomNode& node ) const
|
||||
efifpField.appendChild( doc.createTextNode( QgsProject::instance()->writePath( initFilePath() ) ) );
|
||||
node.appendChild( efifpField );
|
||||
|
||||
|
||||
QDomElement eficField = doc.createElement( "editforminitcode" );
|
||||
eficField.appendChild( doc.createCDATASection( initCode() ) );
|
||||
node.appendChild( eficField );
|
||||
@ -337,6 +382,7 @@ void QgsEditFormConfig::writeXml( QDomNode& node ) const
|
||||
{
|
||||
QDomElement widgetElem = doc.createElement( "widget" );
|
||||
widgetElem.setAttribute( "name", configIt.key() );
|
||||
// widgetElem.setAttribute( "notNull", );
|
||||
|
||||
QDomElement configElem = doc.createElement( "config" );
|
||||
widgetElem.appendChild( configElem );
|
||||
|
@ -484,12 +484,12 @@ class CORE_EXPORT QgsEditFormConfig : public QObject
|
||||
QgsEditorWidgetConfig widgetConfig( const QString& widgetName ) const;
|
||||
|
||||
/**
|
||||
* Remove the configuration for the editor widget used to represent the field at the given index
|
||||
*
|
||||
* @param fieldIdx The index of the field
|
||||
*
|
||||
* @return true if successful, false if the field does not exist
|
||||
*/
|
||||
* Remove the configuration for the editor widget used to represent the field at the given index
|
||||
*
|
||||
* @param fieldIdx The index of the field
|
||||
*
|
||||
* @return true if successful, false if the field does not exist
|
||||
*/
|
||||
bool removeWidgetConfig( int fieldIdx );
|
||||
|
||||
/**
|
||||
@ -512,6 +512,47 @@ 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 expression( 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 setExpression( int idx, const QString& str );
|
||||
|
||||
/**
|
||||
* Returns the constraint expression description of a specific filed.
|
||||
* @param idx The index of the field
|
||||
* @return the expression description
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
QString expressionDescription( int idx ) const;
|
||||
|
||||
/**
|
||||
* Set the constraint expression description for a specific field.
|
||||
* @param idx The index of the field
|
||||
* @param descr The description of the expression
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
void setExpressionDescription( int idx, const QString &descr );
|
||||
|
||||
/**
|
||||
* Returns if the field at fieldidx should be treated as NOT NULL value
|
||||
*/
|
||||
bool notNull( int fieldidx ) const;
|
||||
/**
|
||||
* Set if the field at fieldidx should be treated as NOT NULL value
|
||||
*/
|
||||
void setNotNull( int idx, bool notnull = true );
|
||||
|
||||
/**
|
||||
* If this returns true, the widget at the given index will receive its label on the previous line
|
||||
* while if it returns false, the widget will receive its label on the left hand side.
|
||||
@ -631,8 +672,11 @@ 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, QString> mConstraintsDescription;
|
||||
QMap< QString, bool> mFieldEditables;
|
||||
QMap< QString, bool> mLabelOnTop;
|
||||
QMap< QString, bool> mNotNull;
|
||||
|
||||
QMap<QString, QString> mEditorWidgetV2Types;
|
||||
QMap<QString, QgsEditorWidgetConfig > mWidgetConfigs;
|
||||
|
@ -16,6 +16,8 @@
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
|
||||
#ifndef QGSEDITORWIDGETCONFIG_H
|
||||
#define QGSEDITORWIDGETCONFIG_H
|
||||
|
||||
/**
|
||||
* Holds a set of configuration parameters for a editor widget wrapper.
|
||||
@ -30,4 +32,6 @@
|
||||
* You get these passed, for every new widget wrapper.
|
||||
*/
|
||||
|
||||
typedef QMap<QString, QVariant> QgsEditorWidgetConfig;
|
||||
typedef QVariantMap QgsEditorWidgetConfig;
|
||||
|
||||
#endif // QGSEDITORWIDGETCONFIG_H
|
||||
|
@ -288,6 +288,12 @@ int QgsFeature::fieldNameIndex( const QString& fieldName ) const
|
||||
return d->fields.fieldNameIndex( fieldName );
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
* This class is considered CRITICAL and any change MUST be accompanied with
|
||||
* full unit tests in testqgsfeature.cpp.
|
||||
* See details in QEP #17
|
||||
****************************************************************************/
|
||||
|
||||
QDataStream& operator<<( QDataStream& out, const QgsFeature& feature )
|
||||
{
|
||||
out << feature.id();
|
||||
|
@ -1105,13 +1105,13 @@ QgsFeatureIterator QgsVectorLayer::getFeatures( const QgsFeatureRequest& request
|
||||
}
|
||||
|
||||
|
||||
bool QgsVectorLayer::addFeature( QgsFeature& f, bool alsoUpdateExtent )
|
||||
bool QgsVectorLayer::addFeature( QgsFeature& feature, bool alsoUpdateExtent )
|
||||
{
|
||||
Q_UNUSED( alsoUpdateExtent ); // TODO[MD]
|
||||
if ( !mValid || !mEditBuffer || !mDataProvider )
|
||||
return false;
|
||||
|
||||
bool success = mEditBuffer->addFeature( f );
|
||||
bool success = mEditBuffer->addFeature( feature );
|
||||
|
||||
if ( success )
|
||||
updateExtents();
|
||||
|
@ -975,11 +975,11 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
|
||||
QgsFeatureIterator getFeatures( const QgsFeatureRequest& request = QgsFeatureRequest() );
|
||||
|
||||
/** Adds a feature
|
||||
@param f feature to add
|
||||
@param feature feature to add
|
||||
@param alsoUpdateExtent If True, will also go to the effort of e.g. updating the extents.
|
||||
@return True in case of success and False in case of error
|
||||
*/
|
||||
bool addFeature( QgsFeature& f, bool alsoUpdateExtent = true );
|
||||
bool addFeature( QgsFeature& feature, bool alsoUpdateExtent = true );
|
||||
|
||||
/** Updates an existing feature. This method needs to query the datasource
|
||||
on every call. Consider using {@link changeAttributeValue()} or
|
||||
|
@ -251,6 +251,10 @@ 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()->setExpression( idx, ewv2CfgElem.attribute( "constraint", QString() ) );
|
||||
vectorLayer->editFormConfig()->setExpressionDescription( idx, ewv2CfgElem.attribute( "constraintDescription", QString() ) );
|
||||
|
||||
vectorLayer->editFormConfig()->setWidgetConfig( idx, cfg );
|
||||
}
|
||||
else
|
||||
@ -307,6 +311,9 @@ void QgsEditorWidgetRegistry::writeMapLayer( QgsMapLayer* mapLayer, QDomElement&
|
||||
QDomElement ewv2CfgElem = doc.createElement( "widgetv2config" );
|
||||
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()->expression( idx ) );
|
||||
ewv2CfgElem.setAttribute( "constraintDescription", vectorLayer->editFormConfig()->expressionDescription( idx ) );
|
||||
|
||||
mWidgetFactories[widgetType]->writeConfig( vectorLayer->editFormConfig()->widgetConfig( idx ), ewv2CfgElem, doc, vectorLayer, idx );
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
QgsEditorWidgetWrapper::QgsEditorWidgetWrapper( QgsVectorLayer* vl, int fieldIdx, QWidget* editor, QWidget* parent )
|
||||
: QgsWidgetWrapper( vl, editor, parent )
|
||||
, mValidConstraint( true )
|
||||
, mFieldIdx( fieldIdx )
|
||||
{
|
||||
}
|
||||
@ -60,6 +61,7 @@ void QgsEditorWidgetWrapper::setEnabled( bool enabled )
|
||||
|
||||
void QgsEditorWidgetWrapper::setFeature( const QgsFeature& feature )
|
||||
{
|
||||
mFeature = feature;
|
||||
setValue( feature.attribute( mFieldIdx ) );
|
||||
}
|
||||
|
||||
@ -92,3 +94,78 @@ void QgsEditorWidgetWrapper::valueChanged()
|
||||
{
|
||||
emit valueChanged( value() );
|
||||
}
|
||||
|
||||
void QgsEditorWidgetWrapper::updateConstraintWidgetStatus()
|
||||
{
|
||||
if ( mValidConstraint )
|
||||
widget()->setStyleSheet( QString() );
|
||||
else
|
||||
widget()->setStyleSheet( "background-color: #dd7777;" );
|
||||
}
|
||||
|
||||
void QgsEditorWidgetWrapper::updateConstraint( const QgsFeature &ft )
|
||||
{
|
||||
bool toEmit( false );
|
||||
QString errStr( tr( "predicate is True" ) );
|
||||
QString expression = layer()->editFormConfig()->expression( mFieldIdx );
|
||||
QString description;
|
||||
QVariant value = ft.attribute( mFieldIdx );
|
||||
|
||||
if ( ! expression.isEmpty() )
|
||||
{
|
||||
description = layer()->editFormConfig()->expressionDescription( mFieldIdx );
|
||||
|
||||
QgsExpressionContext context =
|
||||
QgsExpressionContextUtils::createFeatureBasedContext( ft, *ft.fields() );
|
||||
context << QgsExpressionContextUtils::layerScope( layer() );
|
||||
|
||||
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 = tr( "predicate is False" );
|
||||
|
||||
toEmit = true;
|
||||
}
|
||||
else
|
||||
mValidConstraint = true;
|
||||
|
||||
if ( layer()->editFormConfig()->notNull( mFieldIdx ) )
|
||||
{
|
||||
if ( !expression.isEmpty() )
|
||||
{
|
||||
QString fieldName = ft.fields()->field( mFieldIdx ).name();
|
||||
expression = "( " + expression + " ) AND ( " + fieldName + " IS NOT NULL)";
|
||||
description = "( " + description + " ) AND NotNull";
|
||||
}
|
||||
else
|
||||
{
|
||||
description = "NotNull";
|
||||
expression = "NotNull";
|
||||
}
|
||||
|
||||
mValidConstraint = mValidConstraint && !value.isNull();
|
||||
|
||||
if ( value.isNull() )
|
||||
errStr = tr( "predicate is False" );
|
||||
|
||||
toEmit = true;
|
||||
}
|
||||
|
||||
if ( toEmit )
|
||||
{
|
||||
updateConstraintWidgetStatus();
|
||||
emit constraintStatusChanged( expression, description, errStr, mValidConstraint );
|
||||
}
|
||||
}
|
||||
|
||||
bool QgsEditorWidgetWrapper::isValidConstraint() const
|
||||
{
|
||||
return mValidConstraint;
|
||||
}
|
||||
|
@ -110,6 +110,21 @@ class GUI_EXPORT QgsEditorWidgetWrapper : public 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.
|
||||
@ -118,6 +133,16 @@ class GUI_EXPORT QgsEditorWidgetWrapper : public QgsWidgetWrapper
|
||||
*/
|
||||
void valueChanged( const QVariant& value );
|
||||
|
||||
/**
|
||||
* Emit this signal when the constraint status changed.
|
||||
* @brief constraintStatusChanged
|
||||
* @param constraint represented as a string
|
||||
* @param desc is the constraint description
|
||||
* @param err the error represented as a string. Empty if none.
|
||||
* @param status
|
||||
*/
|
||||
void constraintStatusChanged( const QString& constraint, const QString &desc, const QString& err, bool status );
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* Will be called when the feature changes
|
||||
@ -185,8 +210,28 @@ class GUI_EXPORT QgsEditorWidgetWrapper : public QgsWidgetWrapper
|
||||
*/
|
||||
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.
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
virtual void updateConstraintWidgetStatus();
|
||||
|
||||
/**
|
||||
* Boolean storing the current validity of the constraint for this widget.
|
||||
*/
|
||||
bool mValidConstraint;
|
||||
|
||||
private:
|
||||
int mFieldIdx;
|
||||
QgsFeature mFeature;
|
||||
};
|
||||
|
||||
// We'll use this class inside a QVariant in the widgets properties
|
||||
|
@ -77,3 +77,8 @@ void QgsColorWidgetWrapper::setValue( const QVariant& value )
|
||||
if ( mColorButton )
|
||||
mColorButton->setColor( !value.isNull() ? QColor( value.toString() ) : QColor() );
|
||||
}
|
||||
|
||||
void QgsColorWidgetWrapper::updateConstraintWidgetStatus()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
@ -46,6 +46,8 @@ class GUI_EXPORT QgsColorWidgetWrapper : public QgsEditorWidgetWrapper
|
||||
void setValue( const QVariant& value ) override;
|
||||
|
||||
private:
|
||||
void updateConstraintWidgetStatus() override;
|
||||
|
||||
QgsColorButtonV2* mColorButton;
|
||||
};
|
||||
|
||||
|
@ -95,8 +95,9 @@ void QgsExternalResourceWidgetWrapper::initWidget( QWidget* editor )
|
||||
{
|
||||
fle->setNullValue( QSettings().value( "qgis/nullValue", "NULL" ).toString() );
|
||||
}
|
||||
connect( mLineEdit, SIGNAL( textChanged( QString ) ), this, SLOT( valueChanged( QString ) ) );
|
||||
}
|
||||
else
|
||||
mLineEdit = editor->findChild<QLineEdit*>();
|
||||
|
||||
if ( mQgsWidget )
|
||||
{
|
||||
@ -138,6 +139,10 @@ void QgsExternalResourceWidgetWrapper::initWidget( QWidget* editor )
|
||||
mQgsWidget->fileWidget()->setFilter( config( "FileWidgetFilter" ).toString() );
|
||||
}
|
||||
}
|
||||
|
||||
if ( mLineEdit )
|
||||
connect( mLineEdit, SIGNAL( textChanged( QString ) ), this, SLOT( valueChanged( QString ) ) );
|
||||
|
||||
}
|
||||
|
||||
void QgsExternalResourceWidgetWrapper::setValue( const QVariant& value )
|
||||
@ -182,3 +187,14 @@ void QgsExternalResourceWidgetWrapper::setEnabled( bool enabled )
|
||||
if ( mQgsWidget )
|
||||
mQgsWidget->setReadOnly( !enabled );
|
||||
}
|
||||
|
||||
void QgsExternalResourceWidgetWrapper::updateConstraintWidgetStatus()
|
||||
{
|
||||
if ( mLineEdit )
|
||||
{
|
||||
if ( mValidConstraint )
|
||||
mLineEdit->setStyleSheet( QString() );
|
||||
else
|
||||
mLineEdit->setStyleSheet( "QgsFilterLineEdit { background-color: #dd7777; }" );
|
||||
}
|
||||
}
|
||||
|
@ -54,6 +54,8 @@ class GUI_EXPORT QgsExternalResourceWidgetWrapper : public QgsEditorWidgetWrappe
|
||||
void setEnabled( bool enabled ) override;
|
||||
|
||||
private:
|
||||
void updateConstraintWidgetStatus() override;
|
||||
|
||||
QLineEdit* mLineEdit;
|
||||
QLabel* mLabel;
|
||||
QgsExternalResourceWidget* mQgsWidget;
|
||||
|
@ -151,3 +151,16 @@ void QgsFileNameWidgetWrapper::selectFileName()
|
||||
if ( mLabel )
|
||||
mLineEdit->setText( fileName );
|
||||
}
|
||||
|
||||
void QgsFileNameWidgetWrapper::updateConstraintWidgetStatus()
|
||||
{
|
||||
if ( mLineEdit )
|
||||
{
|
||||
if ( mValidConstraint )
|
||||
mLineEdit->setStyleSheet( QString() );
|
||||
else
|
||||
{
|
||||
mLineEdit->setStyleSheet( "QgsFilterLineEdit { background-color: #dd7777; }" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,8 @@ class GUI_EXPORT QgsFileNameWidgetWrapper : public QgsEditorWidgetWrapper
|
||||
void setValue( const QVariant& value ) override;
|
||||
|
||||
private:
|
||||
void updateConstraintWidgetStatus() override;
|
||||
|
||||
QLineEdit* mLineEdit;
|
||||
QPushButton* mPushButton;
|
||||
QLabel* mLabel;
|
||||
|
@ -266,3 +266,16 @@ void QgsPhotoWidgetWrapper::setEnabled( bool enabled )
|
||||
if ( mButton )
|
||||
mButton->setEnabled( enabled );
|
||||
}
|
||||
|
||||
void QgsPhotoWidgetWrapper::updateConstraintWidgetStatus()
|
||||
{
|
||||
if ( mLineEdit )
|
||||
{
|
||||
if ( mValidConstraint )
|
||||
mLineEdit->setStyleSheet( QString() );
|
||||
else
|
||||
{
|
||||
mLineEdit->setStyleSheet( "QgsFilterLineEdit { background-color: #dd7777; }" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,6 +65,8 @@ class GUI_EXPORT QgsPhotoWidgetWrapper : public QgsEditorWidgetWrapper
|
||||
void loadPixmap( const QString& fileName );
|
||||
|
||||
private:
|
||||
void updateConstraintWidgetStatus() override;
|
||||
|
||||
//! This label is used as a container to display the picture
|
||||
QLabel* mPhotoLabel;
|
||||
//! This label is used as a container to display a picture that scales with the dialog layout.
|
||||
|
@ -38,7 +38,7 @@ QgsEditorWidgetConfig QgsRangeWidgetFactory::readConfig( const QDomElement& conf
|
||||
{
|
||||
Q_UNUSED( layer );
|
||||
Q_UNUSED( fieldIdx );
|
||||
QMap<QString, QVariant> cfg;
|
||||
QgsEditorWidgetConfig cfg;
|
||||
|
||||
cfg.insert( "Style", configElement.attribute( "Style" ) );
|
||||
cfg.insert( "Min", configElement.attribute( "Min" ) );
|
||||
|
@ -55,7 +55,7 @@ QgsRelationReferenceConfigDlg::QgsRelationReferenceConfigDlg( QgsVectorLayer* vl
|
||||
}
|
||||
}
|
||||
|
||||
void QgsRelationReferenceConfigDlg::setConfig( const QMap<QString, QVariant>& config )
|
||||
void QgsRelationReferenceConfigDlg::setConfig( const QgsEditorWidgetConfig& config )
|
||||
{
|
||||
if ( config.contains( "AllowNULL" ) )
|
||||
{
|
||||
|
@ -46,7 +46,7 @@ QgsEditorWidgetConfig QgsRelationReferenceFactory::readConfig( const QDomElement
|
||||
{
|
||||
Q_UNUSED( layer );
|
||||
Q_UNUSED( fieldIdx );
|
||||
QMap<QString, QVariant> cfg;
|
||||
QgsEditorWidgetConfig cfg;
|
||||
|
||||
cfg.insert( "AllowNULL", configElement.attribute( "AllowNULL" ) == "1" );
|
||||
cfg.insert( "OrderByValue", configElement.attribute( "OrderByValue" ) == "1" );
|
||||
|
@ -139,3 +139,14 @@ void QgsRelationReferenceWidgetWrapper::foreignKeyChanged( QVariant value )
|
||||
}
|
||||
emit valueChanged( value );
|
||||
}
|
||||
|
||||
void QgsRelationReferenceWidgetWrapper::updateConstraintWidgetStatus()
|
||||
{
|
||||
if ( mWidget )
|
||||
{
|
||||
if ( mValidConstraint )
|
||||
mWidget->setStyleSheet( QString() );
|
||||
else
|
||||
mWidget->setStyleSheet( ".QComboBox { background-color: #dd7777; }" );
|
||||
}
|
||||
}
|
||||
|
@ -59,6 +59,20 @@ class GUI_EXPORT QgsRelationReferenceWidgetWrapper : public QgsEditorWidgetWrapp
|
||||
private slots:
|
||||
void foreignKeyChanged( QVariant value );
|
||||
|
||||
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() override;
|
||||
|
||||
private:
|
||||
QgsRelationReferenceWidget* mWidget;
|
||||
QgsMapCanvas* mCanvas;
|
||||
|
@ -188,3 +188,16 @@ void QgsWebViewWidgetWrapper::selectFileName()
|
||||
if ( mLineEdit )
|
||||
mLineEdit->setText( filePath );
|
||||
}
|
||||
|
||||
void QgsWebViewWidgetWrapper::updateConstraintWidgetStatus()
|
||||
{
|
||||
if ( mLineEdit )
|
||||
{
|
||||
if ( mValidConstraint )
|
||||
mLineEdit->setStyleSheet( QString() );
|
||||
else
|
||||
{
|
||||
mLineEdit->setStyleSheet( "QgsFilterLineEdit { background-color: #dd7777; }" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,8 @@ class GUI_EXPORT QgsWebViewWidgetWrapper : public QgsEditorWidgetWrapper
|
||||
void selectFileName();
|
||||
|
||||
private:
|
||||
void updateConstraintWidgetStatus() override;
|
||||
|
||||
//! This label is used as a container to display the picture
|
||||
QWebView* mWebView;
|
||||
//! The line edit containing the path to the picture
|
||||
|
@ -53,6 +53,7 @@ QgsAttributeForm::QgsAttributeForm( QgsVectorLayer* vl, const QgsFeature &featur
|
||||
, mMessageBar( nullptr )
|
||||
, mMultiEditUnsavedMessageBarItem( nullptr )
|
||||
, mMultiEditMessageBarItem( nullptr )
|
||||
, mInvalidConstraintMessage( nullptr )
|
||||
, mContext( context )
|
||||
, mButtonBox( nullptr )
|
||||
, mSearchButtonBox( nullptr )
|
||||
@ -79,6 +80,9 @@ QgsAttributeForm::QgsAttributeForm( QgsVectorLayer* vl, const QgsFeature &featur
|
||||
connect( vl, SIGNAL( beforeAddingExpressionField( QString ) ), this, SLOT( preventFeatureRefresh() ) );
|
||||
connect( vl, SIGNAL( beforeRemovingExpressionField( int ) ), this, SLOT( preventFeatureRefresh() ) );
|
||||
connect( vl, SIGNAL( selectionChanged() ), this, SLOT( layerSelectionChanged() ) );
|
||||
|
||||
// constraints management
|
||||
updateAllConstaints();
|
||||
}
|
||||
|
||||
QgsAttributeForm::~QgsAttributeForm()
|
||||
@ -655,6 +659,157 @@ void QgsAttributeForm::onAttributeChanged( const QVariant& value )
|
||||
//nothing to do
|
||||
break;
|
||||
}
|
||||
|
||||
if ( eww->layer()->editFormConfig()->notNull( eww->fieldIdx() ) )
|
||||
{
|
||||
QLabel* buddy = mBuddyMap.value( eww->widget() );
|
||||
|
||||
if ( buddy )
|
||||
{
|
||||
if ( !buddy->property( "originalText" ).isValid() )
|
||||
buddy->setProperty( "originalText", buddy->text() );
|
||||
|
||||
QString text = buddy->property( "originalText" ).toString();
|
||||
|
||||
if ( value.isNull() )
|
||||
{
|
||||
// not good
|
||||
#if QT_VERSION >= 0x050000
|
||||
buddy->setText( QString( "%1<font color=\"red\">❌</font>" ).arg( text ) );
|
||||
#else
|
||||
buddy->setText( QString( "%1<font color=\"red\">*</font>" ).arg( text ) );
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// good
|
||||
#if QT_VERSION >= 0x050000
|
||||
buddy->setText( QString( "%1<font color=\"green\">✔</font>" ).arg( text ) );
|
||||
#else
|
||||
buddy->setText( QString( "%1<font color=\"green\">*</font>" ).arg( text ) );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateConstraints( eww );
|
||||
|
||||
// emit
|
||||
emit attributeChanged( eww->field().name(), value );
|
||||
}
|
||||
|
||||
void QgsAttributeForm::updateAllConstaints()
|
||||
{
|
||||
Q_FOREACH ( QgsWidgetWrapper* ww, mWidgets )
|
||||
{
|
||||
QgsEditorWidgetWrapper* eww = qobject_cast<QgsEditorWidgetWrapper*>( ww );
|
||||
if ( eww )
|
||||
updateConstraints( eww );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsAttributeForm::updateConstraints( QgsEditorWidgetWrapper *eww )
|
||||
{
|
||||
// get the current feature set in the form
|
||||
QgsFeature ft;
|
||||
if ( currentFormFeature( ft ) )
|
||||
{
|
||||
// update eww constraint
|
||||
eww->updateConstraint( ft );
|
||||
|
||||
// update eww dependencies constraint
|
||||
QList<QgsEditorWidgetWrapper*> deps;
|
||||
constraintDependencies( eww, deps );
|
||||
|
||||
Q_FOREACH ( QgsEditorWidgetWrapper* depsEww, deps )
|
||||
depsEww->updateConstraint( ft );
|
||||
|
||||
// sync ok button status
|
||||
synchronizeEnabledState();
|
||||
}
|
||||
}
|
||||
|
||||
bool QgsAttributeForm::currentFormFeature( QgsFeature &feature )
|
||||
{
|
||||
bool rc = true;
|
||||
feature = QgsFeature( mFeature );
|
||||
QgsAttributes src = feature.attributes();
|
||||
QgsAttributes dst = feature.attributes();
|
||||
|
||||
Q_FOREACH ( QgsWidgetWrapper* ww, mWidgets )
|
||||
{
|
||||
QgsEditorWidgetWrapper* eww = qobject_cast<QgsEditorWidgetWrapper*>( ww );
|
||||
if ( eww && dst.count() > eww->fieldIdx() )
|
||||
{
|
||||
QVariant dstVar = dst.at( eww->fieldIdx() );
|
||||
QVariant srcVar = eww->value();
|
||||
// need to check dstVar.isNull() != srcVar.isNull()
|
||||
// otherwise if dstVar=NULL and scrVar=0, then dstVar = srcVar
|
||||
if (( dstVar != srcVar || dstVar.isNull() != srcVar.isNull() ) && srcVar.isValid() && !mLayer->editFormConfig()->readOnly( eww->fieldIdx() ) )
|
||||
dst[eww->fieldIdx()] = srcVar;
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
feature.setAttributes( dst );
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void QgsAttributeForm::clearInvalidConstraintsMessage()
|
||||
{
|
||||
mInvalidConstraintMessage->clear();
|
||||
mInvalidConstraintMessage->setStyleSheet( "" );
|
||||
}
|
||||
|
||||
void QgsAttributeForm::displayInvalidConstraintMessage( const QStringList &f,
|
||||
const QStringList &d )
|
||||
{
|
||||
clearInvalidConstraintsMessage();
|
||||
|
||||
// show only the third first errors (to avoid a too long label)
|
||||
int max = 3;
|
||||
int size = f.size() > max ? max : f.size();
|
||||
QString descriptions;
|
||||
for ( int i = 0; i < size; i++ )
|
||||
descriptions += QString( "<li>%1: <i>%2</i></li>" ).arg( f[i] ).arg( d[i] );
|
||||
|
||||
QString icPath = QgsApplication::iconPath( "/mIconWarn.png" );
|
||||
|
||||
QString title = QString( "<img src=\"%1\"> <b>%2:" ).arg( icPath ).arg( tr( "Invalid fields" ) );
|
||||
QString msg = QString( "%1</b><ul>%2</ul>" ).arg( title ).arg( descriptions ) ;
|
||||
|
||||
mInvalidConstraintMessage->setText( msg );
|
||||
mInvalidConstraintMessage->setStyleSheet( "QLabel { background-color : #ffc800; }" );
|
||||
}
|
||||
|
||||
bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields,
|
||||
QStringList &descriptions )
|
||||
{
|
||||
bool valid( true );
|
||||
|
||||
Q_FOREACH ( QgsWidgetWrapper* ww, mWidgets )
|
||||
{
|
||||
QgsEditorWidgetWrapper* eww = qobject_cast<QgsEditorWidgetWrapper*>( ww );
|
||||
if ( eww )
|
||||
{
|
||||
if ( ! eww->isValidConstraint() )
|
||||
{
|
||||
invalidFields.append( eww->field().name() );
|
||||
|
||||
QString desc = eww->layer()->editFormConfig()->expressionDescription( eww->fieldIdx() );
|
||||
descriptions.append( desc );
|
||||
|
||||
valid = false; // continue to get all invalif fields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
void QgsAttributeForm::onAttributeAdded( int idx )
|
||||
@ -714,6 +869,70 @@ void QgsAttributeForm::onUpdatedFields()
|
||||
setFeature( mFeature );
|
||||
}
|
||||
|
||||
void QgsAttributeForm::onConstraintStatusChanged( const QString& constraint,
|
||||
const QString &description, const QString& err, bool ok )
|
||||
{
|
||||
QgsEditorWidgetWrapper* eww = qobject_cast<QgsEditorWidgetWrapper*>( sender() );
|
||||
Q_ASSERT( eww );
|
||||
|
||||
QLabel* buddy = mBuddyMap.value( eww->widget() );
|
||||
|
||||
if ( buddy )
|
||||
{
|
||||
QString tooltip = tr( "Description: " ) + description + "\n" +
|
||||
tr( "Raw expression: " ) + constraint + "\n" + tr( "Constraint: " ) + err;
|
||||
buddy->setToolTip( tooltip );
|
||||
|
||||
if ( !buddy->property( "originalText" ).isValid() )
|
||||
buddy->setProperty( "originalText", buddy->text() );
|
||||
|
||||
QString text = buddy->property( "originalText" ).toString();
|
||||
|
||||
if ( !ok )
|
||||
{
|
||||
// not good
|
||||
buddy->setText( QString( "%1<font color=\"red\">*</font>" ).arg( text ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// good
|
||||
buddy->setText( QString( "%1<font color=\"green\">*</font>" ).arg( text ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QgsAttributeForm::constraintDependencies( QgsEditorWidgetWrapper *w,
|
||||
QList<QgsEditorWidgetWrapper*> &wDeps )
|
||||
{
|
||||
QString name = w->field().name();
|
||||
|
||||
// for each widget in the current form
|
||||
Q_FOREACH ( QgsWidgetWrapper* ww, mWidgets )
|
||||
{
|
||||
// get the wrapper
|
||||
QgsEditorWidgetWrapper* eww = qobject_cast<QgsEditorWidgetWrapper*>( ww );
|
||||
if ( eww )
|
||||
{
|
||||
// compare name to not compare w to itself
|
||||
QString ewwName = eww->field().name();
|
||||
if ( name != ewwName )
|
||||
{
|
||||
// get expression and referencedColumns
|
||||
QgsExpression expr = eww->layer()->editFormConfig()->expression( eww->fieldIdx() );
|
||||
|
||||
Q_FOREACH ( const QString& colName, expr.referencedColumns() )
|
||||
{
|
||||
if ( name == colName )
|
||||
{
|
||||
wDeps.append( eww );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QgsAttributeForm::preventFeatureRefresh()
|
||||
{
|
||||
mPreventFeatureRefresh = true;
|
||||
@ -752,6 +971,18 @@ void QgsAttributeForm::synchronizeEnabledState()
|
||||
ww->setEnabled( isEditable && fieldEditable );
|
||||
}
|
||||
|
||||
// push a message and disable the OK button if constraints are invalid
|
||||
clearInvalidConstraintsMessage();
|
||||
|
||||
QStringList invalidFields, descriptions;
|
||||
bool validConstraint = currentFormValidConstraints( invalidFields, descriptions );
|
||||
|
||||
if ( ! validConstraint )
|
||||
displayInvalidConstraintMessage( invalidFields, descriptions );
|
||||
|
||||
isEditable = isEditable & validConstraint;
|
||||
|
||||
// change ok button status
|
||||
QPushButton* okButton = mButtonBox->button( QDialogButtonBox::Ok );
|
||||
if ( okButton )
|
||||
okButton->setEnabled( isEditable );
|
||||
@ -794,6 +1025,10 @@ void QgsAttributeForm::init()
|
||||
mMessageBar = new QgsMessageBar( this );
|
||||
mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
|
||||
vl->addWidget( mMessageBar );
|
||||
|
||||
mInvalidConstraintMessage = new QLabel( this );
|
||||
vl->addWidget( mInvalidConstraintMessage );
|
||||
|
||||
setLayout( vl );
|
||||
|
||||
// Get a layout
|
||||
@ -899,7 +1134,7 @@ void QgsAttributeForm::init()
|
||||
bool labelOnTop = mLayer->editFormConfig()->labelOnTop( idx );
|
||||
|
||||
// This will also create the widget
|
||||
QWidget *l = new QLabel( fieldName );
|
||||
QLabel *l = new QLabel( fieldName );
|
||||
QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( widgetType, mLayer, idx, widgetConfig, nullptr, this, mContext );
|
||||
|
||||
QWidget* w = nullptr;
|
||||
@ -909,12 +1144,18 @@ void QgsAttributeForm::init()
|
||||
w = formWidget;
|
||||
mFormEditorWidgets.insert( idx, formWidget );
|
||||
formWidget->createSearchWidgetWrappers( widgetType, idx, widgetConfig, mContext );
|
||||
|
||||
l->setBuddy( eww->widget() );
|
||||
}
|
||||
else
|
||||
{
|
||||
w = new QLabel( QString( "<p style=\"color: red; font-style: italic;\">Failed to create widget with type '%1'</p>" ).arg( widgetType ) );
|
||||
}
|
||||
|
||||
|
||||
if ( w )
|
||||
w->setObjectName( field.name() );
|
||||
|
||||
if ( eww )
|
||||
addWidgetWrapper( eww );
|
||||
|
||||
@ -1011,7 +1252,7 @@ void QgsAttributeForm::init()
|
||||
}
|
||||
mSearchButtonBox->setVisible( mMode == SearchMode );
|
||||
|
||||
connectWrappers();
|
||||
afterWidgetInit();
|
||||
|
||||
connect( mButtonBox, SIGNAL( accepted() ), this, SLOT( accept() ) );
|
||||
connect( mButtonBox, SIGNAL( rejected() ), this, SLOT( resetValues() ) );
|
||||
@ -1251,6 +1492,8 @@ QgsAttributeForm::WidgetInfo QgsAttributeForm::createWidgetFromDef( const QgsAtt
|
||||
mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
|
||||
}
|
||||
|
||||
mypLabel->setBuddy( widgetInfo.widget );
|
||||
|
||||
if ( widgetInfo.labelOnTop )
|
||||
{
|
||||
QVBoxLayout* c = new QVBoxLayout();
|
||||
@ -1350,7 +1593,7 @@ void QgsAttributeForm::createWrappers()
|
||||
}
|
||||
}
|
||||
|
||||
void QgsAttributeForm::connectWrappers()
|
||||
void QgsAttributeForm::afterWidgetInit()
|
||||
{
|
||||
bool isFirstEww = true;
|
||||
|
||||
@ -1367,8 +1610,20 @@ void QgsAttributeForm::connectWrappers()
|
||||
}
|
||||
|
||||
connect( eww, SIGNAL( valueChanged( const QVariant& ) ), this, SLOT( onAttributeChanged( const QVariant& ) ) );
|
||||
connect( eww, SIGNAL( constraintStatusChanged( QString, QString, QString, bool ) ),
|
||||
this, SLOT( onConstraintStatusChanged( QString, QString, QString, bool ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Update buddy widget list
|
||||
mBuddyMap.clear();
|
||||
QList<QLabel*> labels = findChildren<QLabel*>();
|
||||
|
||||
Q_FOREACH ( QLabel* label, labels )
|
||||
{
|
||||
if ( label->buddy() )
|
||||
mBuddyMap.insert( label->buddy(), label );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "qgsattributeeditorcontext.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QLabel>
|
||||
#include <QDialogButtonBox>
|
||||
|
||||
class QgsAttributeFormInterface;
|
||||
@ -233,7 +234,8 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
|
||||
void onAttributeAdded( int idx );
|
||||
void onAttributeDeleted( int idx );
|
||||
void onUpdatedFields();
|
||||
|
||||
void onConstraintStatusChanged( const QString& constraint,
|
||||
const QString &description, const QString& err, bool ok );
|
||||
void preventFeatureRefresh();
|
||||
void synchronizeEnabledState();
|
||||
void layerSelectionChanged();
|
||||
@ -282,7 +284,7 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
|
||||
* Called once maximally.
|
||||
*/
|
||||
void createWrappers();
|
||||
void connectWrappers();
|
||||
void afterWidgetInit();
|
||||
|
||||
void scanForEqualAttributes( QgsFeatureIterator& fit, QSet< int >& mixedValueFields, QHash< int, QVariant >& fieldSharedValues ) const;
|
||||
|
||||
@ -296,11 +298,22 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
|
||||
|
||||
QString createFilterExpression() const;
|
||||
|
||||
//! constraints management
|
||||
void updateAllConstaints();
|
||||
void updateConstraints( QgsEditorWidgetWrapper *w );
|
||||
bool currentFormFeature( QgsFeature &feature );
|
||||
bool currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions );
|
||||
void constraintDependencies( QgsEditorWidgetWrapper *w, QList<QgsEditorWidgetWrapper*> &wDeps );
|
||||
void clearInvalidConstraintsMessage();
|
||||
void displayInvalidConstraintMessage( const QStringList &invalidFields,
|
||||
const QStringList &description );
|
||||
|
||||
QgsVectorLayer* mLayer;
|
||||
QgsFeature mFeature;
|
||||
QgsMessageBar* mMessageBar;
|
||||
QgsMessageBarItem* mMultiEditUnsavedMessageBarItem;
|
||||
QgsMessageBarItem* mMultiEditMessageBarItem;
|
||||
QLabel* mInvalidConstraintMessage;
|
||||
QList<QgsWidgetWrapper*> mWidgets;
|
||||
QgsAttributeEditorContext mContext;
|
||||
QDialogButtonBox* mButtonBox;
|
||||
@ -330,7 +343,11 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
|
||||
|
||||
Mode mMode;
|
||||
|
||||
//! Backlinks widgets to buddies.
|
||||
QMap<QWidget*, QLabel*> mBuddyMap;
|
||||
|
||||
friend class TestQgsDualView;
|
||||
friend class TestQgsAttributeForm;
|
||||
};
|
||||
|
||||
#endif // QGSATTRIBUTEFORM_H
|
||||
|
@ -14,19 +14,6 @@
|
||||
<string>Edit Widget Properties</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="6" column="1">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QStackedWidget" name="stackedWidget"/>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="isFieldEditableCheckBox">
|
||||
<property name="text">
|
||||
@ -47,11 +34,72 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" rowspan="7">
|
||||
<item row="0" column="0" rowspan="11">
|
||||
<widget class="QListWidget" name="selectionListWidget"/>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QStackedWidget" name="stackedWidget"/>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="notNullCheckBox">
|
||||
<property name="text">
|
||||
<string>Not Null</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Constraint</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QgsFieldExpressionWidget" name="constraintExpression" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Constraint description</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="constraintExpressionDescription"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QgsFieldExpressionWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>qgsfieldexpressionwidget.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>selectionListWidget</tabstop>
|
||||
<tabstop>isFieldEditableCheckBox</tabstop>
|
||||
|
@ -5,7 +5,7 @@ SET (util_SRCS)
|
||||
#####################################################
|
||||
# Don't forget to include output directory, otherwise
|
||||
# the UI file won't be wrapped!
|
||||
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}
|
||||
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../../../src/ui
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../core #for render checker class
|
||||
@ -62,7 +62,7 @@ ENDIF (APPLE)
|
||||
#qtests in the executable file list as the moc is
|
||||
#directly included in the sources
|
||||
#and should not be compiled twice. Trying to include
|
||||
#them in will cause an error at build time
|
||||
#them in will cause an error at build time
|
||||
#############################################################
|
||||
# Tests:
|
||||
|
||||
@ -76,7 +76,7 @@ ENDIF (APPLE)
|
||||
#ADD_EXECUTABLE(qgis_quickprinttest ${qgis_quickprinttest_SRCS})
|
||||
#ADD_DEPENDENCIES(qgis_quickprinttest qgis_quickprinttestmoc)
|
||||
#TARGET_LINK_LIBRARIES(qgis_quickprinttest ${QT_LIBRARIES} qgis_core qgis_gui)
|
||||
#SET_TARGET_PROPERTIES(qgis_quickprinttest
|
||||
#SET_TARGET_PROPERTIES(qgis_quickprinttest
|
||||
# PROPERTIES INSTALL_RPATH ${QGIS_LIB_DIR}
|
||||
# INSTALL_RPATH_USE_LINK_PATH true)
|
||||
#IF (APPLE)
|
||||
@ -128,6 +128,7 @@ ADD_QGIS_TEST(zoomtest testqgsmaptoolzoom.cpp)
|
||||
#ADD_QGIS_TEST(histogramtest testqgsrasterhistogram.cpp)
|
||||
ADD_QGIS_TEST(doublespinbox testqgsdoublespinbox.cpp)
|
||||
ADD_QGIS_TEST(dualviewtest testqgsdualview.cpp)
|
||||
ADD_QGIS_TEST(attributeformtest testqgsattributeform.cpp)
|
||||
ADD_QGIS_TEST(fieldexpressionwidget testqgsfieldexpressionwidget.cpp)
|
||||
ADD_QGIS_TEST(filewidget testqgsfilewidget.cpp)
|
||||
ADD_QGIS_TEST(focuswatcher testqgsfocuswatcher.cpp)
|
||||
|
245
tests/src/gui/testqgsattributeform.cpp
Normal file
245
tests/src/gui/testqgsattributeform.cpp
Normal file
@ -0,0 +1,245 @@
|
||||
/***************************************************************************
|
||||
testqgsattributeform.cpp
|
||||
--------------------------------------
|
||||
Date : 13 05 2016
|
||||
Copyright : (C) 2016 Paul Blottiere
|
||||
Email : paul dot blottiere at oslandia dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
#include <QPushButton>
|
||||
|
||||
#include <editorwidgets/core/qgseditorwidgetregistry.h>
|
||||
#include "qgsattributeform.h"
|
||||
#include <qgsapplication.h>
|
||||
#include <qgsvectorlayer.h>
|
||||
#include "qgsvectordataprovider.h"
|
||||
#include <qgsfeature.h>
|
||||
|
||||
class TestQgsAttributeForm : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TestQgsAttributeForm() {}
|
||||
|
||||
private slots:
|
||||
void initTestCase(); // will be called before the first testfunction is executed.
|
||||
void cleanupTestCase(); // will be called after the last testfunction was executed.
|
||||
void init(); // will be called before each testfunction is executed.
|
||||
void cleanup(); // will be called after every testfunction.
|
||||
|
||||
void testFieldConstraint();
|
||||
void testFieldMultiConstraints();
|
||||
void testOKButtonStatus();
|
||||
};
|
||||
|
||||
void TestQgsAttributeForm::initTestCase()
|
||||
{
|
||||
QgsApplication::init();
|
||||
QgsApplication::initQgis();
|
||||
QgsEditorWidgetRegistry::initEditors();
|
||||
}
|
||||
|
||||
void TestQgsAttributeForm::cleanupTestCase()
|
||||
{
|
||||
QgsApplication::exitQgis();
|
||||
}
|
||||
|
||||
void TestQgsAttributeForm::init()
|
||||
{
|
||||
}
|
||||
|
||||
void TestQgsAttributeForm::cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
void TestQgsAttributeForm::testFieldConstraint()
|
||||
{
|
||||
// make a temporary vector layer
|
||||
QString def = "Point?field=col0:integer";
|
||||
QgsVectorLayer* layer = new QgsVectorLayer( def, "test", "memory" );
|
||||
|
||||
// add a feature to the vector layer
|
||||
QgsFeature ft( layer->dataProvider()->fields(), 1 );
|
||||
ft.setAttribute( "col0", 0 );
|
||||
|
||||
// build a form for this feature
|
||||
QgsAttributeForm form( layer );
|
||||
form.setFeature( ft );
|
||||
|
||||
// testing stuff
|
||||
QSignalSpy spy( &form, SIGNAL( attributeChanged( QString, QVariant ) ) );
|
||||
QString validLabel = "col0<font color=\"green\">*</font>";
|
||||
QString invalidLabel = "col0<font color=\"red\">*</font>";
|
||||
|
||||
// set constraint
|
||||
layer->editFormConfig()->setExpression( 0, QString() );
|
||||
|
||||
// get wrapper
|
||||
QgsEditorWidgetWrapper *ww;
|
||||
ww = qobject_cast<QgsEditorWidgetWrapper*>( form.mWidgets[0] );
|
||||
|
||||
// no constraint so we expect a label with just the field name
|
||||
QLabel *label = form.mBuddyMap.value( ww->widget() );
|
||||
QCOMPARE( label->text(), QString( "col0" ) );
|
||||
|
||||
// set a not null constraint
|
||||
layer->editFormConfig()->setExpression( 0, "col0 is not null" );
|
||||
|
||||
// set value to 1
|
||||
ww->setValue( 1 );
|
||||
QCOMPARE( spy.count(), 2 );
|
||||
QCOMPARE( label->text(), validLabel );
|
||||
|
||||
// set value to null
|
||||
spy.clear();
|
||||
ww->setValue( QVariant() );
|
||||
QCOMPARE( spy.count(), 2 );
|
||||
QCOMPARE( label->text(), invalidLabel );
|
||||
|
||||
// set value to 1
|
||||
spy.clear();
|
||||
ww->setValue( 1 );
|
||||
QCOMPARE( spy.count(), 2 );
|
||||
QCOMPARE( label->text(), validLabel );
|
||||
}
|
||||
|
||||
void TestQgsAttributeForm::testFieldMultiConstraints()
|
||||
{
|
||||
// make a temporary layer to check through
|
||||
QString def = "Point?field=col0:integer&field=col1:integer&field=col2:integer&field=col3:integer";
|
||||
QgsVectorLayer* layer = new QgsVectorLayer( def, "test", "memory" );
|
||||
|
||||
// add features to the vector layer
|
||||
QgsFeature ft( layer->dataProvider()->fields(), 1 );
|
||||
ft.setAttribute( "col0", 0 );
|
||||
ft.setAttribute( "col1", 1 );
|
||||
ft.setAttribute( "col2", 2 );
|
||||
ft.setAttribute( "col3", 3 );
|
||||
|
||||
// set constraints for each field
|
||||
layer->editFormConfig()->setExpression( 0, QString() );
|
||||
layer->editFormConfig()->setExpression( 1, QString() );
|
||||
layer->editFormConfig()->setExpression( 2, QString() );
|
||||
layer->editFormConfig()->setExpression( 3, QString() );
|
||||
|
||||
// build a form for this feature
|
||||
QgsAttributeForm form( layer );
|
||||
form.setFeature( ft );
|
||||
|
||||
// testing stuff
|
||||
QSignalSpy spy( &form, SIGNAL( attributeChanged( QString, QVariant ) ) );
|
||||
QString val = "<font color=\"green\">*</font>";
|
||||
QString inv = "<font color=\"red\">*</font>";
|
||||
|
||||
// get wrappers for each widget
|
||||
QgsEditorWidgetWrapper *ww0, *ww1, *ww2, *ww3;
|
||||
ww0 = qobject_cast<QgsEditorWidgetWrapper*>( form.mWidgets[0] );
|
||||
ww1 = qobject_cast<QgsEditorWidgetWrapper*>( form.mWidgets[1] );
|
||||
ww2 = qobject_cast<QgsEditorWidgetWrapper*>( form.mWidgets[2] );
|
||||
ww3 = qobject_cast<QgsEditorWidgetWrapper*>( form.mWidgets[3] );
|
||||
|
||||
// get label for wrappers
|
||||
QLabel *label0 = form.mBuddyMap.value( ww0->widget() );
|
||||
QLabel *label1 = form.mBuddyMap.value( ww1->widget() );
|
||||
QLabel *label2 = form.mBuddyMap.value( ww2->widget() );
|
||||
QLabel *label3 = form.mBuddyMap.value( ww3->widget() );
|
||||
|
||||
// no constraint so we expect a label with just the field name
|
||||
QCOMPARE( label0->text(), QString( "col0" ) );
|
||||
QCOMPARE( label1->text(), QString( "col1" ) );
|
||||
QCOMPARE( label2->text(), QString( "col2" ) );
|
||||
QCOMPARE( label3->text(), QString( "col3" ) );
|
||||
|
||||
// update constraint
|
||||
layer->editFormConfig()->setExpression( 0, "col0 < (col1 * col2)" );
|
||||
layer->editFormConfig()->setExpression( 1, QString() );
|
||||
layer->editFormConfig()->setExpression( 2, QString() );
|
||||
layer->editFormConfig()->setExpression( 3, "col0 = 2" );
|
||||
|
||||
// change value
|
||||
ww0->setValue( 2 ); // update col0
|
||||
QCOMPARE( spy.count(), 2 );
|
||||
|
||||
QCOMPARE( label0->text(), QString( "col0" + inv ) ); // 2 < ( 1 + 2 )
|
||||
QCOMPARE( label1->text(), QString( "col1" ) );
|
||||
QCOMPARE( label2->text(), QString( "col2" ) );
|
||||
QCOMPARE( label3->text(), QString( "col3" + val ) ); // 2 = 2
|
||||
|
||||
// change value
|
||||
spy.clear();
|
||||
ww0->setValue( 1 ); // update col0
|
||||
QCOMPARE( spy.count(), 2 );
|
||||
|
||||
QCOMPARE( label0->text(), QString( "col0" + val ) ); // 1 < ( 1 + 2 )
|
||||
QCOMPARE( label1->text(), QString( "col1" ) );
|
||||
QCOMPARE( label2->text(), QString( "col2" ) );
|
||||
QCOMPARE( label3->text(), QString( "col3" + inv ) ); // 2 = 1
|
||||
}
|
||||
|
||||
void TestQgsAttributeForm::testOKButtonStatus()
|
||||
{
|
||||
// make a temporary vector layer
|
||||
QString def = "Point?field=col0:integer";
|
||||
QgsVectorLayer* layer = new QgsVectorLayer( def, "test", "memory" );
|
||||
|
||||
// add a feature to the vector layer
|
||||
QgsFeature ft( layer->dataProvider()->fields(), 1 );
|
||||
ft.setAttribute( "col0", 0 );
|
||||
ft.setValid( true );
|
||||
|
||||
// build a form for this feature
|
||||
QgsAttributeForm form( layer );
|
||||
form.setFeature( ft );
|
||||
|
||||
QPushButton *okButton = form.mButtonBox->button( QDialogButtonBox::Ok );
|
||||
|
||||
// get wrapper
|
||||
QgsEditorWidgetWrapper *ww;
|
||||
ww = qobject_cast<QgsEditorWidgetWrapper*>( form.mWidgets[0] );
|
||||
|
||||
// testing stuff
|
||||
QSignalSpy spy1( &form, SIGNAL( attributeChanged( QString, QVariant ) ) );
|
||||
QSignalSpy spy2( layer, SIGNAL( editingStarted() ) );
|
||||
QSignalSpy spy3( layer, SIGNAL( editingStopped() ) );
|
||||
|
||||
// set constraint
|
||||
layer->editFormConfig()->setExpression( 0, QString() );
|
||||
|
||||
// no constraint but layer not editable : OK button disabled
|
||||
QCOMPARE( layer->isEditable(), false );
|
||||
QCOMPARE( okButton->isEnabled(), false );
|
||||
|
||||
// no constraint and editable layer : OK button enabled
|
||||
layer->startEditing();
|
||||
QCOMPARE( spy2.count(), 1 );
|
||||
QCOMPARE( layer->isEditable(), true );
|
||||
QCOMPARE( okButton->isEnabled(), true );
|
||||
|
||||
// invalid constraint and editable layer : OK button disabled
|
||||
layer->editFormConfig()->setExpression( 0, "col0 = 0" );
|
||||
ww->setValue( 1 );
|
||||
QCOMPARE( okButton->isEnabled(), false );
|
||||
|
||||
// valid constraint and editable layer : OK button enabled
|
||||
layer->editFormConfig()->setExpression( 0, "col0 = 2" );
|
||||
ww->setValue( 2 );
|
||||
QCOMPARE( okButton->isEnabled(), true );
|
||||
|
||||
// valid constraint and not editable layer : OK button disabled
|
||||
layer->rollBack();
|
||||
QCOMPARE( spy3.count(), 1 );
|
||||
QCOMPARE( layer->isEditable(), false );
|
||||
QCOMPARE( okButton->isEnabled(), false );
|
||||
}
|
||||
|
||||
QTEST_MAIN( TestQgsAttributeForm )
|
||||
#include "testqgsattributeform.moc"
|
Loading…
x
Reference in New Issue
Block a user