# Parameters
|pos/kw |name |type |description|
|--- |--- |--- |--- |
-"""
\ No newline at end of file
+"""
+
+
+Panes
+OBS Settings
+ Record path
+OBS-WS Connection
+ Host
+ Port
+ Password
+ Timeout
+
+
+create_pane(settings={setting_name:setting_type, [...])) -> TabPane
+
+
+for each setting_name, we need also a reactive attribute and a message for when it changes
+
+pane_factory()
+ reactive_factory()
+ on_input_submitted_factory()
from textual import on
from textual.app import App
from textual.containers import HorizontalGroup, VerticalGroup, ScrollableContainer
-from textual.widgets import Footer, Header
+from textual.widgets import Footer, Header, Input
# need to import log to initalize logging
from . import data, log
@on(obs.API.Report, "#api")
def on_api_report(self, message: obs.API.Report):
self.update_monitors(message.data)
+
+ # @on(obs.API.Report, "#api")(on_api_report)
@on(RecordControls.RecordStarted, "#record_controls")
api: obs.API = self.query_exactly_one("#api")
getattr(api, query)(*args, **kwargs)
-
- @on(settings_block.pane.OBSSettings.RecordPathChanged, "#settings-content")
- def on_settings_record_path_changed(self, message: settings_block.pane.OBSSettings.RecordPathChanged):
+ @on(settings_block.OBSSettingsPane.RecordPathChanged, "#settings-content")
+ def on_settings_block_record_path_changed(self, message):
api: obs.API = self.query_exactly_one("#api")
- api.set_record_path(message.record_path)
+ api.set_record_path(message.value)
def update_monitors(self, obs_stats: obs.OBSStats):
record_controls: RecordControls = self.query_exactly_one("#record_controls")
- obs_settings: settings_block.pane.OBSSettings.RecordPathChanged = self.query_exactly_one("#settings-content")
+ obs_settings = self.query_exactly_one("#settings-content")
stats_display: StatsDisplay = self.query_exactly_one("#stats_display")
record_controls.obsws_connected = obs_stats.obsws_connection_active
-from .settings_block import SettingsBlock
\ No newline at end of file
+from .settings_block import SettingsBlock, OBSSettingsPane
\ No newline at end of file
--- /dev/null
+from enum import Enum
+from functools import partial
+from logging import getLogger
+from typing import Any
+from textual import on
+from textual.containers import ScrollableContainer, Grid
+from textual.reactive import reactive
+from textual.widgets import TabPane, Input, Label
+from textual.widget import Widget
+from ...message import MSGBase
+
+logger = getLogger(__name__)
+class SettingsMSGBase(MSGBase):
+ value = None
+
+ def __init__(self, control, value, *, _log_me=True):
+ super().__init__(control, _log_me=_log_me)
+ self.value = value
+
+class SettingsType(Enum):
+ STRING = (partial(Input, compact=True), str, Input.Submitted)
+ FLOAT = (partial(Input, compact=True), float, Input.Submitted)
+ INT = (partial(Input, compact=True), int, Input.Submitted)
+
+ @property
+ def widget(self):
+ return self.value[0]
+
+ @property
+ def type(self):
+ return self.value[1]
+
+ @property
+ def msg(self):
+ return self.value[2]
+
+
+def pane_factory(cls_name: str, settings_list: list, settings_type_list: list, defaults_list) -> TabPane:
+ """Creates a new TabPane subclass with cls_name as its name.
+
+ settings_list should be a list of names for settings.
+ settings_type_list should be a list of pane.SettingsType members
+ settings_default_list should be a list of defaults, for the settings.
+
+ Returned widget will have appropriate messages for each input field.
+
+ For example, pane_factory("Settings", ["record_path"], [pane.SettingsType.STRING], [""])
+ will return a TabPane with a Label and an Input box, a reactive attribute "record_path",
+ a watch_record_path function to update the Input value, and an on_record_path_changed
+ function, which emits a RecordPathChanged message containing a value when the user
+ submits the input."""
+ DEFAULT_CSS = """
+ Grid {
+ grid-size:2;
+ grid-rows:2;
+ }
+ """
+ def watch_func_factory(watch_id: str):
+ def watch_func(self, value):
+ input = self.query_exactly_one(watch_id)
+ input.value = value
+
+ return watch_func
+
+ def on_func_factory(reactive, msg_type, post_msg, _ctrl_id):
+ def on_func(self, event):
+ self.set_reactive(reactive, event.value)
+ self.post_message(post_msg(self, event.value))
+
+ return on(msg_type, _ctrl_id)(on_func)
+
+ def compose_factory():
+ def output_func(self):
+ with ScrollableContainer():
+ with Grid():
+ for setting, control_and_type, setting_default in zip(settings_list, settings_type_list, defaults_list):
+ ctrl_id = f"{settings_type.widget.func.__name__.lower()}_{setting}"
+ yield Label(setting)
+ yield control_and_type.widget(value=setting_default, id=ctrl_id)
+
+ return output_func
+
+ new_dict = dict()
+ for setting, control_and_type, setting_default in zip(settings_list, settings_type_list, defaults_list):
+ settings_type: SettingsType = control_and_type
+ ctrl_id = f"{settings_type.widget.func.__name__.lower()}_{setting}"
+ msg_name = f"{snake_to_camel(setting)}Changed"
+ watch_x = f"watch_{setting}"
+ on_x = f"on_{setting}_changed"
+ new_dict[setting] = reactive(settings_type.type(setting_default)) # eg. record_path
+ new_dict[watch_x] = watch_func_factory(f"#{ctrl_id}") # watch_record_path
+ new_dict[msg_name] = type(msg_name, (SettingsMSGBase, ), {}) # RecordPathChanged
+ new_dict[on_x] = on_func_factory(new_dict[setting], settings_type.msg, new_dict[msg_name], f"#{ctrl_id}") # on_record_path_changed
+ new_dict["DEFAULT_CSS"] = DEFAULT_CSS
+ new_dict["compose"] = compose_factory() # Adds all the Inputs
+
+ new_pane = type(cls_name, (TabPane,), new_dict)
+
+ return new_pane
+
+def snake_to_camel(string: str):
+ return str.join("", [str.capitalize(x) for x in string.split("_")])
\ No newline at end of file
+++ /dev/null
-from .obs_settings import OBSSettings
\ No newline at end of file
+++ /dev/null
-from textual.containers import ScrollableContainer, Grid
-from textual.message import Message
-from textual.reactive import reactive
-from textual.widgets import Label, Input, TabPane
-
-from logging import getLogger
-
-logger = getLogger(__name__)
-
-class OBSSettings(TabPane):
- COMPONENT_CLASSES = {
- "obs_settings_pane--input",
- "obs_settings_pane--output",
- "obs_settings_pane--content",
- "obs_settings_pane--scrollbox"
- }
- record_path: reactive[str] = reactive("")
-
- class RecordPathChanged(Message):
- control: "OBSSettings" = None
- record_path: str = None
- def __init__(self, ctrl, path):
- super().__init__()
- self.control = ctrl
- self.record_path = path
-
-
- def __init__(self, *args, **kwargs):
- super().__init__("OBS Settings", *args, **kwargs)
-
-
- def compose(self):
- with ScrollableContainer(classes="obs_settings_pane--scrollbox"):
- with Grid(classes="obs_settings_pane--content"):
- yield Label("Record path:", classes="obs_settings_pane--label")
- yield Input("path", id="input_record_path", classes="obs_settings_pane--input", compact=True)
-
-
- def on_input_submitted(self, event:Input.Submitted):
- self.set_reactive(OBSSettings.record_path, event.value)
- if event.control.id == "input_record_path":
- self.post_message(OBSSettings.RecordPathChanged(self, event.value))
-
- def watch_record_path(self, value):
- input: Input = self.query_exactly_one("#input_record_path")
- input.value = value
+from logging import getLogger
+from textual import on
from textual.widgets import TabbedContent, TabPane, Placeholder
from textual.containers import Container
from . import pane
+logger = getLogger(__name__)
+
+OBSSettingsPane = pane.pane_factory(
+ "OBSSettingsPane",
+ ["record_path"],
+ [pane.SettingsType.STRING, pane.SettingsType.STRING],
+ [""]
+)
class SettingsBlock(Container):
def compose(self):
with TabbedContent(classes="settings_block--tabbed-content"):
- yield pane.OBSSettings(id="settings-content", classes="settings_block--obs_settings")
+ yield OBSSettingsPane("OBS Settings", id="settings-content")
+ # yield pane.OBSSettings(id="settings-content", classes="settings_block--obs_settings")
with TabPane("OBS Connection", id="connection-tab"):
yield Placeholder("CONNECTION BLOCK", id="conection-plc")
with TabPane("obs-ctl Settings", id="clients-tab"):
- yield Placeholder("CLIENT SETTINGS BLOCK", id="client-plc")
\ No newline at end of file
+ yield Placeholder("CLIENT SETTINGS BLOCK", id="client-plc")
name = request.func.__name__
else:
name = request.__name__
- logger.info(f"Sending request: {name}")
+ # logger.info(f"Sending request: {name}")
output = None
_sentry = 3