Source code for edifice.base_components.radio_button
from __future__ import annotations
import typing as tp
from edifice.qt import QT_VERSION
from .base_components import CommandType, Element, QtWidgetElement, _ensure_future, _WidgetTree
if QT_VERSION == "PyQt6" and not tp.TYPE_CHECKING:
from PyQt6 import QtWidgets
else:
from PySide6 import QtWidgets
class EdRadioButton(QtWidgets.QRadioButton):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def nextCheckState(self):
"""
https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QAbstractButton.html#PySide6.QtWidgets.QAbstractButton.nextCheckState
We override this because we never want the radio button checked state to
be set directly by the user, or by the Qt radio button group mechanism.
We always want the checked state to be set by the checked prop.
"""
pass
[docs]
class RadioButton(QtWidgetElement[EdRadioButton]):
"""Radio buttons.
* Underlying Qt Widget
`QRadioButton <https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QRadioButton.html>`_
.. figure:: /image/radio_button.png
:width: 300
Three mutually exclusive radio buttons.
Radio buttons are used to specify a single choice out of many.
Because of the declarative nature of Edifice, we can ignore all of the
Qt mechanisms for radio button “groups” and “exclusivity.”
Just declare each radio button :code:`checked` prop to depend on the state.
Example::
value, value_set = use_state(cast(Literal["op1", "op2"], "op1"))
with ed.VBoxView():
# Exclusive RadioButtons with different parents
with ed.VBoxView():
ed.RadioButton(
checked = value == "op1",
on_change = lambda checked: value_set("op1") if checked else None,
text = "Option 1",
style = {} if value == "op1" else { "color": "grey" },
)
with ed.VBoxView():
ed.RadioButton(
checked = value == "op2",
on_change = lambda checked: value_set("op2") if checked else None,
text = "Option 2",
style = {} if value == "op1" else { "color": "grey" },
)
Args:
checked:
Whether or not the RadioButton is checked.
text:
Text for the label of the RadioButton.
on_change:
Event handler for when the checked value changes, but
only when the user checks or unchecks, not when the checked prop
changes.
"""
[docs]
def __init__(
self,
checked: bool = False,
text: str = "",
on_change: tp.Callable[[bool], None | tp.Awaitable[None]] | None = None,
**kwargs,
):
super().__init__(**kwargs)
self._register_props(
{
"checked": checked,
"text": text,
"on_change": on_change,
},
)
self._register_props(kwargs)
def _initialize(self):
self.underlying = EdRadioButton(str(self.props.text))
size = self.underlying.font().pointSize()
self._set_size(size * len(self.props.text), size)
self.underlying.setObjectName(str(id(self)))
# We setAutoExclusive(False) because we don't want to use the Qt
# radio button group mechanism. We handle the exclusivity.
self.underlying.setAutoExclusive(False)
self.underlying.clicked.connect(self._on_clicked)
def _on_clicked(self, checked: bool):
if self.props.on_change is not None:
return _ensure_future(self.props.on_change)(not checked)
def _set_checked(self, checked: bool):
widget = tp.cast(EdRadioButton, self.underlying)
widget.blockSignals(True)
widget.setChecked(checked)
widget.blockSignals(False)
def _qt_update_commands(
self,
widget_trees: dict[Element, _WidgetTree],
newprops,
):
if self.underlying is None:
self._initialize()
assert self.underlying is not None
widget = tp.cast(EdRadioButton, self.underlying)
commands = super()._qt_update_commands_super(widget_trees, newprops, self.underlying)
if "checked" in newprops:
commands.append(CommandType(self._set_checked, newprops.checked))
if "text" in newprops:
commands.append(CommandType(widget.setText, str(newprops.text)))
return commands