from . import log, data
from .widget import LabeledLabel, OBSSettingsPane
-from .widget.element.single_status_control import SingleStatusControl
+from .widget.element.bool_control import BoolControl
logger = log.getLogger(__name__)
obs_req_client = obs.ReqClient()
with Vertical(id="blocks_v_container", classes="blocks_containers"):
with Horizontal(id="blocks_h_container1", classes="blocks_h_containers"):
with Grid(id="controls_container", classes="blocks gridblocks"):
- yield SingleStatusControl(label="Recording", id="record_control", classes="controls")
- yield SingleStatusControl(label="Paused", on_label="Pause", off_label="Unpause", swap_red=True, id="pause_control", classes="controls")
- yield SingleStatusControl(label="Streaming", id="stream_control", classes="controls")
+ yield BoolControl(False, label="Recording", id="record_control", classes="controls")
+ yield BoolControl(False, label="Paused", true_label="Pause", false_label="Unpause", swap_red=True, id="pause_control", classes="controls")
+ yield BoolControl(False, label="Streaming", id="stream_control", classes="controls")
with TabbedContent(id="settings_container", classes="blocks"):
with TabPane("OBS Settings", id="settings-tab"):
yield OBSSettingsPane(id="settings-content")
yield LabeledLabel("Encdng frames dropped", "-/- (-.-%)", id="encoding_lag", classes="stats")
- @on(SingleStatusControl.TurnedOff, "#stream_control")
+ @on(BoolControl.SetFalse, "#stream_control")
def stream_off(self, message):
logger.debug(f"Stop message rcv'd from {message.control}")
try:
else:
raise
- @on(SingleStatusControl.TurnedOn, "#stream_control")
+ @on(BoolControl.SetTrue, "#stream_control")
def stream_on(self, message):
logger.debug("Stream Started message rcv'd")
try:
else:
raise
- @on(SingleStatusControl.TurnedOff, "#record_control")
+ @on(BoolControl.SetFalse, "#record_control")
def record_off(self, message):
logger.debug("Record Stopped message rcv'd")
try:
else:
raise
- @on(SingleStatusControl.TurnedOn, "#record_control")
+ @on(BoolControl.SetTrue, "#record_control")
def record_on(self, message):
logger.debug("Record Started message rcv'd")
try:
raise
- @on(SingleStatusControl.TurnedOn, "#pause_control")
+ @on(BoolControl.SetTrue, "#pause_control")
def pause_on(self, message):
logger.debug("Record Paused message rcv'd")
try:
raise
- @on(SingleStatusControl.TurnedOff, "#pause_control")
+ @on(BoolControl.SetFalse, "#pause_control")
def pause_off(self, message):
logger.debug("Record Unpaused message rcv'd")
try:
def update_monitors(self, obs_stats: OBSStats):
- stream_ctl: SingleStatusControl = self.query_exactly_one("#stream_control")
- record_ctl: SingleStatusControl = self.query_exactly_one("#record_control")
- pause_ctl: SingleStatusControl = self.query_exactly_one("#pause_control")
+ stream_ctl: BoolControl = self.query_exactly_one("#stream_control")
+ record_ctl: BoolControl = self.query_exactly_one("#record_control")
+ pause_ctl: BoolControl = self.query_exactly_one("#pause_control")
cpu_usage: LabeledLabel = self.query_exactly_one("#cpu_usage")
memory_usage: LabeledLabel = self.query_exactly_one("#memory_usage")
active_fp: LabeledLabel = self.query_exactly_one("#fps")
min-height:8;
}
-SingleStatusControl {
+BoolControl {
background:$surface;
border:blank;
max-height:5;
--- /dev/null
+from textual import on
+from textual.app import ComposeResult
+from textual.containers import Container
+from textual.css import query
+from textual.message import Message
+from textual.reactive import reactive
+from textual.widgets import Button, RadioButton
+
+from ...log import getLogger
+
+logger = getLogger(__name__)
+
+class BoolControl(Container):
+ """BoolControl manages a boolean that belongs to something else.
+
+ In other words, it can display true or false and this can be set
+ on it directly, but its buttons do nothing other than post a
+ message. Designed as i/o for an API"""
+ DEFAULT_CSS = """
+ .bool_control--light {
+ background:$panel;
+ text-align:center;
+ align:center bottom;
+ width:1fr;
+ height:1;
+ }
+ .bool_control--true_button {
+ width:1fr;
+ height:1fr;
+ }
+ .bool_control--false_button {
+ width:1fr;
+ height:1fr;
+ }
+ """
+ COMPONENT_CLASSES = {
+ "bool_control--light",
+ "bool_control--true_button",
+ "bool_control--false_button",
+ }
+ value = reactive(False)
+ label = reactive("Status")
+ true_label = reactive("Start")
+ false_label = reactive("Stop")
+ swap_red = reactive(False)
+ compact = reactive(True)
+ control_layout = reactive("vertical")
+
+ class MsgBase(Message):
+ control: "BoolControl" = None
+ def __init__(self, control):
+ super().__init__()
+ self.control = control
+ class SetTrue(MsgBase): ...
+ class SetFalse(MsgBase): ...
+
+
+ def __init__(
+ self,
+ value,
+ label: str = "Boolean",
+ true_label = "Start",
+ false_label = "Stop",
+ swap_red = False,
+ compact = True,
+ *children,
+ layout = "vertical",
+ name = None,
+ id = None,
+ classes = None,
+ disabled = False,
+ markup = True):
+ super().__init__(*children, name=name, id=id, classes=classes, disabled=disabled, markup=markup)
+ self.set_reactive(BoolControl.value, value)
+ self.set_reactive(BoolControl.label, label)
+ self.set_reactive(BoolControl.true_label, true_label)
+ self.set_reactive(BoolControl.false_label, false_label)
+ self.set_reactive(BoolControl.swap_red, swap_red)
+ self.set_reactive(BoolControl.compact, compact)
+ self.set_reactive(BoolControl.control_layout, layout)
+
+ def compose(self) -> ComposeResult:
+ with Container(classes="bool_control--light"):
+ yield RadioButton(
+ label="",
+ value=False,
+ button_first=False,
+ id="light",
+ compact=True,
+ disabled=True,
+ classes="bool_control--light",
+ )
+ yield Button(
+ label="Start",
+ id="true",
+ variant="success",
+ compact=True,
+ classes="bool_control--true_button"
+ )
+ yield Button(
+ label="Stop",
+ id="false",
+ variant="error",
+ compact=True,
+ classes = "bool_control--false_button"
+ )
+
+
+ def _on_mount(self, event):
+ # super()._on_mount(event)
+ self.watch_value(self.value)
+ self.watch_label(self.label)
+ self.watch_true_label(self.true_label)
+ self.watch_false_label(self.false_label)
+ self.watch_swap_red(self.swap_red)
+ self.watch_compact(self.compact)
+ self.watch_control_layout(self.control_layout)
+
+
+ @on(Button.Pressed, "#false")
+ def false_pressed(self, event: Button.Pressed):
+ self.post_message(self.SetFalse(self))
+
+
+ @on(Button.Pressed, "#true")
+ def true_pressed(self):
+ self.post_message(self.SetTrue(self))
+
+
+ def watch_value(self, value: bool) -> None:
+ light: RadioButton = self.query_exactly_one("#light")
+ light.value = value
+
+
+ def watch_label(self, value):
+ light: RadioButton = self.query_exactly_one("#light")
+ light.label = value
+
+
+ def watch_true_label(self, value):
+ true: Button = self.query_exactly_one("#true")
+ true.label = value
+
+
+ def watch_false_label(self, value):
+ false: Button = self.query_exactly_one("#false")
+ false.label = value
+
+
+ def watch_swap_red(self, value):
+ true: Button = self.query_exactly_one("#true")
+ false: Button = self.query_exactly_one("#false")
+ false.variant = "success" if value else "error"
+ true.variant = "error" if value else "success"
+
+
+ def watch_compact(self, value):
+ light: RadioButton = self.query_exactly_one("#light")
+ true: Button = self.query_exactly_one("#true")
+ false: Button = self.query_exactly_one("#false")
+ light.compact = value
+ true.compact = value
+ false.compact = value
+
+
+ def watch_control_layout(self, value):
+ self.set_styles(layout=value)
\ No newline at end of file
+++ /dev/null
-from textual.containers import Container
-from textual.message import Message
-from textual.reactive import reactive
-from textual.css import query
-from textual.widget import ComposeResult
-from textual.widgets import Button, RadioButton
-
-from ...log import getLogger
-
-logger = getLogger()
-
-class SingleStatusControl(Container):
- DEFAULT_CSS = """
- .singlestatuscontrol--light {
- background:$panel;
- text-align:center;
- align:center bottom;
- width:1fr;
- height:1fr;
- }
- .singlestatuscontrol--on_button {
- width:1fr;
- height:1fr;
- }
- .singlestatuscontrol--off_button {
- width:1fr;
- height:1fr;
- }
- """
- COMPONENT_CLASSES = {
- "singlestatuscontrol--light",
- "singlesttauscontrol--on_button",
- "singlestatuscontrol--off_button",
- }
- status = reactive(False)
- label = reactive("Status")
- on_label = reactive("Start")
- off_label = reactive("Stop")
- swap_red = reactive(False)
-
- class MsgBase(Message):
- control: "SingleStatusControl" = None
- def __init__(self, control):
- super().__init__()
- self.control = control
- class TurnedOn(MsgBase): ...
- class TurnedOff(MsgBase): ...
-
-
- def __init__(
- self,
- *children,
- label: str = "Status",
- on_label = "Start",
- off_label = "Stop",
- swap_red = False,
- layout = "vertical",
- name = None,
- id = None,
- classes = None,
- disabled = False,
- markup = True):
- super().__init__(*children, name=name, id=id, classes=classes, disabled=disabled, markup=markup)
- self.status = False
- self.label = label
- self.on_label = on_label
- self.off_label = off_label
- self.swap_red = swap_red
- self.set_styles(layout=layout)
-
- def compose(self) -> ComposeResult:
- logger.debug("Composing SingleStatusControl")
- with Container(classes="singlestatuscontrol--light"):
- yield RadioButton(
- label="",
- value=False,
- button_first=False,
- id="light",
- compact=True,
- disabled=True,
- )
- yield Button(
- label="Start",
- id="on",
- variant="success",
- compact=True,
- classes="singlestatuscontrol--on_button"
- )
- yield Button(
- label="Stop",
- id="off",
- variant="error",
- compact=True,
- classes = "singlestatuscontrol--off_button"
- )
-
-
- def _on_mount(self, event):
- # super()._on_mount(event)
- self.watch_status(self.status)
- self.watch_label(self.label)
- self.watch_on_label(self.on_label)
- self.watch_off_label(self.off_label)
- self.watch_swap_red(self.swap_red)
- self.refresh()
-
-
- def on_button_pressed(self, event: Button.Pressed):
- if event.button.id == "on":
- logger.debug(f"'On' pressed on {event.control}")
- self.post_message(self.TurnedOn(self))
- elif event.button.id == "off":
- logger.debug(f"'Off' pressed on {event.control}")
- self.post_message(self.TurnedOff(self))
-
-
- def watch_status(self, value: bool) -> None:
- try:
- light: RadioButton = self.query_exactly_one("#light")
- light.value = value
- except query.NoMatches:
- if self.is_mounted:
- raise
-
-
- def watch_label(self, value):
- try:
- light: RadioButton = self.query_exactly_one("#light")
- light.label = value
- except query.NoMatches:
- if self.is_mounted:
- raise
-
- def watch_on_label(self, value):
- try:
- on: Button = self.query_exactly_one("#on")
- on.label = value
- except query.NoMatches:
- if self.is_mounted:
- raise
-
- def watch_off_label(self, value):
- try:
- off: Button = self.query_exactly_one("#off")
- off.label = value
- except query.NoMatches:
- if self.is_mounted:
- raise
-
- def watch_swap_red(self, value):
- try:
- on: Button = self.query_exactly_one("#on")
- off: Button = self.query_exactly_one("#off")
- off.variant = "success" if value else "error"
- on.variant = "error" if value else "success"
- except query.NoMatches:
- if self.is_mounted:
- raise
\ No newline at end of file