Skip to content

Commit 46606b2

Browse files
committed
[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.
1 parent 3fc7d17 commit 46606b2

10 files changed

+203
-18
lines changed

python/core/qgsexpression.sip

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class QgsExpression
1515
/**
1616
* Create an empty expression.
1717
*
18-
* @note Added in QGIS 3.0
18+
* @note Added in QGIS 2.18
1919
*/
2020
QgsExpression();
2121

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

@@ -153,14 +153,7 @@ class QgsExpression
153153
/**
154154
* Set the expression string, will reset the whole internal structure.
155155
*
156-
* @note Added in QGIS 3.0
157-
*/
158-
void setExpression( const QString& expression );
159-
160-
/**
161-
* Set the expression string, will reset the whole internal structure.
162-
*
163-
* @note Added in QGIS 3.0
156+
* @note Added in QGIS 2.18
164157
*/
165158
void setExpression( const QString& expression );
166159

src/app/qgsfieldsproperties.cpp

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "qgsrelationmanager.h"
2929
#include "qgsvectordataprovider.h"
3030
#include "qgsvectorlayer.h"
31+
#include "qgsfieldexpressionwidget.h"
3132

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

76-
mDesignerTree = new DesignerTree( mAttributesTreeFrame );
77+
mDesignerTree = new DesignerTree( mLayer, mAttributesTreeFrame );
7778
mDesignerListLayout->addWidget( mDesignerTree );
7879
mDesignerTree->setHeaderLabels( QStringList() << tr( "Label" ) );
7980

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

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

189190
itemData.setColumnCount( container->columnCount() );
190191
itemData.setShowAsGroupBox( container->isGroupBox() );
192+
itemData.setVisibilityExpression( container->visibilityExpression() );
191193
newWidget = mDesignerTree->addItem( parent, itemData );
192194

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

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

1127-
DesignerTree::DesignerTree( QWidget* parent )
1130+
DesignerTree::DesignerTree( QgsVectorLayer* layer, QWidget* parent )
11281131
: QTreeWidget( parent )
1132+
, mLayer( layer )
11291133
{
11301134
connect( this, SIGNAL( itemDoubleClicked( QTreeWidgetItem*, int ) ), this, SLOT( onItemDoubleClicked( QTreeWidgetItem*, int ) ) );
11311135
}
@@ -1303,11 +1307,22 @@ void DesignerTree::onItemDoubleClicked( QTreeWidgetItem* item, int column )
13031307
QCheckBox* showAsGroupBox = nullptr;
13041308
QLineEdit* title = new QLineEdit( itemData.name() );
13051309
QSpinBox* columnCount = new QSpinBox();
1310+
QGroupBox* visibilityExpressionGroupBox = new QGroupBox( tr( "Control visibility by expression " ) );
1311+
visibilityExpressionGroupBox->setCheckable( true );
1312+
visibilityExpressionGroupBox->setChecked( itemData.visibilityExpression().enabled() );
1313+
visibilityExpressionGroupBox->setLayout( new QGridLayout );
1314+
QgsFieldExpressionWidget* visibilityExpressionWidget = new QgsFieldExpressionWidget;
1315+
visibilityExpressionWidget->setLayer( mLayer );
1316+
visibilityExpressionWidget->setExpressionDialogTitle( tr( "Visibility expression" ) );
1317+
visibilityExpressionWidget->setExpression( itemData.visibilityExpression()->expression() );
1318+
visibilityExpressionGroupBox->layout()->addWidget( visibilityExpressionWidget );
1319+
13061320
columnCount->setRange( 1, 5 );
13071321
columnCount->setValue( itemData.columnCount() );
13081322

13091323
layout->addRow( tr( "Title" ), title );
13101324
layout->addRow( tr( "Column count" ), columnCount );
1325+
layout->addWidget( visibilityExpressionGroupBox );
13111326

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

1349+
QgsOptionalExpression visibilityExpression;
1350+
visibilityExpression.setData( QgsExpression( visibilityExpressionWidget->expression() ) );
1351+
visibilityExpression.setEnabled( visibilityExpressionGroupBox->isChecked() );
1352+
itemData.setVisibilityExpression( visibilityExpression );
1353+
13341354
item->setData( 0, QgsFieldsProperties::DesignerTreeRole, itemData.asQVariant() );
13351355
item->setText( 0, title->text() );
13361356
}
@@ -1401,3 +1421,13 @@ void QgsFieldsProperties::DesignerTreeItemData::setShowLabel( bool showLabel )
14011421
{
14021422
mShowLabel = showLabel;
14031423
}
1424+
1425+
QgsOptionalExpression QgsFieldsProperties::DesignerTreeItemData::visibilityExpression() const
1426+
{
1427+
return mVisibilityExpression;
1428+
}
1429+
1430+
void QgsFieldsProperties::DesignerTreeItemData::setVisibilityExpression( const QgsOptionalExpression& visibilityExpression )
1431+
{
1432+
mVisibilityExpression = visibilityExpression;
1433+
}

