Skip to content

Commit 4ae1b55

Browse files
m-kuhnpblottiere
authored andcommitted
Add not null constraint and visual feedback to widgets
1 parent fda06c0 commit 4ae1b55

21 files changed

+254
-32
lines changed

python/core/qgseditformconfig.sip

+10-1
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,16 @@ class QgsEditFormConfig : QObject
474474
/**
475475
* If set to false, the widget at the given index will be read-only.
476476
*/
477-
void setReadOnly(int idx, bool readOnly );
477+
void setReadOnly( int idx, bool readOnly = true );
478+
479+
/**
480+
* Returns if the field at fieldidx should be treated as NOT NULL value
481+
*/
482+
bool notNull( int fieldidx) const;
483+
/**
484+
* Set if the field at fieldidx should be treated as NOT NULL value
485+
*/
486+
void setNotNull( int idx, bool notnull = true );
478487

479488
/**
480489
* If this returns true, the widget at the given index will receive its label on the previous line

python/core/qgsfield.sip

+1-1
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ class QgsField
165165
/* Raise an exception if the arguments couldn't be parsed. */
166166
sipNoMethod(sipParseErr, sipName_QgsField, sipName_convertCompatible, doc_QgsField_convertCompatible);
167167

168-
return NULL;
168+
return nullptr;
169169
%End
170170

171171
//! Allows direct construction of QVariants from fields.

src/app/qgsattributetypedialog.cpp

+12-2
Original file line numberDiff line numberDiff line change
@@ -163,16 +163,26 @@ void QgsAttributeTypeDialog::setWidgetV2Config( const QgsEditorWidgetConfig& con
163163
mWidgetV2Config = config;
164164
}
165165

166-
bool QgsAttributeTypeDialog::fieldEditable()
166+
bool QgsAttributeTypeDialog::fieldEditable() const
167167
{
168168
return isFieldEditableCheckBox->isChecked();
169169
}
170170

171-
bool QgsAttributeTypeDialog::labelOnTop()
171+
void QgsAttributeTypeDialog::setNotNull( bool notnull )
172+
{
173+
notNullCheckBox->setChecked( notnull );
174+
}
175+
176+
bool QgsAttributeTypeDialog::labelOnTop() const
172177
{
173178
return labelOnTopCheckBox->isChecked();
174179
}
175180

