Pyside6 Grouping QRadioButtons for Exclusive Choices in GUIs

Pyside6 QRadioButtons

QRadioButtons are essential for presenting users with mutually exclusive options, like selecting one item from a set of choices. By grouping them, you ensure only one button can be selected at a time, which is perfect for scenarios such as choosing a payment method or selecting a difficulty level in an app. This lesson dives into how to effectively group QRadioButtons using PySide6's built-in mechanisms and the QButtonGroup class for more control.

Key Features

  • Auto-Exclusive Behavior: When QRadioButtons share the same parent widget, they automatically form an exclusive group where selecting one deselects the others.
  • Custom Grouping with QButtonGroup: Allows creation of multiple exclusive groups within the same parent, managing button states and providing signals for interactions.
  • State Management: Each QRadioButton can be checked or unchecked, emitting a toggled() signal on state changes for easy event handling.
  • Text and Icons: Supports text labels with optional icons and keyboard shortcuts (e.g., using ampersands for accelerators).
  • ID-Based Tracking: QButtonGroup assigns IDs to buttons, making it simpler to identify the selected option via checkedId().

Creating a QRadioButton

Start by subclassing QWidget to create a structured application. A basic QRadioButton is instantiated with optional text and a parent. Here's a example to create and display a single radio button:

from PySide6.QtWidgets import QApplication, QWidget, QRadioButton, QVBoxLayout

