11. Containers

Qt container widgets are visual elements that group other widgets together to form structured interfaces - they organize layout, provide borders, and create logical sections of the UI.

11.1 QGroupBox

QGroupBox is a widget that has a frame and a title on top and allows you to display other widgets inside itself but is commonly used to group checkboxes or radiobuttons. QGroupBox does not lay out its child widgets automatically - you need to do it yourself using one of the Qt layout classes. You can set a QGroupBox to be checkable, which lets the user enable or disable all its child widgets simultaneously.

An icon of a clipboard-list1

Your task is to create a group of mutually exclusive options. You also need to be able to enable or disable the whole group.

 1 # A group box provides a frame, a title on top
 2 # and displays various other widgets inside itself.
 3 
 4 import sys
 5 
 6 from PySide6.QtWidgets import (QApplication,
 7     QWidget, QHBoxLayout, QVBoxLayout,
 8     QGroupBox, QRadioButton, QLabel)
 9 
10 
11 class Window(QWidget):
12     
13     def __init__(self):
14         
15         super().__init__()
16 
17         layout = QHBoxLayout()
18 
19         self.label = QLabel()
20         self.label.setFixedWidth(80)
21         
22         # 1 - Create the group box
23         #     and add a layout to it. You can't 
24         #     add widgets directly to the group box.
25         
26         self.groupbox = QGroupBox()
27         self.groupbox.setTitle('Group box')
28 
29         groupbox_layout = QVBoxLayout()
30         self.groupbox.setLayout(groupbox_layout)
31 
32         # 2 - Add widgets to the layout.
33 
34         self.radiobutton_1 = QRadioButton('Option 1')
35         self.radiobutton_2 = QRadioButton('Set checkable')
36         self.radiobutton_3 = QRadioButton('Set non-checkable')
37 
38         groupbox_layout.addWidget(self.radiobutton_1)
39         groupbox_layout.addWidget(self.radiobutton_2)
40         groupbox_layout.addWidget(self.radiobutton_3)
41         
42         # 4- Connect child widget signals with the slot
43         
44         self.radiobutton_1.toggled.connect(self.on_toggled)
45         self.radiobutton_2.toggled.connect(self.on_toggled)
46         self.radiobutton_3.toggled.connect(self.on_toggled)
47         
48         self.radiobutton_1.setChecked(True)
49 
50         layout.addWidget(self.groupbox)
51         layout.addWidget(self.label)
52         
53         self.setLayout(layout)
54 
55     # 3 - Add slot method. If there are 
56     #     multiple radio buttons in a group box
57     #     only one can be checked, unlike checkboxes.
58     
59     def on_toggled(self):
60 
61         if self.radiobutton_1.isChecked():
62             self.label.setText(self.radiobutton_1.text())
63         elif self.radiobutton_2.isChecked():
64             self.label.setText(self.radiobutton_2.text())
65             self.groupbox.setCheckable(True)
66         else:
67             self.label.setText(self.radiobutton_3.text())
68             self.groupbox.setCheckable(False)
69 
70 
71 if __name__ == '__main__':
72 
73     app = QApplication(sys.argv)
74 
75     main_window = Window()
76     main_window.show()
77 
78     sys.exit(app.exec())
  1. Create a QGroupBox object and add a layout to it as you can’t add widgets directly. In the example, we use a QVBoxLayout.

  2. Create widgets and add them to the layout. In the example, we add three QRadioButtons. We also set one of the radio buttons to checked.

  3. Create the slot method to handle QRadioButton.toggled() signals. The slot sets a label’s text to the text of the checked radiobutton and the last two radiobuttons also toggle the group box togglable property.

  4. Connect QRadioButton.toggled signals with the slot.

11.2 QScrollArea

If a QScrollArea’ child widget exceeds its size it provides scrollbars so that the whole child widget can be viewed.

An icon of a clipboard-list1

You need to create a simple text editor in a constrained space.

 1 # The QScrollArea class provides a 
 2 # scrolling view onto another widget
 3 
 4 import sys
 5 
 6 from PySide6.QtGui import QTextOption
 7 from PySide6.QtWidgets import (QApplication,
 8     QWidget, QVBoxLayout, QScrollArea, QPlainTextEdit)
 9 
