Skip to content

Commit

Permalink
Merge pull request #5468 from m-kuhn/unobtrusiveConstraints
Browse files Browse the repository at this point in the history
[FEATURE] Make constraint reporting less obtrusive
  • Loading branch information
m-kuhn committed Oct 27, 2017
2 parents b0ee642 + e38bd65 commit 6829a92
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 159 deletions.
5 changes: 5 additions & 0 deletions python/gui/qgsattributeformeditorwidget.sip
Expand Up @@ -96,6 +96,11 @@ class QgsAttributeFormEditorWidget : QWidget
:rtype: str
%End

void setConstraintStatus( const QString &constraint, const QString &description, const QString &err, QgsEditorWidgetWrapper::ConstraintResult result );
%Docstring
Set the constraint status for this widget.
%End

public slots:

void setIsMixed( bool mixed );
Expand Down
4 changes: 2 additions & 2 deletions src/gui/editorwidgets/core/qgseditorwidgetwrapper.cpp
Expand Up @@ -110,11 +110,11 @@ void QgsEditorWidgetWrapper::updateConstraintWidgetStatus( ConstraintResult cons
break;

case ConstraintResultFailHard:
widget()->setStyleSheet( QStringLiteral( "background-color: #dd7777;" ) );
widget()->setStyleSheet( QStringLiteral( "background-color: #FFE0B2;" ) );
break;

case ConstraintResultFailSoft:
widget()->setStyleSheet( QStringLiteral( "background-color: #ffd85d;" ) );
widget()->setStyleSheet( QStringLiteral( "background-color: #FFECB3;" ) );
break;
}
}
Expand Down
130 changes: 26 additions & 104 deletions src/gui/qgsattributeform.cpp
Expand Up @@ -201,15 +201,6 @@ void QgsAttributeForm::setMode( QgsAttributeForm::Mode mode )
case QgsAttributeForm::SearchMode:
mSearchButtonBox->setVisible( true );
hideButtonBox();
if ( mContext.formMode() != QgsAttributeEditorContext::Embed )
{
delete mInvalidConstraintMessage;
mInvalidConstraintMessage = nullptr;
}
else
{
mTopMessageWidget->hide();
}
break;
}

Expand Down Expand Up @@ -726,9 +717,9 @@ void QgsAttributeForm::updateConstraints( QgsEditorWidgetWrapper *eww )
updateConstraint( ft, eww );

// update eww dependencies constraint
QList<QgsEditorWidgetWrapper *> deps = constraintDependencies( eww );
const QList<QgsEditorWidgetWrapper *> deps = constraintDependencies( eww );

Q_FOREACH ( QgsEditorWidgetWrapper *depsEww, deps )
for ( QgsEditorWidgetWrapper *depsEww : deps )
updateConstraint( ft, depsEww );

// sync OK button status
Expand All @@ -737,7 +728,8 @@ void QgsAttributeForm::updateConstraints( QgsEditorWidgetWrapper *eww )
mExpressionContext.setFeature( ft );

// Recheck visibility for all containers which are controlled by this value
Q_FOREACH ( ContainerInformation *info, mContainerInformationDependency.value( eww->field().name() ) )
const QVector<ContainerInformation *> infos = mContainerInformationDependency.value( eww->field().name() );
for ( ContainerInformation *info : infos )
{
info->apply( &mExpressionContext );
}
Expand Down Expand Up @@ -776,10 +768,9 @@ bool QgsAttributeForm::currentFormFeature( QgsFeature &feature )
{
bool rc = true;
feature = QgsFeature( mFeature );
QgsAttributes src = feature.attributes();
QgsAttributes dst = feature.attributes();

Q_FOREACH ( QgsWidgetWrapper *ww, mWidgets )
for ( QgsWidgetWrapper *ww : qgis::as_const( mWidgets ) )
{
QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );

Expand Down Expand Up @@ -807,45 +798,24 @@ bool QgsAttributeForm::currentFormFeature( QgsFeature &feature )
return rc;
}

void QgsAttributeForm::clearInvalidConstraintsMessage()
{
mTopMessageWidget->hide();
mInvalidConstraintMessage->clear();
}

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 += QStringLiteral( "<li>%1: <i>%2</i></li>" ).arg( f[i], d[i] );