181+
bool QgsAttributeTypeDialog::notNull() const
182+
{
183+
return notNullCheckBox->isChecked();
184+
}
185+
176186
void QgsAttributeTypeDialog::setFieldEditable( bool editable )
177187
{
178188
isFieldEditableCheckBox->setChecked( editable );

src/app/qgsattributetypedialog.h

+13-3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ class APP_EXPORT QgsAttributeTypeDialog: public QDialog, private Ui::QgsAttribut
6969
*/
7070
void setLabelOnTop( bool onTop );
7171

72+
/**
73+
* Getter for checkbox for label on top of field
74+
*/
75+
bool labelOnTop() const;
76+
7277
/**
7378
* Setter for checkbox for editable state of field
7479
*/
@@ -77,12 +82,17 @@ class APP_EXPORT QgsAttributeTypeDialog: public QDialog, private Ui::QgsAttribut
7782
/**
7883
* Getter for checkbox for editable state of field
7984
*/
80-
bool fieldEditable();
85+
bool fieldEditable() const;
8186

8287
/**
83-
* Getter for checkbox for label on top of field
88+
* Getter for checkbox for not null
89+
*/
90+
void setNotNull( bool notnull );
91+
92+
/**
93+
* Getter for checkbox for not null
8494
*/
85-
bool labelOnTop();
95+
bool notNull() const;
8696

8797
private slots:
8898
/**

src/app/qgsfieldsproperties.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@ void QgsFieldsProperties::attributeTypeDialog()
527527

528528
attributeTypeDialog.setFieldEditable( cfg.mEditable );
529529
attributeTypeDialog.setLabelOnTop( cfg.mLabelOnTop );
530+
attributeTypeDialog.setNotNull( cfg.mNotNull );
530531

531532
attributeTypeDialog.setWidgetV2Config( cfg.mEditorWidgetV2Config );
532533
attributeTypeDialog.setWidgetV2Type( cfg.mEditorWidgetV2Type );
@@ -536,6 +537,7 @@ void QgsFieldsProperties::attributeTypeDialog()
536537

537538
cfg.mEditable = attributeTypeDialog.fieldEditable();
538539
cfg.mLabelOnTop = attributeTypeDialog.labelOnTop();
540+
cfg.mNotNull = attributeTypeDialog.notNull();
539541

540542
cfg.mEditorWidgetV2Type = attributeTypeDialog.editorWidgetV2Type();
541543
cfg.mEditorWidgetV2Config = attributeTypeDialog.editorWidgetV2Config();
@@ -908,6 +910,7 @@ void QgsFieldsProperties::apply()
908910

909911
mLayer->editFormConfig()->setReadOnly( i, !cfg.mEditable );
910912
mLayer->editFormConfig()->setLabelOnTop( i, cfg.mLabelOnTop );
913+
mLayer->editFormConfig()->setNotNull( i, cfg.mNotNull );
911914

912915
mLayer->editFormConfig()->setWidgetType( idx, cfg.mEditorWidgetV2Type );
913916
mLayer->editFormConfig()->setWidgetConfig( idx, cfg.mEditorWidgetV2Config );
@@ -974,6 +977,7 @@ QgsFieldsProperties::FieldConfig::FieldConfig()
974977
: mEditable( true )
975978
, mEditableEnabled( true )
976979
, mLabelOnTop( false )
980+
, mNotNull( false )
977981
, mButton( nullptr )
978982
{
979983
}
@@ -985,6 +989,7 @@ QgsFieldsProperties::FieldConfig::FieldConfig( QgsVectorLayer* layer, int idx )
985989
mEditableEnabled = layer->fields().fieldOrigin( idx ) != QgsFields::OriginJoin
986990
&& layer->fields().fieldOrigin( idx ) != QgsFields::OriginExpression;
987991
mLabelOnTop = layer->editFormConfig()->labelOnTop( idx );
992+
mNotNull = layer->editFormConfig()->notNull( idx );
988993
mEditorWidgetV2Type = layer->editFormConfig()->widgetType( idx );
989994
mEditorWidgetV2Config = layer->editFormConfig()->widgetConfig( idx );
990995

src/app/qgsfieldsproperties.h

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ class APP_EXPORT QgsFieldsProperties : public QWidget, private Ui_QgsFieldsPrope
9292
bool mEditable;
9393
bool mEditableEnabled;
9494
bool mLabelOnTop;
95+
bool mNotNull;
9596
QPushButton* mButton;
9697
QString mEditorWidgetV2Type;
9798
QMap<QString, QVariant> mEditorWidgetV2Config;

src/core/qgseditformconfig.cpp

+15-1
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,14 @@ bool QgsEditFormConfig::labelOnTop( int idx ) const
119119
return false;
120120
}
121121

122+
bool QgsEditFormConfig::notNull( int idx ) const
123+
{
124+
if ( idx >= 0 && idx < mFields.count() )
125+
return mNotNull.value( mFields.at( idx ).name(), false );
126+
else
127+
return false;
128+
}
129+
122130
void QgsEditFormConfig::setReadOnly( int idx, bool readOnly )
123131
{
124132
if ( idx >= 0 && idx < mFields.count() )
@@ -131,6 +139,12 @@ void QgsEditFormConfig::setLabelOnTop( int idx, bool onTop )
131139
mLabelOnTop[ mFields.at( idx ).name()] = onTop;
132140
}
133141

142+
void QgsEditFormConfig::setNotNull( int idx, bool notnull )
143+
{
144+
if ( idx >= 0 && idx < mFields.count() )
145+
mNotNull[ mFields.at( idx ).name()] = notnull;
146+
}
147+
134148
void QgsEditFormConfig::readXml( const QDomNode& node )
135149
{
136150
QDomNode editFormNode = node.namedItem( "editform" );
@@ -280,7 +294,6 @@ void QgsEditFormConfig::writeXml( QDomNode& node ) const
280294
efifpField.appendChild( doc.createTextNode( QgsProject::instance()->writePath( initFilePath() ) ) );
281295
node.appendChild( efifpField );
282296

283-
284297
QDomElement eficField = doc.createElement( "editforminitcode" );
285298
eficField.appendChild( doc.createCDATASection( initCode() ) );
286299
node.appendChild( eficField );
@@ -337,6 +350,7 @@ void QgsEditFormConfig::writeXml( QDomNode& node ) const
337350
{
338351
QDomElement widgetElem = doc.createElement( "widget" );
339352
widgetElem.setAttribute( "name", configIt.key() );
353+
// widgetElem.setAttribute( "notNull", );
340354

341355
QDomElement configElem = doc.createElement( "config" );
342356
widgetElem.appendChild( configElem );

src/core/qgseditformconfig.h

+10
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,15 @@ class CORE_EXPORT QgsEditFormConfig : public QObject
512512
*/
513513
void setReadOnly( int idx, bool readOnly = true );
514514

515+
/**
516+
* Returns if the field at fieldidx should be treated as NOT NULL value
517+
*/
518+
bool notNull( int fieldidx ) const;
519+
/**
520+
* Set if the field at fieldidx should be treated as NOT NULL value
521+
*/
522+
void setNotNull( int idx, bool notnull = true );
523+
515524
/**
516525
* If this returns true, the widget at the given index will receive its label on the previous line
517526
* while if it returns false, the widget will receive its label on the left hand side.
@@ -633,6 +642,7 @@ class CORE_EXPORT QgsEditFormConfig : public QObject
633642

634643
QMap< QString, bool> mFieldEditables;
635644
QMap< QString, bool> mLabelOnTop;
645+
QMap< QString, bool> mNotNull;
636646

637647
QMap<QString, QString> mEditorWidgetV2Types;
638648
QMap<QString, QgsEditorWidgetConfig > mWidgetConfigs;

src/core/qgseditorwidgetconfig.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#include <QString>
1717
#include <QVariant>
1818

19+
#ifndef QGSEDITORWIDGETCONFIG_H
20+
#define QGSEDITORWIDGETCONFIG_H
1921

2022
/**
2123
* Holds a set of configuration parameters for a editor widget wrapper.
@@ -30,4 +32,6 @@
3032
* You get these passed, for every new widget wrapper.
3133
*/
3234

33-
typedef QMap<QString, QVariant> QgsEditorWidgetConfig;
35+
typedef QVariantMap QgsEditorWidgetConfig;
36+
37+
#endif // QGSEDITORWIDGETCONFIG_H

src/core/qgsfeature.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,12 @@ int QgsFeature::fieldNameIndex( const QString& fieldName ) const
288288
return d->fields.fieldNameIndex( fieldName );
289289
}
290290

