Skip to content

Commit

Permalink
[Feature] conditional visibility for tabs and groupboxes
Browse files Browse the repository at this point in the history
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 ac41436 commit 1882cab
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 8 deletions.
36 changes: 33 additions & 3 deletions src/app/qgsfieldsproperties.cpp
Expand Up @@ -28,6 +28,7 @@
#include "qgsrelationmanager.h"
#include "qgsvectordataprovider.h"
#include "qgsvectorlayer.h"
#include "qgsfieldexpressionwidget.h"

#include <QTreeWidgetItem>
#include <QWidget>
Expand Down Expand Up @@ -72,7 +73,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" ) );

Expand Down Expand Up @@ -154,7 +155,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() )
Expand Down Expand Up @@ -187,6 +188,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() )
Expand Down Expand Up @@ -865,6 +867,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++ )
{
Expand Down Expand Up @@ -1092,8 +1095,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 ) ) );
}
Expand Down Expand Up @@ -1271,11 +1275,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() )
{
Expand All @@ -1299,6 +1314,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() );
}
Expand Down Expand Up @@ -1369,3 +1389,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;
}
9 changes: 8 additions & 1 deletion src/app/qgsfieldsproperties.h
Expand Up @@ -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;
};

/**
Expand Down Expand Up @@ -265,7 +269,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 );

Expand All @@ -282,6 +286,9 @@ class DesignerTree : public QTreeWidget

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

private:
QgsVectorLayer* mLayer;
};

Q_DECLARE_METATYPE( QgsFieldsProperties::FieldConfig )
Expand Down
13 changes: 13 additions & 0 deletions src/core/qgsattributeeditorelement.cpp
Expand Up @@ -26,6 +26,19 @@ void QgsAttributeEditorContainer::setName( const QString& name )
mName = name;
}

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

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

mVisibilityExpression = visibilityExpression;
}

QList<QgsAttributeEditorElement*> QgsAttributeEditorContainer::findElements( QgsAttributeEditorElement::AttributeEditorType type ) const
{
QList<QgsAttributeEditorElement*> results;
Expand Down
5 changes: 5 additions & 0 deletions src/core/qgsattributeeditorelement.h
Expand Up @@ -17,6 +17,7 @@
#define QGSATTRIBUTEEDITORELEMENT_H

#include "qgsrelation.h"
#include "qgsoptionalexpression.h"

class QgsRelationManager;

Expand Down Expand Up @@ -219,13 +220,17 @@ class CORE_EXPORT QgsAttributeEditorContainer : public QgsAttributeEditorElement
*/
virtual QgsAttributeEditorElement* clone( QgsAttributeEditorElement* parent ) const override;

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

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

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

/** \ingroup core
Expand Down
13 changes: 13 additions & 0 deletions src/core/qgseditformconfig.cpp
Expand Up @@ -16,6 +16,7 @@
#include "qgseditformconfig.h"
#include "qgsproject.h"
#include "qgsrelationmanager.h"

//#include "qgseditorwidgetregistry.h"

QgsAttributeEditorContainer::~QgsAttributeEditorContainer()
Expand Down Expand Up @@ -573,6 +574,15 @@ QgsAttributeEditorElement* QgsEditFormConfig::attributeEditorElementFromDomEleme
else
container->setIsGroupBox( 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++ )
Expand Down Expand Up @@ -627,6 +637,7 @@ QgsAttributeEditorElement* QgsAttributeEditorContainer::clone( QgsAttributeEdito
}
element->mIsGroupBox = mIsGroupBox;
element->mColumnCount = mColumnCount;
element->mVisibilityExpression = mVisibilityExpression;

return element;
}
Expand All @@ -635,6 +646,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 )
{
Expand Down
54 changes: 51 additions & 3 deletions src/gui/qgsattributeform.cpp
Expand Up @@ -30,6 +30,7 @@
#include "qgseditorwidgetwrapper.h"
#include "qgsrelationmanager.h"
#include "qgslogger.h"
#include "qgstabwidget.h"

#include <QDir>
#include <QTextStream>
Expand All @@ -42,7 +43,6 @@
#include <QLabel>
#include <QPushButton>
#include <QScrollArea>
#include <QTabWidget>
#include <QUiLoader>
#include <QMessageBox>
#include <QSettings>
Expand Down Expand Up @@ -747,6 +747,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 );
}
}
}

Expand Down Expand Up @@ -810,6 +818,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 )
{
Expand Down Expand Up @@ -1095,7 +1112,7 @@ void QgsAttributeForm::init()
}
}

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

// Tab layout
if ( !formWidget && mLayer->editFormConfig().layout() == QgsEditFormConfig::TabLayout )
Expand All @@ -1117,20 +1134,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 );

Expand Down Expand Up @@ -1595,6 +1618,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 );
Expand Down Expand Up @@ -1875,3 +1904,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;
}
}
32 changes: 32 additions & 0 deletions src/gui/qgsattributeform.h
Expand Up @@ -28,6 +28,7 @@ class QgsAttributeFormEditorWidget;
class QgsMessageBar;
class QgsMessageBarItem;
class QgsWidgetWrapper;
class QgsTabWidget;

/** \ingroup gui
* \class QgsAttributeForm
Expand Down Expand Up @@ -343,6 +344,37 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
QWidget* mSearchButtonBox;
QList<QgsAttributeFormInterface*> mInterfaces;
QMap< int, QgsAttributeFormEditorWidget* > mFormEditorWidgets;
QgsExpressionContext mExpressionContext;

struct ContainerInformation
{
ContainerInformation( QgsTabWidget* tabWidget, QWidget* widget, QgsExpression expression )
: tabWidget( tabWidget )
, widget( widget )
, expression( expression )
, isVisible( true )
{}

ContainerInformation( QWidget* widget, QgsExpression expression )
: tabWidget( nullptr )
, widget( widget )
, expression( expression )
, isVisible( true )
{}

QgsTabWidget* tabWidget;
QWidget* widget;
QgsExpression expression;
bool isVisible;

void apply( QgsExpressionContext* expressionContext );
};

void registerContainerInformation( ContainerInformation* info );

// Contains information about tabs and groupboxes, their visibility state visibility conditions
QVector<ContainerInformation*> mContainerVisibilityInformation;
QMap<QString, QVector<ContainerInformation*> > mContainerInformationDependency;

// Variables below are used for python
static int sFormCounter;
Expand Down

0 comments on commit 1882cab

Please sign in to comment.