10 
11 class Window(QWidget):
12     
13     def __init__(self):
14 
15         super().__init__()
16         
17         layout = QVBoxLayout()
18         self.setLayout(layout)
19         
20         # 1 - Create the scroll area
21         
22         scroll_area = QScrollArea()
23         
24         # 2 - Create the widget that needs to be scrolled
25         
26         text_edit = QPlainTextEdit()
27         text_edit.setWordWrapMode(QTextOption.WrapMode.NoWrap)
28 
29         # 3 - Add the widget to the scroll area
30 
31         scroll_area.setWidget(text_edit)
32         
33         # Use this if you want the inner widget to 
34         # get resized together with the scroll area.
35         
36         scroll_area.setWidgetResizable(True)
37         
38         layout.addWidget(scroll_area)
39 
40 
41 if __name__ == '__main__':
42 
43     app = QApplication(sys.argv)
44 
45     main_window = Window()
46     main_window.show()
47 
48     sys.exit(app.exec())
  1. Create a QScrollArea object

  2. Create the child widget. In the example we use a QPlainTextEdit object and set its wrap mode to WrapMode.NoWrap so that it expands when text is entered

  3. Add the QPlainTextEdit to the scroll area using QScrollArea.addWidget(). We also set the QScrollArea.widgetResizable property to True so that the text box resizes along with the scroll area.

Now if you enter a long line of text in the text box the scroll area shows the horizontal scrollbar and if you enter several lines the scroll area shows the vertical scrollbar.

11.3 QToolBox

QToolBox provides a column of tabbed widget items. This doesn’t really tell you much - it is a Qt container widget pretty similar to the ubiquitous accordion widget that lets you pack multiple widgets within a relatively small space and expand or collapse them as needed. QToolBox pages are called items in the documentation.

An icon of a clipboard-list1

You need to add a collapsible set of options within a small space.

 1 # The QToolBox class provides a column of tabbed widget items
 2 
 3 import sys
 4 
 5 from PySide6.QtWidgets import (QApplication,
 6     QWidget, QVBoxLayout, QToolBox, QPushButton, QLabel)
 7 
 8 
 9 class Window(QWidget):
