From bac8317a4ad488fbbb218a1ffc11655b02dec2d0 Mon Sep 17 00:00:00 2001 From: goodboy Date: Fri, 30 Jan 2026 15:39:25 -0500 Subject: [PATCH] Add `get_godw()` singleton getter for `GodWidget` Expose `get_godw()` helper to retrieve the central `GodWidget` instance from anywhere in the UI code. Set the singleton in `_async_main()` on startup. Also, - add docstring to `run_qtractor()` explaining trio guest mode - type annotate `instance: GodWidget` in `run_qtractor()` - import reorg in `._app` for cleaner grouping - whitespace cleanup: `Type | None` -> `Type|None` throughout - fix bitwise-or alignment: `Flag | Other` -> `Flag|Other` (this commit-msg was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- piker/ui/_app.py | 21 ++++++------ piker/ui/_chart.py | 79 ++++++++++++++++++++++++++++----------------- piker/ui/_exec.py | 6 +++- piker/ui/_window.py | 12 +++---- 4 files changed, 72 insertions(+), 46 deletions(-) diff --git a/piker/ui/_app.py b/piker/ui/_app.py index 68ecb3dd..f078163d 100644 --- a/piker/ui/_app.py +++ b/piker/ui/_app.py @@ -27,15 +27,15 @@ import trio from piker.ui.qt import ( QEvent, ) -from ..service import maybe_spawn_brokerd +from . import _chart from . import _event -from ._exec import run_qtractor -from ..data.feed import install_brokerd_search -from ..data._symcache import open_symcache -from ..accounting import unpack_fqme from . import _search -from ._chart import GodWidget +from ..accounting import unpack_fqme +from ..data._symcache import open_symcache +from ..data.feed import install_brokerd_search from ..log import get_logger +from ..service import maybe_spawn_brokerd +from ._exec import run_qtractor log = get_logger(__name__) @@ -73,8 +73,8 @@ async def load_provider_search( async def _async_main( - # implicit required argument provided by ``qtractor_run()`` - main_widget: GodWidget, + # implicit required argument provided by `qtractor_run()` + main_widget: _chart.GodWidget, syms: list[str], brokers: dict[str, ModuleType], @@ -87,6 +87,9 @@ async def _async_main( Provision the "main" widget with initial symbol data and root nursery. """ + # set as singleton + _chart._godw = main_widget + from . import _display from ._pg_overrides import _do_overrides _do_overrides() @@ -201,6 +204,6 @@ def _main( brokermods, piker_loglevel, ), - main_widget_type=GodWidget, + main_widget_type=_chart.GodWidget, tractor_kwargs=tractor_kwargs, ) diff --git a/piker/ui/_chart.py b/piker/ui/_chart.py index 98b25398..270255fc 100644 --- a/piker/ui/_chart.py +++ b/piker/ui/_chart.py @@ -82,6 +82,25 @@ if TYPE_CHECKING: log = get_logger(__name__) +_godw: GodWidget|None = None + +def get_godw() -> GodWidget: + ''' + Get the top level "god widget", the root/central-most Qt + widget-object set as `QMainWindow.setCentralWidget(_godw)`. + + See `piker.ui._exec` for the runtime init details and all the + machinery for running `trio` on the Qt event loop in guest mode. + + ''' + if _godw is None: + raise RuntimeError( + 'No god-widget initialized ??\n' + 'Have you called `run_qtractor()` yet?\n' + ) + return _godw + + class GodWidget(QWidget): ''' "Our lord and savior, the holy child of window-shua, there is no @@ -104,7 +123,7 @@ class GodWidget(QWidget): super().__init__(parent) - self.search: SearchWidget | None = None + self.search: SearchWidget|None = None self.hbox = QHBoxLayout(self) self.hbox.setContentsMargins(0, 0, 0, 0) @@ -123,9 +142,9 @@ class GodWidget(QWidget): tuple[LinkedSplits, LinkedSplits], ] = {} - self.hist_linked: LinkedSplits | None = None - self.rt_linked: LinkedSplits | None = None - self._active_cursor: Cursor | None = None + self.hist_linked: LinkedSplits|None = None + self.rt_linked: LinkedSplits|None = None + self._active_cursor: Cursor|None = None # assigned in the startup func `_async_main()` self._root_n: trio.Nursery = None @@ -369,9 +388,9 @@ class ChartnPane(QFrame): https://doc.qt.io/qt-5/qwidget.html#composite-widgets ''' - sidepane: FieldsForm | SearchWidget + sidepane: FieldsForm|SearchWidget hbox: QHBoxLayout - chart: ChartPlotWidget | None = None + chart: ChartPlotWidget|None = None def __init__( self, @@ -387,13 +406,13 @@ class ChartnPane(QFrame): self.chart = None hbox = self.hbox = QHBoxLayout(self) - hbox.setAlignment(Qt.AlignTop | Qt.AlignLeft) + hbox.setAlignment(Qt.AlignTop|Qt.AlignLeft) hbox.setContentsMargins(0, 0, 0, 0) hbox.setSpacing(3) def set_sidepane( self, - sidepane: FieldsForm | SearchWidget, + sidepane: FieldsForm|SearchWidget, ) -> None: # add sidepane **after** chart; place it on axis side @@ -404,7 +423,7 @@ class ChartnPane(QFrame): self._sidepane = sidepane @property - def sidepane(self) -> FieldsForm | SearchWidget: + def sidepane(self) -> FieldsForm|SearchWidget: return self._sidepane @@ -450,7 +469,7 @@ class LinkedSplits(QWidget): # chart-local graphics state that can be passed to # a ``graphic_update_cycle()`` call by any task wishing to # update the UI for a given "chart instance". - self.display_state: DisplayState | None = None + self.display_state: DisplayState|None = None self._mkt: MktPair = None @@ -486,7 +505,7 @@ class LinkedSplits(QWidget): def set_split_sizes( self, - prop: float | None = None, + prop: float|None = None, ) -> None: ''' @@ -567,8 +586,8 @@ class LinkedSplits(QWidget): # style? self.chart.setFrameStyle( - QFrame.Shape.StyledPanel | - QFrame.Shadow.Plain + QFrame.Shape.StyledPanel + |QFrame.Shadow.Plain ) return self.chart @@ -580,11 +599,11 @@ class LinkedSplits(QWidget): shm: ShmArray, flume: Flume, - array_key: str | None = None, + array_key: str|None = None, style: str = 'line', _is_main: bool = False, - sidepane: QWidget | None = None, + sidepane: QWidget|None = None, draw_kwargs: dict = {}, **cpw_kwargs, @@ -687,7 +706,7 @@ class LinkedSplits(QWidget): cpw.plotItem.vb.linked = self cpw.setFrameStyle( QFrame.Shape.StyledPanel - # | QFrame.Shadow.Plain + # |QFrame.Shadow.Plain ) # don't show the little "autoscale" A label. @@ -800,7 +819,7 @@ class LinkedSplits(QWidget): def resize_sidepanes( self, - from_linked: LinkedSplits | None = None, + from_linked: LinkedSplits|None = None, ) -> None: ''' @@ -874,7 +893,7 @@ class ChartPlotWidget(pg.PlotWidget): # TODO: load from config use_open_gl: bool = False, - static_yrange: tuple[float, float] | None = None, + static_yrange: tuple[float, float]|None = None, parent=None, **kwargs, @@ -889,7 +908,7 @@ class ChartPlotWidget(pg.PlotWidget): # NOTE: must be set bfore calling ``.mk_vb()`` self.linked = linkedsplits - self.sidepane: FieldsForm | None = None + self.sidepane: FieldsForm|None = None # source of our custom interactions self.cv = self.mk_vb(name) @@ -923,7 +942,7 @@ class ChartPlotWidget(pg.PlotWidget): self.useOpenGL(use_open_gl) self.name = name self.data_key = data_key or name - self.qframe: ChartnPane | None = None + self.qframe: ChartnPane|None = None # scene-local placeholder for book graphics # sizing to avoid overlap with data contents @@ -934,7 +953,7 @@ class ChartPlotWidget(pg.PlotWidget): # registry of overlay curve names self._vizs: dict[str, Viz] = {} - self.feed: Feed | None = None + self.feed: Feed|None = None self._labels = {} # registry of underlying graphics self._ysticks = {} # registry of underlying graphics @@ -1027,7 +1046,7 @@ class ChartPlotWidget(pg.PlotWidget): def increment_view( self, datums: int = 1, - vb: ChartView | None = None, + vb: ChartView|None = None, ) -> None: ''' @@ -1058,8 +1077,8 @@ class ChartPlotWidget(pg.PlotWidget): def overlay_plotitem( self, name: str, - index: int | None = None, - axis_title: str | None = None, + index: int|None = None, + axis_title: str|None = None, axis_side: str = 'right', axis_kwargs: dict = {}, @@ -1147,14 +1166,14 @@ class ChartPlotWidget(pg.PlotWidget): shm: ShmArray, flume: Flume, - array_key: str | None = None, + array_key: str|None = None, overlay: bool = False, - color: str | None = None, + color: str|None = None, add_label: bool = True, - pi: pg.PlotItem | None = None, + pi: pg.PlotItem|None = None, step_mode: bool = False, is_ohlc: bool = False, - add_sticky: None | str = 'right', + add_sticky: None|str = 'right', **graphics_kwargs, @@ -1252,7 +1271,7 @@ class ChartPlotWidget(pg.PlotWidget): # use the tick size precision for display name = name or pi.name mkt: MktPair = self.linked.mkt - digits: int | None = None + digits: int|None = None if name in mkt.fqme: digits = mkt.price_tick_digits @@ -1286,7 +1305,7 @@ class ChartPlotWidget(pg.PlotWidget): shm: ShmArray, flume: Flume, - array_key: str | None = None, + array_key: str|None = None, **draw_curve_kwargs, ) -> Viz: diff --git a/piker/ui/_exec.py b/piker/ui/_exec.py index ba91e534..3643786d 100644 --- a/piker/ui/_exec.py +++ b/piker/ui/_exec.py @@ -91,6 +91,10 @@ def run_qtractor( window_type: QMainWindow = None, ) -> None: + ''' + Run the Qt event loop and embed `trio` via guest mode on it. + + ''' # avoids annoying message when entering debugger from qt loop pyqtRemoveInputHook() @@ -170,7 +174,7 @@ def run_qtractor( # hook into app focus change events app.focusChanged.connect(window.on_focus_change) - instance = main_widget_type() + instance: GodWidget = main_widget_type() instance.window = window # override tractor's defaults diff --git a/piker/ui/_window.py b/piker/ui/_window.py index a15ecd24..12f4209a 100644 --- a/piker/ui/_window.py +++ b/piker/ui/_window.py @@ -61,9 +61,9 @@ class MultiStatus: self, msg: str, - final_msg: str | None = None, + final_msg: str|None = None, clear_on_next: bool = False, - group_key: Union[bool, str] | None = False, + group_key: Union[bool, str]|None = False, ) -> Union[Callable[..., None], str]: ''' @@ -175,11 +175,11 @@ class MainWindow(QMainWindow): self.setWindowTitle(self.title) # set by runtime after `trio` is engaged. - self.godwidget: GodWidget | None = None + self.godwidget: GodWidget|None = None self._status_bar: QStatusBar = None self._status_label: QLabel = None - self._size: tuple[int, int] | None = None + self._size: tuple[int, int]|None = None @property def mode_label(self) -> QLabel: @@ -202,7 +202,7 @@ class MainWindow(QMainWindow): label.setMargin(2) label.setAlignment( QtCore.Qt.AlignVCenter - | QtCore.Qt.AlignRight + |QtCore.Qt.AlignRight ) self.statusBar().addPermanentWidget(label) label.show() @@ -288,7 +288,7 @@ class MainWindow(QMainWindow): def configure_to_desktop( self, - size: tuple[int, int] | None = None, + size: tuple[int, int]|None = None, ) -> None: '''