QString msg = QStringLiteral( "<b>%1</b><ul>%2</ul>" ).arg( tr( "Invalid fields" ), descriptions );

mInvalidConstraintMessage->setText( msg );
mTopMessageWidget->show();
}

void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation *info )
{
mContainerVisibilityInformation.append( info );
Q_FOREACH ( const QString &col, info->expression.referencedColumns() )

const QSet<QString> referencedColumns = info->expression.referencedColumns();

for ( const QString &col : referencedColumns )
{
mContainerInformationDependency[ col ].append( info );
}
}

bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields,
QStringList &descriptions )
bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions )
{
bool valid( true );

Q_FOREACH ( QgsWidgetWrapper *ww, mWidgets )
for ( QgsWidgetWrapper *ww : qgis::as_const( mWidgets ) )
{
QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
if ( eww )
Expand Down Expand Up @@ -928,35 +898,9 @@ void QgsAttributeForm::onConstraintStatusChanged( const QString &constraint,
QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( sender() );
Q_ASSERT( eww );

QLabel *buddy = mBuddyMap.value( eww->widget() );

if ( buddy )
{
QString tooltip = QStringLiteral( "<b>" ) + tr( "Constraints: " ) + QStringLiteral( "</b>" ) + description +
QStringLiteral( "<br /><b>" ) + tr( "Raw expression: " ) + QStringLiteral( "</b>" ) + constraint +
QStringLiteral( "<br /><b>" ) + tr( "Result: " ) + QStringLiteral( "</b>" ) + err;
buddy->setToolTip( tooltip );

if ( !buddy->property( "originalText" ).isValid() )
buddy->setProperty( "originalText", buddy->text() );

QString text = buddy->property( "originalText" ).toString();

switch ( result )
{
case QgsEditorWidgetWrapper::ConstraintResultFailHard:
buddy->setText( trUtf8( "%1<font color=\"red\">✘</font>" ).arg( text ) );
break;

case QgsEditorWidgetWrapper::ConstraintResultFailSoft:
buddy->setText( trUtf8( "%1<font color=\"orange\">✘</font>" ).arg( text ) );
break;
QgsAttributeFormEditorWidget *formEditorWidget = mFormEditorWidgets.value( eww->fieldIdx() );

case QgsEditorWidgetWrapper::ConstraintResultPass:
buddy->setText( trUtf8( "%1<font color=\"green\">✔</font>" ).arg( text ) );
break;
}
}
formEditorWidget->setConstraintStatus( constraint, description, err, result );
}

QList<QgsEditorWidgetWrapper *> QgsAttributeForm::constraintDependencies( QgsEditorWidgetWrapper *w )
Expand All @@ -965,7 +909,7 @@ QList<QgsEditorWidgetWrapper *> QgsAttributeForm::constraintDependencies( QgsEdi
QString name = w->field().name();

// for each widget in the current form
Q_FOREACH ( QgsWidgetWrapper *ww, mWidgets )
for ( QgsWidgetWrapper *ww : qgis::as_const( mWidgets ) )
{
// get the wrapper
QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
Expand All @@ -978,7 +922,9 @@ QList<QgsEditorWidgetWrapper *> QgsAttributeForm::constraintDependencies( QgsEdi
// get expression and referencedColumns
QgsExpression expr = eww->layer()->fields().at( eww->fieldIdx() ).constraints().constraintExpression();

Q_FOREACH ( const QString &colName, expr.referencedColumns() )
const auto referencedColumns = expr.referencedColumns();

for ( const QString &colName : referencedColumns )
{
if ( name == colName )
{
Expand Down Expand Up @@ -1018,7 +964,7 @@ void QgsAttributeForm::synchronizeEnabledState()
|| mMode == AddFeatureMode
|| mMode == MultiEditMode ) && mLayer->isEditable();

Q_FOREACH ( QgsWidgetWrapper *ww, mWidgets )
for ( QgsWidgetWrapper *ww : qgis::as_const( mWidgets ) )
{
QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
if ( eww )
Expand All @@ -1030,17 +976,11 @@ void QgsAttributeForm::synchronizeEnabledState()
}
}

// push a message and disable the OK button if constraints are invalid
clearInvalidConstraintsMessage();

if ( mMode != SearchMode )
{
QStringList invalidFields, descriptions;
bool validConstraint = currentFormValidConstraints( invalidFields, descriptions );

if ( ! invalidFields.isEmpty() )
displayInvalidConstraintMessage( invalidFields, descriptions );

isEditable = isEditable & validConstraint;
}

Expand Down Expand Up @@ -1088,19 +1028,6 @@ void QgsAttributeForm::init()
mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
vl->addWidget( mMessageBar );

mTopMessageWidget = new QWidget();
mTopMessageWidget->hide();
mTopMessageWidget->setLayout( new QHBoxLayout() );

QSvgWidget *warningIcon = new QSvgWidget( QgsApplication::iconPath( QStringLiteral( "/mIconWarning.svg" ) ) );
warningIcon->setFixedSize( 48, 48 );
mTopMessageWidget->layout()->addWidget( warningIcon );
mInvalidConstraintMessage = new QLabel( this );
mTopMessageWidget->layout()->addWidget( mInvalidConstraintMessage );
mTopMessageWidget->hide();

vl->addWidget( mTopMessageWidget );

setLayout( vl );

// Get a layout
Expand Down Expand Up @@ -1147,7 +1074,9 @@ void QgsAttributeForm::init()
int column = 0;
int columnCount = 1;

Q_FOREACH ( QgsAttributeEditorElement *widgDef, mLayer->editFormConfig().tabs() )
const QList<QgsAttributeEditorElement *> tabs = mLayer->editFormConfig().tabs();

for ( QgsAttributeEditorElement *widgDef : tabs )
{
if ( widgDef->type() == QgsAttributeEditorElement::AeTypeContainer )
{
Expand Down Expand Up @@ -1260,9 +1189,12 @@ void QgsAttributeForm::init()
}

int row = 0;
Q_FOREACH ( const QgsField &field, mLayer->fields().toList() )

const QgsFields fields = mLayer->fields();

for ( const QgsField &field : fields )
{
int idx = mLayer->fields().lookupField( field.name() );
int idx = fields.lookupField( field.name() );
if ( idx < 0 )
continue;

Expand Down Expand Up @@ -1817,16 +1749,6 @@ void QgsAttributeForm::afterWidgetInit()
connect( eww, &QgsEditorWidgetWrapper::constraintStatusChanged, this, &QgsAttributeForm::onConstraintStatusChanged );
}
}

// Update buddy widget list
mBuddyMap.clear();
QList<QLabel *> labels = findChildren<QLabel *>();

Q_FOREACH ( QLabel *label, labels )
{
if ( label->buddy() )
mBuddyMap.insert( label->buddy(), label );
}
}


Expand Down
7 changes: 0 additions & 7 deletions src/gui/qgsattributeform.h
Expand Up @@ -340,18 +340,13 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
bool currentFormFeature( QgsFeature &feature );
bool currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions );
QList<QgsEditorWidgetWrapper *> constraintDependencies( QgsEditorWidgetWrapper *w );
void clearInvalidConstraintsMessage();
void displayInvalidConstraintMessage( const QStringList &invalidFields,
const QStringList &description );

QgsVectorLayer *mLayer = nullptr;
QgsFeature mFeature;
QgsMessageBar *mMessageBar = nullptr;
bool mOwnsMessageBar;
QgsMessageBarItem *mMultiEditUnsavedMessageBarItem = nullptr;
QgsMessageBarItem *mMultiEditMessageBarItem = nullptr;
QLabel *mInvalidConstraintMessage = nullptr;
QWidget *mTopMessageWidget = nullptr;
QList<QgsWidgetWrapper *> mWidgets;
QgsAttributeEditorContext mContext;
QDialogButtonBox *mButtonBox = nullptr;
Expand Down Expand Up @@ -416,8 +411,6 @@ class GUI_EXPORT QgsAttributeForm : public QWidget

Mode mMode;

//! Backlinks widgets to buddies.
QMap<QWidget *, QLabel *> mBuddyMap;
QMap<QWidget *, QSvgWidget *> mIconMap;

friend class TestQgsDualView;
Expand Down
28 changes: 28 additions & 0 deletions src/gui/qgsattributeformeditorwidget.cpp
Expand Up @@ -37,6 +37,10 @@ QgsAttributeFormEditorWidget::QgsAttributeFormEditorWidget( QgsEditorWidgetWrapp
, mIsMixed( false )
, mIsChanged( false )
{
mConstraintResultLabel = new QLabel( this );
mConstraintResultLabel->setObjectName( QStringLiteral( "ConstraintStatus" ) );
mConstraintResultLabel->setSizePolicy( QSizePolicy::Fixed, mConstraintResultLabel->sizePolicy().verticalPolicy() );

mEditPage = new QWidget();
QHBoxLayout *l = new QHBoxLayout();
l->setMargin( 0 );
Expand Down Expand Up @@ -134,6 +138,27 @@ QList< QgsSearchWidgetWrapper * > QgsAttributeFormEditorWidget::searchWidgetWrap
return mSearchWidgets;
}

void QgsAttributeFormEditorWidget::setConstraintStatus( const QString &constraint, const QString &description, const QString &err, QgsEditorWidgetWrapper::ConstraintResult result )
{
switch ( result )
{
case QgsEditorWidgetWrapper::ConstraintResultFailHard:
mConstraintResultLabel->setText( QStringLiteral( "<font color=\"#FF9800\">✘</font>" ) );
mConstraintResultLabel->setToolTip( description.isEmpty() ? QStringLiteral( "<b>%1</b>: %2" ).arg( constraint, err ) : description );
break;

case QgsEditorWidgetWrapper::ConstraintResultFailSoft:
mConstraintResultLabel->setText( QStringLiteral( "<font color=\"#FFC107\">✘</font>" ) );
mConstraintResultLabel->setToolTip( description.isEmpty() ? QStringLiteral( "<b>%1</b>: %2" ).arg( constraint, err ) : description );
break;

case QgsEditorWidgetWrapper::ConstraintResultPass:
mConstraintResultLabel->setText( QStringLiteral( "<font color=\"#259b24\">✔</font>" ) );
mConstraintResultLabel->setToolTip( QString() );
break;
}
}

void QgsAttributeFormEditorWidget::setMode( QgsAttributeFormEditorWidget::Mode mode )
{
mMode = mode;
Expand Down Expand Up @@ -316,6 +341,9 @@ void QgsAttributeFormEditorWidget::updateWidgets()
case MultiEditMode:
{
mStack->setCurrentWidget( mEditPage );

mEditPage->layout()->addWidget( mConstraintResultLabel );

break;
}

Expand Down
9 changes: 8 additions & 1 deletion src/gui/qgsattributeformeditorwidget.h
Expand Up @@ -23,6 +23,7 @@
#include "qgsattributeeditorcontext.h"
#include "qgssearchwidgetwrapper.h"
#include "qgis_gui.h"
#include "qgseditorwidgetwrapper.h"

class QgsAttributeForm;
class QgsEditorWidgetWrapper;
Expand All @@ -31,6 +32,7 @@ class QgsSearchWidgetToolButton;
class QgsVectorLayer;
class QStackedWidget;
class QgsAttributeEditorContext;
class QLabel;

/**
* \ingroup gui
Expand Down Expand Up @@ -116,6 +118,11 @@ class GUI_EXPORT QgsAttributeFormEditorWidget : public QWidget
*/
QString currentFilterExpression() const;

/**
* Set the constraint status for this widget.
*/
void setConstraintStatus( const QString &constraint, const QString &description, const QString &err, QgsEditorWidgetWrapper::ConstraintResult result );

public slots:

/**
Expand Down Expand Up @@ -202,6 +209,7 @@ class GUI_EXPORT QgsAttributeFormEditorWidget : public QWidget
QgsEditorWidgetWrapper *mWidget = nullptr;
QList< QgsSearchWidgetWrapper * > mSearchWidgets;
QgsAttributeForm *mForm = nullptr;
QLabel *mConstraintResultLabel = nullptr;
Mode mMode;

QgsMultiEditToolButton *mMultiEditButton = nullptr;
Expand All @@ -211,7 +219,6 @@ class GUI_EXPORT QgsAttributeFormEditorWidget : public QWidget
bool mIsMixed;
bool mIsChanged;


QgsVectorLayer *layer();
void updateWidgets();
};
Expand Down

0 comments on commit 6829a92

Please sign in to comment.