src/app/qgsfieldsproperties.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,16 @@ class APP_EXPORT QgsFieldsProperties : public QWidget, private Ui_QgsFieldsPrope
8484
bool showLabel() const;
8585
void setShowLabel( bool showLabel );
8686

87+
QgsOptionalExpression visibilityExpression() const;
88+
void setVisibilityExpression( const QgsOptionalExpression& visibilityExpression );
89+
8790
private:
8891
Type mType;
8992
QString mName;
9093
int mColumnCount;
9194
bool mShowAsGroupBox;
9295
bool mShowLabel;
96+
QgsOptionalExpression mVisibilityExpression;
9397
};
9498

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

272276
public:
273-
explicit DesignerTree( QWidget* parent = nullptr );
277+
explicit DesignerTree( QgsVectorLayer* layer, QWidget* parent = nullptr );
274278
QTreeWidgetItem* addItem( QTreeWidgetItem* parent, QgsFieldsProperties::DesignerTreeItemData data );
275279
QTreeWidgetItem* addContainer( QTreeWidgetItem* parent, const QString& title , int columnCount );
276280

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

288292
private slots:
289293
void onItemDoubleClicked( QTreeWidgetItem* item, int column );
294+
295+
private:
296+
QgsVectorLayer* mLayer;
290297
};
291298

292299
Q_DECLARE_METATYPE( QgsFieldsProperties::FieldConfig )

src/core/qgseditformconfig.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,15 @@ QgsAttributeEditorElement* QgsEditFormConfig::attributeEditorElementFromDomEleme
427427
else
428428
container->setIsGroupBox( qobject_cast<QgsAttributeEditorContainer*>( parent ) );
429429

430+
bool visibilityExpressionEnabled = elem.attribute( "visibilityExpressionEnabled" ).toInt( &ok );
431+
QgsOptionalExpression visibilityExpression;
432+
if ( ok )
433+
{
434+
visibilityExpression.setEnabled( visibilityExpressionEnabled );
435+
visibilityExpression.setData( QgsExpression( elem.attribute( "visibilityExpression" ) ) );
436+
}
437+
container->setVisibilityExpression( visibilityExpression );
438+
430439
QDomNodeList childNodeList = elem.childNodes();
431440

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

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

520+
QgsOptionalExpression QgsAttributeEditorContainer::visibilityExpression() const
521+
{
522+
return mVisibilityExpression;
523+
}
524+
525+
void QgsAttributeEditorContainer::setVisibilityExpression( const QgsOptionalExpression& visibilityExpression )
526+
{
527+
mVisibilityExpression = visibilityExpression;
528+
}
529+
509530
QString QgsAttributeEditorContainer::typeIdentifier() const
510531
{
511532
return "attributeEditorContainer";

src/core/qgseditformconfig.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include "qgseditorwidgetconfig.h"
2424
#include "qgsrelationmanager.h"
25+
#include "qgsoptionalexpression.h"
2526

2627
/** \ingroup core
2728
* 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
198199
*/
199200
void setColumnCount( int columnCount );
200201

202+
/**
203+
* An expression that controls the visibility of this container.
204+
*
205+
* @note Added in QGIS 2.18
206+
*/
207+
QgsOptionalExpression visibilityExpression() const;
208+
209+
/**
210+
* An expression that controls the visibility of this container.
211+
*
212+
* @note Added in QGIS 2.18
213+
*/
214+
void setVisibilityExpression( const QgsOptionalExpression& visibilityExpression );
215+
201216
private:
202217
virtual void saveConfiguration( QDomElement& elem ) const override;
203218
virtual QString typeIdentifier() const override;
204219

205220
bool mIsGroupBox;
206221
QList<QgsAttributeEditorElement*> mChildren;
207222
int mColumnCount;
223+
QgsOptionalExpression mVisibilityExpression;
208224
};
209225

