Skip to content

Commit 1882cab

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 ac41436 commit 1882cab

9 files changed

+186
-8
lines changed

src/app/qgsfieldsproperties.cpp

+33-3
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>
@@ -72,7 +73,7 @@ QgsFieldsProperties::QgsFieldsProperties( QgsVectorLayer *layer, QWidget* parent
7273
// tab and group display
7374
mAddItemButton->setEnabled( false );
7475

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

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

157-
QTreeWidgetItem *QgsFieldsProperties::loadAttributeEditorTreeItem( QgsAttributeEditorElement* const widgetDef, QTreeWidgetItem* parent )
158+
QTreeWidgetItem* QgsFieldsProperties::loadAttributeEditorTreeItem( QgsAttributeEditorElement* const widgetDef, QTreeWidgetItem* parent )
158159
{
159160
QTreeWidgetItem* newWidget = nullptr;
160161
switch ( widgetDef->type() )
@@ -187,6 +188,7 @@ QTreeWidgetItem *QgsFieldsProperties::loadAttributeEditorTreeItem( QgsAttributeE
187188

188189
itemData.setColumnCount( container->columnCount() );
189190
itemData.setShowAsGroupBox( container->isGroupBox() );
191+
itemData.setVisibilityExpression( container->visibilityExpression() );
190192
newWidget = mDesignerTree->addItem( parent, itemData );
191193

192194
Q_FOREACH ( QgsAttributeEditorElement* wdg, container->children() )
@@ -865,6 +867,7 @@ QgsAttributeEditorElement* QgsFieldsProperties::createAttributeEditorWidget( QTr
865867
QgsAttributeEditorContainer* container = new QgsAttributeEditorContainer( item->text( 0 ), parent );
866868
container->setColumnCount( itemData.columnCount() );
867869
container->setIsGroupBox( forceGroup ? true : itemData.showAsGroupBox() );
870+
container->setVisibilityExpression( itemData.visibilityExpression() );
868871

869872
for ( int t = 0; t < item->childCount(); t++ )
870873
{
@@ -1092,8 +1095,9 @@ QTreeWidgetItem* DesignerTree::addContainer( QTreeWidgetItem* parent, const QStr
10921095
return newItem;
10931096
}
10941097

1095-
DesignerTree::DesignerTree( QWidget* parent )
1098+
DesignerTree::DesignerTree( QgsVectorLayer* layer, QWidget* parent )
10961099
: QTreeWidget( parent )
1100+
, mLayer( layer )
10971101
{
10981102
connect( this, SIGNAL( itemDoubleClicked( QTreeWidgetItem*, int ) ), this, SLOT( onItemDoubleClicked( QTreeWidgetItem*, int ) ) );
10991103
}
@@ -1271,11 +1275,22 @@ void DesignerTree::onItemDoubleClicked( QTreeWidgetItem* item, int column )
12711275
QCheckBox* showAsGroupBox = nullptr;
12721276
QLineEdit* title = new QLineEdit( itemData.name() );
12731277
QSpinBox* columnCount = new QSpinBox();
1278+
QGroupBox* visibilityExpressionGroupBox = new QGroupBox( tr( "Control visibility by expression " ) );
1279+
visibilityExpressionGroupBox->setCheckable( true );
1280+
visibilityExpressionGroupBox->setChecked( itemData.visibilityExpression().enabled() );
1281+
visibilityExpressionGroupBox->setLayout( new QGridLayout );
1282+
QgsFieldExpressionWidget* visibilityExpressionWidget = new QgsFieldExpressionWidget;
1283+
visibilityExpressionWidget->setLayer( mLayer );
1284+
visibilityExpressionWidget->setExpressionDialogTitle( tr( "Visibility expression" ) );
1285+
visibilityExpressionWidget->setExpression( itemData.visibilityExpression()->expression() );
1286+
visibilityExpressionGroupBox->layout()->addWidget( visibilityExpressionWidget );
1287+
12741288
columnCount->setRange( 1, 5 );
12751289
columnCount->setValue( itemData.columnCount() );
12761290

12771291
layout->addRow( tr( "Title" ), title );
12781292
layout->addRow( tr( "Column count" ), columnCount );
1293+
layout->addWidget( visibilityExpressionGroupBox );
12791294

12801295
if ( !item->parent() )
12811296
{
@@ -1299,6 +1314,11 @@ void DesignerTree::onItemDoubleClicked( QTreeWidgetItem* item, int column )
12991314
itemData.setName( title->text() );
13001315
itemData.setShowLabel( showLabelCheckbox->isChecked() );
13011316

1317+
QgsOptionalExpression visibilityExpression;
1318+
visibilityExpression.setData( QgsExpression( visibilityExpressionWidget->expression() ) );
1319+
visibilityExpression.setEnabled( visibilityExpressionGroupBox->isChecked() );
1320+
itemData.setVisibilityExpression( visibilityExpression );
1321+
13021322
item->setData( 0, QgsFieldsProperties::DesignerTreeRole, itemData.asQVariant() );
13031323
item->setText( 0, title->text() );
13041324
}
@@ -1369,3 +1389,13 @@ void QgsFieldsProperties::DesignerTreeItemData::setShowLabel( bool showLabel )
13691389
{
13701390
mShowLabel = showLabel;
13711391
}
1392+
1393+
QgsOptionalExpression QgsFieldsProperties::DesignerTreeItemData::visibilityExpression() const
1394+
{
1395+
return mVisibilityExpression;
1396+
}
1397+
1398+
void QgsFieldsProperties::DesignerTreeItemData::setVisibilityExpression( const QgsOptionalExpression& visibilityExpression )
1399+
{
1400+
mVisibilityExpression = visibilityExpression;
1401+
}

src/app/qgsfieldsproperties.h

+8-1
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
/**
@@ -265,7 +269,7 @@ class DesignerTree : public QTreeWidget
265269
Q_OBJECT
266270

267271
public:
268-
explicit DesignerTree( QWidget* parent = nullptr );
272+
explicit DesignerTree( QgsVectorLayer* layer, QWidget* parent = nullptr );
269273
QTreeWidgetItem* addItem( QTreeWidgetItem* parent, QgsFieldsProperties::DesignerTreeItemData data );
270274
QTreeWidgetItem* addContainer( QTreeWidgetItem* parent, const QString& title , int columnCount );
271275

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

283287
private slots:
284288
void onItemDoubleClicked( QTreeWidgetItem* item, int column );
289+
290+
private:
291+
QgsVectorLayer* mLayer;
285292
};
286293

287294
Q_DECLARE_METATYPE( QgsFieldsProperties::FieldConfig )

src/core/qgsattributeeditorelement.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@ void QgsAttributeEditorContainer::setName( const QString& name )
2626
mName = name;
2727
}
2828

29+
QgsOptionalExpression QgsAttributeEditorContainer::visibilityExpression() const
30+
{
31+
return mVisibilityExpression;
32+
}
33+
34+
void QgsAttributeEditorContainer::setVisibilityExpression( const QgsOptionalExpression& visibilityExpression )
35+
{
36+
if ( visibilityExpression == mVisibilityExpression )
37+
return;
38+
39+
mVisibilityExpression = visibilityExpression;
40+
}
41+
2942
QList<QgsAttributeEditorElement*> QgsAttributeEditorContainer::findElements( QgsAttributeEditorElement::AttributeEditorType type ) const
3043
{
3144
QList<QgsAttributeEditorElement*> results;

src/core/qgsattributeeditorelement.h

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#define QGSATTRIBUTEEDITORELEMENT_H
1818

1919
#include "qgsrelation.h"
20+
#include "qgsoptionalexpression.h"
2021

2122
class QgsRelationManager;
2223

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

223+
QgsOptionalExpression visibilityExpression() const;
224+
void setVisibilityExpression( const QgsOptionalExpression& visibilityExpression );
225+
222226
private:
223227
void saveConfiguration( QDomElement& elem ) const override;
224228
QString typeIdentifier() const override;
225229

226230
bool mIsGroupBox;
227231
QList<QgsAttributeEditorElement*> mChildren;
228232
int mColumnCount;
233+
QgsOptionalExpression mVisibilityExpression;
229234
};
230235

231236
/** \ingroup core

src/core/qgseditformconfig.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "qgseditformconfig.h"
1717
#include "qgsproject.h"
1818
#include "qgsrelationmanager.h"
19+
1920
//#include "qgseditorwidgetregistry.h"
2021

2122
QgsAttributeEditorContainer::~QgsAttributeEditorContainer()
@@ -573,6 +574,15 @@ QgsAttributeEditorElement* QgsEditFormConfig::attributeEditorElementFromDomEleme
573574
else
574575
container->setIsGroupBox( parent );
575576

577+
bool visibilityExpressionEnabled = elem.attribute( "visibilityExpressionEnabled" ).toInt( &ok );
578+
QgsOptionalExpression visibilityExpression;
579+
if ( ok )
580+
{
581+
visibilityExpression.setEnabled( visibilityExpressionEnabled );
582+
visibilityExpression.setData( QgsExpression( elem.attribute( "visibilityExpression" ) ) );
583+
}
584+
container->setVisibilityExpression( visibilityExpression );
585+
576586
QDomNodeList childNodeList = elem.childNodes();
577587

578588
for ( int i = 0; i < childNodeList.size(); i++ )
@@ -627,6 +637,7 @@ QgsAttributeEditorElement* QgsAttributeEditorContainer::clone( QgsAttributeEdito
627637
}
628638
element->mIsGroupBox = mIsGroupBox;
629639
element->mColumnCount = mColumnCount;
640+
element->mVisibilityExpression = mVisibilityExpression;
630641

631642
return element;
632643
}
@@ -635,6 +646,8 @@ void QgsAttributeEditorContainer::saveConfiguration( QDomElement& elem ) const
635646
{
636647
elem.setAttribute( "columnCount", mColumnCount );
637648
elem.setAttribute( "groupBox", mIsGroupBox ? 1 : 0 );
649+
elem.setAttribute( "visibilityExpressionEnabled", mVisibilityExpression.enabled() ? 1 : 0 );
650+
elem.setAttribute( "visibilityExpression", mVisibilityExpression->expression() );
638651

639652
Q_FOREACH ( QgsAttributeEditorElement* child, mChildren )
640653
{

src/gui/qgsattributeform.cpp

+51-3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "qgseditorwidgetwrapper.h"
3131
#include "qgsrelationmanager.h"
3232
#include "qgslogger.h"
33+
#include "qgstabwidget.h"
3334

3435
#include <QDir>
3536
#include <QTextStream>
@@ -42,7 +43,6 @@
4243
#include <QLabel>
4344
#include <QPushButton>
4445
#include <QScrollArea>
45-
#include <QTabWidget>
4646
#include <QUiLoader>
4747
#include <QMessageBox>
4848
#include <QSettings>
@@ -747,6 +747,14 @@ void QgsAttributeForm::updateConstraints( QgsEditorWidgetWrapper *eww )
747747

748748
// sync ok button status
749749
synchronizeEnabledState();
750+
751+
mExpressionContext.setFeature( ft );
752+
753+
// Recheck visibility for all containers which are controlled by this value
754+
Q_FOREACH ( ContainerInformation* info, mContainerInformationDependency.value( eww->field().name() ) )
755+
{
756+
info->apply( &mExpressionContext );
757+
}
750758
}
751759
}
752760

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

821+
void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation* info )
822+
{
823+
mContainerVisibilityInformation.append( info );
824+
Q_FOREACH ( const QString& col, info->expression.referencedColumns() )
825+
{
826+
mContainerInformationDependency[ col ].append( info );
827+
}
828+
}
829+
813830
bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields,
814831
QStringList &descriptions )
815832
{
@@ -1095,7 +1112,7 @@ void QgsAttributeForm::init()
10951112
}
10961113
}
10971114

1098-
QTabWidget* tabWidget = nullptr;
1115+
QgsTabWidget* tabWidget = nullptr;
10991116

11001117
// Tab layout
11011118
if ( !formWidget && mLayer->editFormConfig().layout() == QgsEditFormConfig::TabLayout )
@@ -1117,20 +1134,26 @@ void QgsAttributeForm::init()
11171134
tabWidget = nullptr;
11181135
WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
11191136
layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1137+
registerContainerInformation( new ContainerInformation( widgetInfo.widget, containerDef->visibilityExpression().data() ) );
11201138
column += 2;
11211139
}
11221140
else
11231141
{
11241142
if ( !tabWidget )
11251143
{
1126-
tabWidget = new QTabWidget();
1144+
tabWidget = new QgsTabWidget();
11271145
layout->addWidget( tabWidget, row, column, 1, 2 );
11281146
column += 2;
11291147
}
11301148

11311149
QWidget* tabPage = new QWidget( tabWidget );
11321150

11331151
tabWidget->addTab( tabPage, widgDef->name() );
1152+
1153+
if ( containerDef->visibilityExpression().enabled() )
1154+
{
1155+
registerContainerInformation( new ContainerInformation( tabWidget, tabPage, containerDef->visibilityExpression().data() ) );
1156+
}
11341157
QGridLayout* tabPageLayout = new QGridLayout();
11351158
tabPage->setLayout( tabPageLayout );
11361159

@@ -1595,6 +1618,12 @@ QgsAttributeForm::WidgetInfo QgsAttributeForm::createWidgetFromDef( const QgsAtt
15951618
{
15961619
WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
15971620

1621+
if ( childDef->type() == QgsAttributeEditorElement::AeTypeContainer )
1622+
{
1623+
QgsAttributeEditorContainer* containerDef = static_cast<QgsAttributeEditorContainer*>( childDef );
1624+
registerContainerInformation( new ContainerInformation( widgetInfo.widget, containerDef->visibilityExpression().data() ) );
1625+
}
1626+
15981627
if ( widgetInfo.labelText.isNull() )
15991628
{
16001629
gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
@@ -1875,3 +1904,22 @@ int QgsAttributeForm::messageTimeout()
18751904
QSettings settings;
18761905
return settings.value( "/qgis/messageTimeout", 5 ).toInt();
18771906
}
1907+
1908+
void QgsAttributeForm::ContainerInformation::apply( QgsExpressionContext* expressionContext )
1909+
{
1910+
bool newVisibility = expression.evaluate( expressionContext ).toBool();
1911+
1912+
if ( newVisibility != isVisible )
1913+
{
1914+
if ( tabWidget )
1915+
{
1916+
tabWidget->setTabVisible( widget, newVisibility );
1917+
}
1918+
else
1919+
{
1920+
widget->setVisible( newVisibility );
1921+
}
1922+
1923+
isVisible = newVisibility;
1924+
}
1925+
}

src/gui/qgsattributeform.h

+32
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class QgsAttributeFormEditorWidget;
2828
class QgsMessageBar;
2929
class QgsMessageBarItem;
3030
class QgsWidgetWrapper;
31+
class QgsTabWidget;
3132

3233
/** \ingroup gui
3334
* \class QgsAttributeForm
@@ -343,6 +344,37 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
343344
QWidget* mSearchButtonBox;
344345
QList<QgsAttributeFormInterface*> mInterfaces;
345346
QMap< int, QgsAttributeFormEditorWidget* > mFormEditorWidgets;
347+
QgsExpressionContext mExpressionContext;
348+
349+
struct ContainerInformation
350+
{
351+
ContainerInformation( QgsTabWidget* tabWidget, QWidget* widget, QgsExpression expression )
352+
: tabWidget( tabWidget )
353+
, widget( widget )
354+
, expression( expression )
355+
, isVisible( true )
356+
{}
357+
358+
ContainerInformation( QWidget* widget, QgsExpression expression )
359+
: tabWidget( nullptr )
360+
, widget( widget )
361+
, expression( expression )
362+
, isVisible( true )
363+
{}
364+
365+
QgsTabWidget* tabWidget;
366+
QWidget* widget;
367+
QgsExpression expression;
368+
bool isVisible;
369+
370+
void apply( QgsExpressionContext* expressionContext );
371+
};
372+
373+
void registerContainerInformation( ContainerInformation* info );
374+
375+
// Contains information about tabs and groupboxes, their visibility state visibility conditions
376+
QVector<ContainerInformation*> mContainerVisibilityInformation;
377+
QMap<QString, QVector<ContainerInformation*> > mContainerInformationDependency;
346378

347379
// Variables below are used for python
348380
static int sFormCounter;

0 commit comments

Comments
 (0)