Widget Box
There is something called the Widget Box in the Qt Designer. It contains a list of widgets separated by categories. Each category button can be clicked in order to expand and collapse the list below the button.
You might want something like the Widget Box for one reason or another, to display your own collapsible list of items.
Tree Widget
In my approach in recreating the Widget Box I used a Tree Widget because it already has the basic idea of expanding and collapsing items in the list to display and hide its child widgets. The gist of turning the Tree Widget into a Widget Box is adding buttons as top level items and adding a frame with a layout as the child of those top level items. The button should then expand and collapse its own Tree Widget Item when clicked to display and hide the contents of a category. The button will require a custom class inheriting the QPushButton class in order to expand and collapse the Tree Widget Items.
You might want to disable root decoration and set indentation to 0 on the Tree Widget to turn it into a flat list. The custom button type is named QtCategoryButton in this snippet.
MyApplication::MyApplication(QWidget *parent, Qt::WFlags flags) : QMainWindow(parent, flags) { ui.setupUi(this); ui.treeWidget->setRootIsDecorated(false); ui.treeWidget->setIndentation(0); // First category { QTreeWidgetItem* pCategory = new QTreeWidgetItem(); ui.treeWidget->addTopLevelItem(pCategory); ui.treeWidget->setItemWidget(pCategory, 0, new QtCategoryButton("Category 1", ui.treeWidget, pCategory)); QFrame* pFrame = new QFrame(ui.treeWidget); QBoxLayout* pLayout = new QVBoxLayout(pFrame); pLayout->addWidget(new QPushButton("Btn1")); pLayout->addWidget(new QPushButton("Btn2")); QTreeWidgetItem* pContainer = new QTreeWidgetItem(); pContainer->setDisabled(true); pCategory->addChild(pContainer); ui.treeWidget->setItemWidget(pContainer, 0, pFrame); } // Second category { QTreeWidgetItem* pCategory = new QTreeWidgetItem(); ui.treeWidget->addTopLevelItem(pCategory); ui.treeWidget->setItemWidget(pCategory, 0, new QtCategoryButton("Category 2", ui.treeWidget, pCategory)); QFrame* pFrame = new QFrame(ui.treeWidget); QBoxLayout* pLayout = new QHBoxLayout(pFrame); pLayout->addWidget(new QPushButton("Btn1")); pLayout->addWidget(new QPushButton("Btn2")); QTreeWidgetItem* pContainer = new QTreeWidgetItem(); pContainer->setDisabled(true); pCategory->addChild(pContainer); ui.treeWidget->setItemWidget(pContainer, 0, pFrame); } }
Custom button
The custom button is a quite simple class, all it does it catch the pressed() signal and expands or collapses the Tree Widget Item it is bound to.
class QtCategoryButton : public QPushButton { Q_OBJECT public: QtCategoryButton(const QString& a_Text, QTreeWidget* a_pParent, QTreeWidgetItem* a_pItem); private slots: void ButtonPressed(); private: QTreeWidgetItem* m_pItem; };
QtCategoryButton::QtCategoryButton( const QString& a_Text, QTreeWidget* a_pParent, QTreeWidgetItem* a_pItem ) : QPushButton(a_Text, a_pParent) , m_pItem(a_pItem) { connect(this, SIGNAL(pressed()), this, SLOT(ButtonPressed())); } void QtCategoryButton::ButtonPressed() { m_pItem->setExpanded( !m_pItem->isExpanded() ); }
Finally
If everything went well you should get something like this. You can of course use any other widgets, not just buttons.
Thanks. Will adapt for pyqt.
Thanks. It’s very helpful to me.
Really good example! Thanks for sharing!
thanks a lot you saved my day
Wow! Thnx buddy!
Good example, thanks. However how to make it look like in Qt designer with same root decoration (triangle) in the left? Or replace root button with a line for example like this:
+ ——- Category 1 ————-
widgets
widgets
+ ——- Category 2 ————-
widgets
widgets
etc.
thanks!
Created sample widget and plugin for Qt Designer, put it on GitHub if somebody else need this and want to improve: https://github.com/akontsevich/WidgetBox. It is ready for using however some points good to have improved:
– Style category button (or panel) closer to Qt Creator (Designer) look or like to LibreOffice Writer Properties Tool Box on the right side
– Improve pages behaviour in Qt Designer similar to QToolBox: now Designer does not change currentPageIndex PROPERTY and focus on page or category click for some reason
– Improve pages geometry resizing to content and their layout on design time and on run-time: QTreeWidget automatically increases itemWidget size height (in runtime, not design time – it remains terrible there) if contents is bigger than widget (page) height, however if size (sizeHint) is bigger – it does not decrease size to necessary minimum.
Hope for community help on these improvements.
This is jut a test on page
That’s awesome !
It helps me a lot. Thanks.
Thanks for the great concept! I have reimplemented it for PyQt5:
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class SectionExpandButton(QPushButton):
“””a QPushbutton that can expand or collapse its section
“””
def __init__(self, item, text = “”, parent = None):
super().__init__(text, parent)
self.section = item
self.clicked.connect(self.on_clicked)
def on_clicked(self):
“””toggle expand/collapse of section by clicking
“””
if self.section.isExpanded():
self.section.setExpanded(False)
else:
self.section.setExpanded(True)
class CollapsibleDialog(QDialog):
“””a dialog to which collapsible sections can be added;
reimplement define_sections() to define sections and
add them as (title, widget) tuples to self.sections
“””
def __init__(self):
super().__init__()
self.tree = QTreeWidget()
self.tree.setHeaderHidden(True)
layout = QVBoxLayout()
layout.addWidget(self.tree)
self.setLayout(layout)
self.tree.setIndentation(0)
self.sections = []
self.define_sections()
self.add_sections()
def add_sections(self):
“””adds a collapsible sections for every
(title, widget) tuple in self.sections
“””
for (title, widget) in self.sections:
button1 = self.add_button(title)
section1 = self.add_widget(button1, widget)
button1.addChild(section1)
def define_sections(self):
“””reimplement this to define all your sections
and add them as (title, widget) tuples to self.sections
“””
widget = QFrame(self.tree)
layout = QHBoxLayout(widget)
layout.addWidget(QLabel(“Bla”))
layout.addWidget(QLabel(“Blubb”))
title = “Section 1”
self.sections.append((title, widget))
def add_button(self, title):
“””creates a QTreeWidgetItem containing a button
to expand or collapse its section
“””
item = QTreeWidgetItem()
self.tree.addTopLevelItem(item)
self.tree.setItemWidget(item, 0, SectionExpandButton(item, text = title))
return item
def add_widget(self, button, widget):
“””creates a QWidgetItem containing the widget,
as child of the button-QWidgetItem
“””
section = QTreeWidgetItem(button)
section.setDisabled(True)
self.tree.setItemWidget(section, 0, widget)
return section
if __name__ == “__main__”:
app = QApplication(sys.argv)
window = CollapsibleDialog()
window.show()
sys.exit(app.exec_())
Oh, that messed up the indentation. Sorry. I have put the code on StackOverflow to copy and use:
https://stackoverflow.com/a/49941230/1413513
Great post. I am going through some of these issues as well..