210226
/** \ingroup core

src/core/qgsexpression.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3571,6 +3571,14 @@ bool QgsExpression::isValid( const QString &text, const QgsExpressionContext *co
35713571
return !exp.hasParserError();
35723572
}
35733573

3574+
void QgsExpression::setExpression( const QString& expression )
3575+
{
3576+
detach();
3577+
d->mRootNode = ::parseExpression( expression, d->mParserErrorString );
3578+
d->mEvalErrorString = QString();
3579+
d->mExp = expression;
3580+
}
3581+
35743582
void QgsExpression::setScale( double scale ) { d->mScale = scale; }
35753583

35763584
double QgsExpression::scale() { return d->mScale; }

src/gui/qgsattributeform.cpp

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "qgsattributeformeditorwidget.h"
2727
#include "qgsmessagebar.h"
2828
#include "qgsmessagebaritem.h"
29+
#include "qgstabwidget.h"
2930

3031
#include <QDir>
3132
#include <QTextStream>
@@ -38,7 +39,6 @@
3839
#include <QLabel>
3940
#include <QPushButton>
4041
#include <QScrollArea>
41-
#include <QTabWidget>
4242
#include <QUiLoader>
4343
#include <QMessageBox>
4444
#include <QSettings>
@@ -744,6 +744,14 @@ void QgsAttributeForm::updateConstraints( QgsEditorWidgetWrapper *eww )
744744

745745
// sync ok button status
746746
synchronizeEnabledState();
747+
748+
mExpressionContext.setFeature( ft );
749+
750+
// Recheck visibility for all containers which are controlled by this value
751+
Q_FOREACH ( ContainerInformation* info, mContainerInformationDependency.value( eww->field().name() ) )
752+
{
753+
info->apply( &mExpressionContext );
754+
}
747755
}
748756
}
749757

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

818+
void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation* info )
819+
{
820+
mContainerVisibilityInformation.append( info );
821+
Q_FOREACH ( const QString& col, info->expression.referencedColumns() )
822+
{
823+
mContainerInformationDependency[ col ].append( info );
824+
}
825+
}
826+
810827
bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields,
811828
QStringList &descriptions )
812829
{
@@ -1090,7 +1107,7 @@ void QgsAttributeForm::init()
10901107
}
10911108
}
10921109

1093-
QTabWidget* tabWidget = nullptr;
1110+
QgsTabWidget* tabWidget = nullptr;
10941111

10951112
// Tab layout
10961113
if ( !formWidget && mLayer->editFormConfig()->layout() == QgsEditFormConfig::TabLayout )
@@ -1112,20 +1129,26 @@ void QgsAttributeForm::init()
11121129
tabWidget = nullptr;
11131130
WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
11141131
layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1132+
registerContainerInformation( new ContainerInformation( widgetInfo.widget, containerDef->visibilityExpression().data() ) );
11151133
column += 2;
11161134
}
11171135
else
11181136
{
11191137
if ( !tabWidget )
11201138
{
1121-
tabWidget = new QTabWidget();
1139+
tabWidget = new QgsTabWidget();
11221140
layout->addWidget( tabWidget, row, column, 1, 2 );
11231141
column += 2;
11241142
}
11251143

11261144
QWidget* tabPage = new QWidget( tabWidget );
11271145

11281146
tabWidget->addTab( tabPage, widgDef->name() );
1147+
1148+
if ( containerDef->visibilityExpression().enabled() )
1149+
{
1150+
registerContainerInformation( new ContainerInformation( tabWidget, tabPage, containerDef->visibilityExpression().data() ) );
1151+
}
11291152
QGridLayout* tabPageLayout = new QGridLayout();
11301153
tabPage->setLayout( tabPageLayout );
11311154

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

1618+
if ( childDef->type() == QgsAttributeEditorElement::AeTypeContainer )
1619+
{
1620+
QgsAttributeEditorContainer* containerDef = static_cast<QgsAttributeEditorContainer*>( childDef );
1621+
registerContainerInformation( new ContainerInformation( widgetInfo.widget, containerDef->visibilityExpression().data() ) );
1622+
}
1623+
15951624
if ( widgetInfo.labelText.isNull() )
15961625
{
15971626
gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
@@ -1873,3 +1902,22 @@ int QgsAttributeForm::messageTimeout()
18731902
QSettings settings;
18741903
return settings.value( "/qgis/messageTimeout", 5 ).toInt();
18751904
}
1905+
1906+
void QgsAttributeForm::ContainerInformation::apply( QgsExpressionContext* expressionContext )
1907+
{
1908+
bool newVisibility = expression.evaluate( expressionContext ).toBool();
1909+
1910+
if ( newVisibility != isVisible )
1911+
{
1912+
if ( tabWidget )
1913+
{
1914+
tabWidget->setTabVisible( widget, newVisibility );
1915+
}
1916+
else
1917+
{
1918+
widget->setVisible( newVisibility );
1919+
}
1920+
1921+
isVisible = newVisibility;
1922+
}
1923+
}

0 commit comments

Comments
 (0)