10     
11     def __init__(self):
12 
13         super().__init__()
14         
15         layout = QVBoxLayout()
16         self.setLayout(layout)
17         
18         # 1 - Create the toolbox
19         
20         toolbox = QToolBox()
21         
22         # 2 - Create the widgets (buttons in this case)
23         
24         windows_button = QPushButton('Windows')
25         mac_button = QPushButton('Mac')
26         linux_widget = QWidget()
27 
28         linux_widget.setLayout(QVBoxLayout())
29         debian_button = QPushButton('Debian')
30         arch_button = QPushButton('Arch')
31         linux_widget.layout().addWidget(debian_button)
32         linux_widget.layout().addWidget(arch_button)
33         
34         # 3 - Add widgets to the toolbox
35         
36         toolbox.addItem(windows_button, 'Win')
37         toolbox.addItem(mac_button, 'Mac')
38         toolbox.addItem(linux_widget, 'Lin')
39         
40         windows_button.clicked.connect(self.on_clicked)
41         mac_button.clicked.connect(self.on_clicked)
42         debian_button.clicked.connect(self.on_clicked)
43         arch_button.clicked.connect(self.on_clicked)
44         
45         self.label = QLabel()
46 
47         layout.addWidget(toolbox)
48         layout.addWidget(self.label)
49         
50     def on_clicked(self):
51         self.label.setText(self.sender().text() + ' clicked!')
52 
53 
54 if __name__ == '__main__':
55 
56     app = QApplication(sys.argv)
57 
58     main_window = Window()
59     main_window.show()
60 
61     sys.exit(app.exec())
  1. Create a QToolBox object

  2. Create the child widgets. In the example we simply create three push buttons each representing a popular operating system. You can also add multiple child widgets to a page by adding them to a layout as demonstrated on the `Linux’ page.

  3. Add the widgets to the toolbox using QToolBox.addItem()

In the example we also handle the child push buttons clicked signals for demonstration purposes, setting a label’s text to the text of the clicked button.

11.4 QTabWidget

QTabWidget is a tabbed container widget - when you click on a tab its associated page is shown.

An icon of a clipboard-list1

Your space is limited and you need to create options for your editor styles and margins.

 1 # The QTabWidget class provides a stack of tabbed widgets.  :S
 2 # ie. tabs
 3 
 4 import sys
 5 
 6 from PySide6.QtWidgets import (QApplication, QWidget,
 7     QVBoxLayout, QTabWidget, QRadioButton, QCheckBox)
 8 
 9 
10 class Window(QWidget):
11 
12     def __init__(self):
13 
14         super().__init__()
15 
16         layout = QVBoxLayout()
17         self.setLayout(layout)
18 
19         # 1 - Create the tab widget
20 
21         self.tab_widget = QTabWidget()
22 
23         # 2 - Create some widgets
24         #     A tab contains only one widget
25         #     but that widget can be a QWidget instance
26         #     or some other container.
27 
28         # Tab 0 widgets:
29 
30         styles = QWidget()
31         styles_layout = QVBoxLayout()
32         styles_layout.addWidget(QCheckBox('Heading'))
33         styles_layout.addWidget(QCheckBox('Paragraph'))
34         styles_layout.addWidget(QCheckBox('List'))
35         styles.setLayout(styles_layout)
36 
37         # Tab 1 widgets
38 
39         margins = QWidget()
40         margins_layout = QVBoxLayout()
41         margins_layout.addWidget(QRadioButton('Normal'))
42         margins_layout.addWidget(QRadioButton('Wide'))
43         margins_layout.addWidget(QRadioButton('Narrow'))
44         margins.setLayout(margins_layout)
45 
46         # 3 - Add tabs to the widget
47 
48         self.tab_widget.addTab(styles, 'Styles')
49         self.tab_widget.addTab(margins, 'Margins')
50 
51         layout.addWidget(self.tab_widget)
52 
53 
54 if __name__ == '__main__':
55 
56     app = QApplication(sys.argv)
57 
58     main_window = Window()
59     main_window.show()
60 
61     sys.exit(app.exec())
  1. Create a QTabWidget object.

  2. Create its intended child widgets. Each page contains a single widget but that widget, in turn, can be a QWidget or a container class which allows you to pack multiple children into a QTabWidget page.

  3. Use QTabWidget.addTab() to add the child widgets to the tab widget object. In the example we have two QWidgets and add each to the tab widget. The first QWidget has three check boxes as its children and the second QWidgets children are three radio buttons.

The tab indexes start with zero so the first tab has the index of 0 and the second has the index of 1. Each tab has a label (Styles and Margins in the example). You can change the tab position (North, South, West and East) and shape (Rounded and Triangular).

11.5 QSplitter

The QSplitter class lets the user resize its child widgets using the mouse.

An icon of a clipboard-list1

Your task is to create an application with three resizable panes and you want the user to be able to toggle their orientation.

 1 # The QSplitter class implements a splitter widget.
 2 # A splitter lets the user control the size of 
 3 # child widgets by dragging the boundary between them. 
 4 # Any number of widgets may be controlled by a single splitter. 
 5 
 6 import sys
 7 
 8 from PySide6.QtCore import Qt
 9 from PySide6.QtWidgets import (QApplication, QWidget,
10     QVBoxLayout, QSplitter, QGroupBox, QRadioButton)
11 
12 
13 class Window(QWidget):
14     
15     def __init__(self):
16 
17         super().__init__()
18         
19         layout = QVBoxLayout()
20         self.setLayout(layout)
21         
22         # 1 - Create a splitter
23         
24         self.splitter = QSplitter()
25         
26         # 2 - Create widgets
27         #     In this case it's three groupboxes
28         
29         groupbox_1 = QGroupBox('Orientation')
30         groupbox_1.setLayout(QVBoxLayout())
31         
32         # The radio buttons are just to demonstrate
33         # splitter orientation. I don't think changing
34         # splitter orientation at run time is that common.
35         
36         self.button_horizontal = QRadioButton('Horizontal')
37         self.button_horizontal.setChecked(True)
38         self.button_vertical = QRadioButton('Vertical')
39         
40         self.button_horizontal.toggled.connect(self.on_toggled)
41         self.button_vertical.toggled.connect(self.on_toggled)
42         
43         groupbox_1.layout().addWidget(self.button_horizontal)
44         groupbox_1.layout().addWidget(self.button_vertical)
45         
46         groupbox_2 = QGroupBox('group box 2')
47         groupbox_3 = QGroupBox('group box 3')
48         
49         # 3 - Add widgets to the splitter
50         
51         self.splitter.addWidget(groupbox_1)
52         self.splitter.addWidget(groupbox_2)
53         self.splitter.addWidget(groupbox_3)
54         
55         layout.addWidget(self.splitter)
56         
57     def on_toggled(self):
58         
59         if self.button_horizontal.isChecked():
60             self.splitter.setOrientation(
61                 Qt.Orientation.Horizontal)
62         else:
63             self.splitter.setOrientation(
64                 Qt.Orientation.Vertical)
65 
66 
67 if __name__ == '__main__':
68 
69     app = QApplication(sys.argv)
70 
71     main_window = Window()
72     main_window.show()
73 
74     sys.exit(app.exec())
  1. Create the QSplitter object. It lays its child widgets horizontally by default but you can change that using the QSplitter.setOrientation() method.

  2. Create the child widgets. In the example we create three QGroupBox objects. We also add two radio buttons to the first group box - selecting the buttons changes the QSplitter orientation dynamically.

  3. Add the child widgets to the splitter.