Skip to content
Permalink
Browse files
[Feature] conditional visibility for tabs and groupboxes
This adds a new configuration option to conditionally show or hide
tabs and groupboxes in drag and drop designer forms.

Configuration is done via a double click in the designer tree in the
fields configuration interface.

An expression can be entered to control the visibility. The expression
will be re-evaluated everytime values in the form change and the tab or
groupbox shown/hidden accordingly.
  • Loading branch information
m-kuhn committed Sep 9, 2016
1 parent 3fc7d17 commit 46606b2d775df0f56e302283ff790d426f775c9f
@@ -15,7 +15,7 @@ class QgsExpression
/**
* Create an empty expression.
*
* @note Added in QGIS 3.0
* @note Added in QGIS 2.18
*/
QgsExpression();

@@ -25,7 +25,7 @@ class QgsExpression
* Checks if this expression is valid.
* A valid expression could be parsed but does not necessarily evaluate properly.
*
* @note Added in QGIS 3.0
* @note Added in QGIS 2.18
*/
bool isValid() const;

@@ -153,14 +153,7 @@ class QgsExpression
/**
* Set the expression string, will reset the whole internal structure.
*
* @note Added in QGIS 3.0
*/
void setExpression( const QString& expression );

/**
* Set the expression string, will reset the whole internal structure.
*
* @note Added in QGIS 3.0
* @note Added in QGIS 2.18
*/
void setExpression( const QString& expression );

@@ -28,6 +28,7 @@
#include "qgsrelationmanager.h"
#include "qgsvectordataprovider.h"
#include "qgsvectorlayer.h"
#include "qgsfieldexpressionwidget.h"

#include <QTreeWidgetItem>
#include <QWidget>
@@ -73,7 +74,7 @@ QgsFieldsProperties::QgsFieldsProperties( QgsVectorLayer *layer, QWidget* parent
// tab and group display
mAddItemButton->setEnabled( false );

mDesignerTree = new DesignerTree( mAttributesTreeFrame );
mDesignerTree = new DesignerTree( mLayer, mAttributesTreeFrame );
mDesignerListLayout->addWidget( mDesignerTree );
mDesignerTree->setHeaderLabels( QStringList() << tr( "Label" ) );

@@ -155,7 +156,7 @@ void QgsFieldsProperties::onAttributeSelectionChanged()
updateButtons();
}

