Create something like the Widget Box as in the Qt Designer

Widget Box

Widget Box

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

Custom Widget Box

Custom Widget Box

If everything went well you should get something like this. You can of course use any other widgets, not just buttons.

12 thoughts on “Create something like the Widget Box as in the Qt Designer

  1. Aleksey

    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!

    Reply
  2. Aleksey

    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.

    Reply
  3. CodingCat

    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_())

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.