291+
/***************************************************************************
292+
* This class is considered CRITICAL and any change MUST be accompanied with
293+
* full unit tests in testqgsfeature.cpp.
294+
* See details in QEP #17
295+
****************************************************************************/
296+
291297
QDataStream& operator<<( QDataStream& out, const QgsFeature& feature )
292298
{
293299
out << feature.id();

src/core/qgsvectorlayer.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -1105,13 +1105,13 @@ QgsFeatureIterator QgsVectorLayer::getFeatures( const QgsFeatureRequest& request
11051105
}
11061106

11071107

1108-
bool QgsVectorLayer::addFeature( QgsFeature& f, bool alsoUpdateExtent )
1108+
bool QgsVectorLayer::addFeature( QgsFeature& feature, bool alsoUpdateExtent )
11091109
{
11101110
Q_UNUSED( alsoUpdateExtent ); // TODO[MD]
11111111
if ( !mValid || !mEditBuffer || !mDataProvider )
11121112
return false;
11131113

1114-
bool success = mEditBuffer->addFeature( f );
1114+
bool success = mEditBuffer->addFeature( feature );
11151115

11161116
if ( success )
11171117
updateExtents();

src/core/qgsvectorlayer.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -975,11 +975,11 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
975975
QgsFeatureIterator getFeatures( const QgsFeatureRequest& request = QgsFeatureRequest() );
976976

977977
/** Adds a feature
978-
@param f feature to add
978+
@param feature feature to add
979979
@param alsoUpdateExtent If True, will also go to the effort of e.g. updating the extents.
980980
@return True in case of success and False in case of error
981981
*/
982-
bool addFeature( QgsFeature& f, bool alsoUpdateExtent = true );
982+
bool addFeature( QgsFeature& feature, bool alsoUpdateExtent = true );
983983

984984
/** Updates an existing feature. This method needs to query the datasource
985985
on every call. Consider using {@link changeAttributeValue()} or

src/gui/editorwidgets/core/qgseditorwidgetregistry.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ void QgsEditorWidgetRegistry::readMapLayer( QgsMapLayer* mapLayer, const QDomEle
251251

252252
vectorLayer->editFormConfig()->setReadOnly( idx, ewv2CfgElem.attribute( "fieldEditable", "1" ) != "1" );
253253
vectorLayer->editFormConfig()->setLabelOnTop( idx, ewv2CfgElem.attribute( "labelOnTop", "0" ) == "1" );
254+
vectorLayer->editFormConfig()->setNotNull( idx, ewv2CfgElem.attribute( "notNull", "0" ) == "1" );
254255
vectorLayer->editFormConfig()->setWidgetConfig( idx, cfg );
255256
}
256257
else
@@ -307,6 +308,7 @@ void QgsEditorWidgetRegistry::writeMapLayer( QgsMapLayer* mapLayer, QDomElement&
307308
QDomElement ewv2CfgElem = doc.createElement( "widgetv2config" );
308309
ewv2CfgElem.setAttribute( "fieldEditable", !vectorLayer->editFormConfig()->readOnly( idx ) );
309310
ewv2CfgElem.setAttribute( "labelOnTop", vectorLayer->editFormConfig()->labelOnTop( idx ) );
311+
ewv2CfgElem.setAttribute( "notNull", vectorLayer->editFormConfig()->notNull( idx ) );
310312

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

src/gui/editorwidgets/core/qgseditorwidgetwrapper.cpp

+27
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ QgsEditorWidgetWrapper::QgsEditorWidgetWrapper( QgsVectorLayer* vl, int fieldIdx
2424
: QgsWidgetWrapper( vl, editor, parent )
2525
, mFieldIdx( fieldIdx )
2626
{
27+
connect( this, SIGNAL( valueChanged( QVariant ) ), this, SLOT( onValueChanged( QVariant ) ) );
2728
}
2829

2930
int QgsEditorWidgetWrapper::fieldIdx() const
@@ -61,6 +62,7 @@ void QgsEditorWidgetWrapper::setEnabled( bool enabled )
6162
void QgsEditorWidgetWrapper::setFeature( const QgsFeature& feature )
6263
{
6364
setValue( feature.attribute( mFieldIdx ) );
65+
onValueChanged( value() );
6466
}
6567

6668
void QgsEditorWidgetWrapper::valueChanged( const QString& value )
@@ -92,3 +94,28 @@ void QgsEditorWidgetWrapper::valueChanged()
9294
{
9395
emit valueChanged( value() );
9496
}
97+
98+
void QgsEditorWidgetWrapper::updateConstraintsOk( bool constraintStatus )
99+
{
100+
if ( constraintStatus )
101+
{
102+
widget()->setStyleSheet( "" );
103+
}
104+
else
105+
{
106+
widget()->setStyleSheet( "QWidget{ background-color: '#dd7777': }" );
107+
}
108+
}
109+
110+
void QgsEditorWidgetWrapper::onValueChanged( const QVariant& value )
111+
{
112+
if ( layer()->editFormConfig()->notNull( mFieldIdx ) )
113+
{
114+
if ( value.isNull() != mIsNull )
115+
{
116+
updateConstraintsOk( value.isNull() );
117+
emit constraintStatusChanged( "NotNull", !value.isNull() );
118+
mIsNull = value.isNull();
119+
}
120+
}
121+
}

src/gui/editorwidgets/core/qgseditorwidgetwrapper.h

+27
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,13 @@ class GUI_EXPORT QgsEditorWidgetWrapper : public QgsWidgetWrapper
118118
*/
119119
void valueChanged( const QVariant& value );
120120

121+
/**
122+
* @brief constraintStatusChanged
123+
* @param constraint
124+
* @param status
125+
*/
126+
void constraintStatusChanged( const QString& constraint, bool status );
127+
121128
public slots:
122129
/**
123130
* Will be called when the feature changes
@@ -185,8 +192,28 @@ class GUI_EXPORT QgsEditorWidgetWrapper : public QgsWidgetWrapper
185192
*/
186193
void valueChanged();
187194

195+
private:
196+
/**
197+
* This should update the widget with a visual cue if a constraint status
198+
* changed.
199+
*
200+
* By default a stylesheet will be applied on the widget that changes the
201+
* background color to red.
202+
*
203+
* This can be overwritten in subclasses to allow individual widgets to
204+
* change the visual cue.
205+
*/
206+
virtual void updateConstraintsOk( bool constraintStatus );
207+
208+
private slots:
209+
/**
210+
* @brief mFieldIdx
211+
*/
212+
void onValueChanged( const QVariant& value );
213+
188214
private:
189215
int mFieldIdx;
216+
bool mIsNull;
190217
};
191218

192219
// We'll use this class inside a QVariant in the widgets properties

src/gui/editorwidgets/qgsrangewidgetfactory.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ QgsEditorWidgetConfig QgsRangeWidgetFactory::readConfig( const QDomElement& conf
3838
{
3939
Q_UNUSED( layer );
4040
Q_UNUSED( fieldIdx );
41-
QMap<QString, QVariant> cfg;
41+
QgsEditorWidgetConfig cfg;
4242

4343
cfg.insert( "Style", configElement.attribute( "Style" ) );
4444
cfg.insert( "Min", configElement.attribute( "Min" ) );

src/gui/editorwidgets/qgsrelationreferenceconfigdlg.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ QgsRelationReferenceConfigDlg::QgsRelationReferenceConfigDlg( QgsVectorLayer* vl
5555
}
5656
}
5757

58-
void QgsRelationReferenceConfigDlg::setConfig( const QMap<QString, QVariant>& config )
58+
void QgsRelationReferenceConfigDlg::setConfig( const QgsEditorWidgetConfig& config )
5959
{
6060
if ( config.contains( "AllowNULL" ) )
6161
{

src/gui/editorwidgets/qgsrelationreferencefactory.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ QgsEditorWidgetConfig QgsRelationReferenceFactory::readConfig( const QDomElement
4646
{
4747
Q_UNUSED( layer );
4848
Q_UNUSED( fieldIdx );
49-
QMap<QString, QVariant> cfg;
49+
QgsEditorWidgetConfig cfg;
5050

5151
cfg.insert( "AllowNULL", configElement.attribute( "AllowNULL" ) == "1" );
5252
cfg.insert( "OrderByValue", configElement.attribute( "OrderByValue" ) == "1" );

0 commit comments

Comments
 (0)