QTreeWidgetItem *QgsFieldsProperties::loadAttributeEditorTreeItem( QgsAttributeEditorElement* const widgetDef, QTreeWidgetItem* parent )
QTreeWidgetItem* QgsFieldsProperties::loadAttributeEditorTreeItem( QgsAttributeEditorElement* const widgetDef, QTreeWidgetItem* parent )
{
QTreeWidgetItem* newWidget = nullptr;
switch ( widgetDef->type() )
@@ -188,6 +189,7 @@ QTreeWidgetItem *QgsFieldsProperties::loadAttributeEditorTreeItem( QgsAttributeE

itemData.setColumnCount( container->columnCount() );
itemData.setShowAsGroupBox( container->isGroupBox() );
itemData.setVisibilityExpression( container->visibilityExpression() );
newWidget = mDesignerTree->addItem( parent, itemData );

Q_FOREACH ( QgsAttributeEditorElement* wdg, container->children() )
@@ -901,6 +903,7 @@ QgsAttributeEditorElement* QgsFieldsProperties::createAttributeEditorWidget( QTr
QgsAttributeEditorContainer* container = new QgsAttributeEditorContainer( item->text( 0 ), parent );
container->setColumnCount( itemData.columnCount() );
container->setIsGroupBox( forceGroup ? true : itemData.showAsGroupBox() );
container->setVisibilityExpression( itemData.visibilityExpression() );

for ( int t = 0; t < item->childCount(); t++ )
{
@@ -1124,8 +1127,9 @@ QTreeWidgetItem* DesignerTree::addContainer( QTreeWidgetItem* parent, const QStr
return newItem;
}

DesignerTree::DesignerTree( QWidget* parent )
DesignerTree::DesignerTree( QgsVectorLayer* layer, QWidget* parent )
: QTreeWidget( parent )
, mLayer( layer )
{
connect( this, SIGNAL( itemDoubleClicked( QTreeWidgetItem*, int ) ), this, SLOT( onItemDoubleClicked( QTreeWidgetItem*, int ) ) );
}
@@ -1303,11 +1307,22 @@ void DesignerTree::onItemDoubleClicked( QTreeWidgetItem* item, int column )
QCheckBox* showAsGroupBox = nullptr;
QLineEdit* title = new QLineEdit( itemData.name() );
QSpinBox* columnCount = new QSpinBox();
QGroupBox* visibilityExpressionGroupBox = new QGroupBox( tr( "Control visibility by expression " ) );
visibilityExpressionGroupBox->setCheckable( true );
visibilityExpressionGroupBox->setChecked( itemData.visibilityExpression().enabled() );
visibilityExpressionGroupBox->setLayout( new QGridLayout );
QgsFieldExpressionWidget* visibilityExpressionWidget = new QgsFieldExpressionWidget;
visibilityExpressionWidget->setLayer( mLayer );
visibilityExpressionWidget->setExpressionDialogTitle( tr( "Visibility expression" ) );
visibilityExpressionWidget->setExpression( itemData.visibilityExpression()->expression() );
visibilityExpressionGroupBox->layout()->addWidget( visibilityExpressionWidget );

columnCount->setRange( 1, 5 );
columnCount->setValue( itemData.columnCount() );

layout->addRow( tr( "Title" ), title );
layout->addRow( tr( "Column count" ), columnCount );
layout->addWidget( visibilityExpressionGroupBox );

if ( !item->parent() )
{
@@ -1331,6 +1346,11 @@ void DesignerTree::onItemDoubleClicked( QTreeWidgetItem* item, int column )
itemData.setName( title->text() );
itemData.setShowLabel( showLabelCheckbox->isChecked() );

QgsOptionalExpression visibilityExpression;
visibilityExpression.setData( QgsExpression( visibilityExpressionWidget->expression() ) );
visibilityExpression.setEnabled( visibilityExpressionGroupBox->isChecked() );
itemData.setVisibilityExpression( visibilityExpression );

item->setData( 0, QgsFieldsProperties::DesignerTreeRole, itemData.asQVariant() );
item->setText( 0, title->text() );
}
@@ -1401,3 +1421,13 @@ void QgsFieldsProperties::DesignerTreeItemData::setShowLabel( bool showLabel )
{
mShowLabel = showLabel;
}

QgsOptionalExpression QgsFieldsProperties::DesignerTreeItemData::visibilityExpression() const
{
return mVisibilityExpression;
}

void QgsFieldsProperties::DesignerTreeItemData::setVisibilityExpression( const QgsOptionalExpression& visibilityExpression )
{
mVisibilityExpression = visibilityExpression;
}
@@ -84,12 +84,16 @@ class APP_EXPORT QgsFieldsProperties : public QWidget, private Ui_QgsFieldsPrope
bool showLabel() const;
void setShowLabel( bool showLabel );

QgsOptionalExpression visibilityExpression() const;
void setVisibilityExpression( const QgsOptionalExpression& visibilityExpression );

private:
Type mType;
QString mName;
int mColumnCount;
bool mShowAsGroupBox;
bool mShowLabel;
QgsOptionalExpression mVisibilityExpression;
};

/**
@@ -270,7 +274,7 @@ class DesignerTree : public QTreeWidget
Q_OBJECT

public:
explicit DesignerTree( QWidget* parent = nullptr );
explicit DesignerTree( QgsVectorLayer* layer, QWidget* parent = nullptr );
QTreeWidgetItem* addItem( QTreeWidgetItem* parent, QgsFieldsProperties::DesignerTreeItemData data );
QTreeWidgetItem* addContainer( QTreeWidgetItem* parent, const QString& title , int columnCount );

@@ -287,6 +291,9 @@ class DesignerTree : public QTreeWidget

private slots:
void onItemDoubleClicked( QTreeWidgetItem* item, int column );

private:
QgsVectorLayer* mLayer;
};

Q_DECLARE_METATYPE( QgsFieldsProperties::FieldConfig )
@@ -427,6 +427,15 @@ QgsAttributeEditorElement* QgsEditFormConfig::attributeEditorElementFromDomEleme
else
container->setIsGroupBox( qobject_cast<QgsAttributeEditorContainer*>( parent ) );

bool visibilityExpressionEnabled = elem.attribute( "visibilityExpressionEnabled" ).toInt( &ok );
QgsOptionalExpression visibilityExpression;
if ( ok )
{
visibilityExpression.setEnabled( visibilityExpressionEnabled );
visibilityExpression.setData( QgsExpression( elem.attribute( "visibilityExpression" ) ) );
}
container->setVisibilityExpression( visibilityExpression );

QDomNodeList childNodeList = elem.childNodes();

for ( int i = 0; i < childNodeList.size(); i++ )
@@ -498,6 +507,8 @@ void QgsAttributeEditorContainer::saveConfiguration( QDomElement& elem ) const
{
elem.setAttribute( "columnCount", mColumnCount );
elem.setAttribute( "groupBox", mIsGroupBox ? 1 : 0 );
elem.setAttribute( "visibilityExpressionEnabled", mVisibilityExpression.enabled() ? 1 : 0 );
elem.setAttribute( "visibilityExpression", mVisibilityExpression->expression() );

Q_FOREACH ( QgsAttributeEditorElement* child, mChildren )
{
@@ -506,6 +517,16 @@ void QgsAttributeEditorContainer::saveConfiguration( QDomElement& elem ) const
}
}

QgsOptionalExpression QgsAttributeEditorContainer::visibilityExpression() const
{
return mVisibilityExpression;
}

void QgsAttributeEditorContainer::setVisibilityExpression( const QgsOptionalExpression& visibilityExpression )
{
mVisibilityExpression = visibilityExpression;
}

QString QgsAttributeEditorContainer::typeIdentifier() const
{
return "attributeEditorContainer";
@@ -22,6 +22,7 @@

#include "qgseditorwidgetconfig.h"
#include "qgsrelationmanager.h"
#include "qgsoptionalexpression.h"

/** \ingroup core
* This is an abstract base class for any elements of a drag and drop form.
@@ -198,13 +199,28 @@ class CORE_EXPORT QgsAttributeEditorContainer : public QgsAttributeEditorElement
*/
void setColumnCount( int columnCount );

/**
* An expression that controls the visibility of this container.
*
* @note Added in QGIS 2.18
*/
QgsOptionalExpression visibilityExpression() const;

/**
* An expression that controls the visibility of this container.
*
* @note Added in QGIS 2.18
*/
void setVisibilityExpression( const QgsOptionalExpression& visibilityExpression );

private:
virtual void saveConfiguration( QDomElement& elem ) const override;
virtual QString typeIdentifier() const override;

bool mIsGroupBox;
QList<QgsAttributeEditorElement*> mChildren;
int mColumnCount;
QgsOptionalExpression mVisibilityExpression;
};

/** \ingroup core
@@ -3571,6 +3571,14 @@ bool QgsExpression::isValid( const QString &text, const QgsExpressionContext *co
return !exp.hasParserError();
}

void QgsExpression::setExpression( const QString& expression )
{
detach();
d->mRootNode = ::parseExpression( expression, d->mParserErrorString );
d->mEvalErrorString = QString();
d->mExp = expression;
}

void QgsExpression::setScale( double scale ) { d->mScale = scale; }

double QgsExpression::scale() { return d->mScale; }
@@ -26,6 +26,7 @@
#include "qgsattributeformeditorwidget.h"
#include "qgsmessagebar.h"
#include "qgsmessagebaritem.h"
#include "qgstabwidget.h"

#include <QDir>
#include <QTextStream>
@@ -38,7 +39,6 @@
#include <QLabel>
#include <QPushButton>
#include <QScrollArea>
#include <QTabWidget>
#include <QUiLoader>
#include <QMessageBox>
#include <QSettings>
@@ -744,6 +744,14 @@ void QgsAttributeForm::updateConstraints( QgsEditorWidgetWrapper *eww )

// sync ok button status
synchronizeEnabledState();

mExpressionContext.setFeature( ft );

// Recheck visibility for all containers which are controlled by this value
Q_FOREACH ( ContainerInformation* info, mContainerInformationDependency.value( eww->field().name() ) )
{
info->apply( &mExpressionContext );
}
}
}

@@ -807,6 +815,15 @@ void QgsAttributeForm::displayInvalidConstraintMessage( const QStringList& f,
mInvalidConstraintMessage->setStyleSheet( "QLabel { background-color : #ffc800; }" );
}

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

bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields,
QStringList &descriptions )
{
@@ -1090,7 +1107,7 @@ void QgsAttributeForm::init()
}
}

QTabWidget* tabWidget = nullptr;
QgsTabWidget* tabWidget = nullptr;

// Tab layout
if ( !formWidget && mLayer->editFormConfig()->layout() == QgsEditFormConfig::TabLayout )
@@ -1112,20 +1129,26 @@ void QgsAttributeForm::init()
tabWidget = nullptr;
WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
registerContainerInformation( new ContainerInformation( widgetInfo.widget, containerDef->visibilityExpression().data() ) );
column += 2;
}
else
{
if ( !tabWidget )
{
tabWidget = new QTabWidget();
tabWidget = new QgsTabWidget();
layout->addWidget( tabWidget, row, column, 1, 2 );
column += 2;
}

QWidget* tabPage = new QWidget( tabWidget );

tabWidget->addTab( tabPage, widgDef->name() );

if ( containerDef->visibilityExpression().enabled() )
{
registerContainerInformation( new ContainerInformation( tabWidget, tabPage, containerDef->visibilityExpression().data() ) );
}
QGridLayout* tabPageLayout = new QGridLayout();
tabPage->setLayout( tabPageLayout );

@@ -1592,6 +1615,12 @@ QgsAttributeForm::WidgetInfo QgsAttributeForm::createWidgetFromDef( const QgsAtt
{
WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );

if ( childDef->type() == QgsAttributeEditorElement::AeTypeContainer )
{
QgsAttributeEditorContainer* containerDef = static_cast<QgsAttributeEditorContainer*>( childDef );
registerContainerInformation( new ContainerInformation( widgetInfo.widget, containerDef->visibilityExpression().data() ) );
}

if ( widgetInfo.labelText.isNull() )
{
gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
@@ -1873,3 +1902,22 @@ int QgsAttributeForm::messageTimeout()
QSettings settings;
return settings.value( "/qgis/messageTimeout", 5 ).toInt();
}

void QgsAttributeForm::ContainerInformation::apply( QgsExpressionContext* expressionContext )
{
bool newVisibility = expression.evaluate( expressionContext ).toBool();

if ( newVisibility != isVisible )
{
if ( tabWidget )
{
tabWidget->setTabVisible( widget, newVisibility );
}
else
{
widget->setVisible( newVisibility );
}

isVisible = newVisibility;
}
}

0 comments on commit 46606b2

Please sign in to comment.