class RadioExample(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Single Radio Button")
        layout = QVBoxLayout(self)

        self.radio = QRadioButton("Option 1", self)  # Creates a radio button with text
        layout.addWidget(self.radio)

app = QApplication([])
window = RadioExample()
window.show()
app.exec()

This sets up a radio button that can be toggled on or off independently. For exclusivity, add more buttons with the same parent.


Grouping with Auto-Exclusivity

If all QRadioButtons have the same parent, PySide6 handles exclusivity automatically. Here's a example adding multiple buttons to a layout:

from PySide6.QtWidgets import QApplication, QWidget, QRadioButton, QVBoxLayout

class AutoExclusiveGroup(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Auto-Exclusive Radio Buttons")
        layout = QVBoxLayout(self)

        self.radio1 = QRadioButton("Option 1")
        self.radio2 = QRadioButton("Option 2")
        self.radio3 = QRadioButton("Option 3")

        layout.addWidget(self.radio1)
        layout.addWidget(self.radio2)
        layout.addWidget(self.radio3)

app = QApplication([])
window = AutoExclusiveGroup()
window.show()
app.exec()

Selecting any one will deselect the others automatically.


Using QButtonGroup for Advanced Grouping

For scenarios needing multiple groups in one widget or ID-based management, use QButtonGroup. Create the group, add buttons with optional IDs, and connect signals.

from PySide6.QtWidgets import QApplication, QWidget, QRadioButton, QVBoxLayout, QButtonGroup

class ButtonGroupExample(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QButtonGroup Example")
        layout = QVBoxLayout(self)
        self.group = QButtonGroup(self)  # Create the button group
        self.radio1 = QRadioButton("Option A")
        self.radio2 = QRadioButton("Option B")
        self.radio3 = QRadioButton("Option C")
        self.group.addButton(self.radio1, 1)  # Add with ID 1
        self.group.addButton(self.radio2, 2)
        self.group.addButton(self.radio3, 3)
        layout.addWidget(self.radio1)
        layout.addWidget(self.radio2)
        layout.addWidget(self.radio3)
        # Connect to signal for checked changes
        self.group.buttonToggled.connect(self.on_button_toggled)

    def on_button_toggled(self, button, checked):
        if checked:
            print(f"Selected: {button.text()} with ID: {self.group.checkedId()}")

app = QApplication([])
window = ButtonGroupExample()
window.show()
app.exec()

The group ensures exclusivity, and you can retrieve the selected ID with self.group.checkedId().


Handling Signals and Initial State

Set an initial checked state with setChecked(True), and use the toggled() signal for individual buttons or group-level signals like idToggled() :

from PySide6.QtWidgets import QApplication, QWidget, QRadioButton, QVBoxLayout, QButtonGroup

class SignalHandlingExample(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Signals and Initial State")
        layout = QVBoxLayout(self)

        self.group = QButtonGroup(self)

        self.radio1 = QRadioButton("Option A")
        self.radio2 = QRadioButton("Option B")
        self.radio3 = QRadioButton("Option C")

        self.group.addButton(self.radio1, 1)
        self.group.addButton(self.radio2, 2)
        self.group.addButton(self.radio3, 3)

        layout.addWidget(self.radio1)
        layout.addWidget(self.radio2)
        layout.addWidget(self.radio3)

        self.radio1.setChecked(True)  # Set initial selection

        # Group-level signal with ID
        self.group.idToggled.connect(self.on_id_toggled)

    def on_id_toggled(self, radio_id, checked):
        if checked:
            print(f"Selected ID: {radio_id}")

app = QApplication([])
window = SignalHandlingExample()
window.show()
app.exec()

This allows responsive actions based on user selection.


Practical Example

Here's a complete example combining QRadioButtons in a QButtonGroup within a simple settings dialog, demonstrating exclusivity and signal handling for a theme selection:

from PySide6.QtWidgets import QApplication, QWidget, QRadioButton, QVBoxLayout, QButtonGroup, QLabel, QPushButton, \\
    QLineEdit, QCheckBox, QSpacerItem, QSizePolicy

class ThemeSelector(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Theme Selection")
        self.setMinimumSize(400, 300)

        layout = QVBoxLayout(self)
        # Theme selection label
        label = QLabel("Choose a theme:")
        layout.addWidget(label)

        # Group for radio buttons
        self.group = QButtonGroup(self)

        self.light = QRadioButton("Light Theme")
        self.dark = QRadioButton("Dark Theme")
        self.custom = QRadioButton("Custom Theme")

        self.group.addButton(self.light, 1)
        self.group.addButton(self.dark, 2)
        self.group.addButton(self.custom, 3)
        self.group.idToggled.connect(self.on_theme_changed)

        layout.addWidget(self.light)
        layout.addWidget(self.dark)
        layout.addWidget(self.custom)

        self.dark.setChecked(True)  # Default selection

        # Example widgets
        example_label = QLabel("Example Widgets:")
        self.button = QPushButton("Click Me")
        self.extra_label = QLabel("This is an extra label")
        self.line_edit = QLineEdit("Enter text here")
        self.checkbox = QCheckBox("Check me")

        layout.addWidget(example_label)
        layout.addWidget(self.button)
        layout.addWidget(self.extra_label)
        layout.addWidget(self.line_edit)
        layout.addWidget(self.checkbox)

        spacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
        layout.addItem(spacer)

    def on_theme_changed(self, radio_id, checked):
        if checked:
            themes = {1: "Light", 2: "Dark", 3: "Custom"}
            print(f"Theme changed to: {themes[radio_id]}")

            # Apply the theme to the application
            app = QApplication.instance()
            if radio_id == 1:
                app.setStyleSheet("""
                    QWidget {
                        background-color: white;
                        color: black;
                    }
                    QRadioButton, QLabel, QPushButton, QLineEdit, QCheckBox {
                        color: black;
                    }
                    QPushButton {
                        background-color: #f0f0f0;
                        border: 1px solid #ccc;
                    }
                    QLineEdit {
                        background-color: white;
                        border: 1px solid #ccc;
                    }
                """)
            elif radio_id == 2:
                app.setStyleSheet("""
                    QWidget {
                        background-color: black;
                        color: white;
                    }
                    QRadioButton, QLabel, QPushButton, QLineEdit, QCheckBox {
                        color: white;
                    }
                    QPushButton {
                        background-color: #333;
                        border: 1px solid #555;
                    }
                    QLineEdit {
                        background-color: #222;
                        border: 1px solid #555;
                    }
                """)
            elif radio_id == 3:
                app.setStyleSheet("""
                    QWidget {
                        background-color: #add8e6;  /* Light blue */
                        color: navy;
                    }
                    QRadioButton, QLabel, QPushButton, QLineEdit, QCheckBox {
                        color: navy;
                    }
                    QPushButton {
                        background-color: #87ceeb;  /* Sky blue */
                        border: 1px solid #4682b4;
                    }
                    QLineEdit {
                        background-color: #fff;
                        border: 1px solid #4682b4;
                    }
                """)

app = QApplication([])
window = ThemeSelector()
window.show()
app.exec()


For related topics, explore advanced event handling in Module 4 or styling radio buttons in Module 10 to enhance their appearance. Upcoming Qt updates may introduce more QML integrations for radio-like controls in declarative UIs.