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
refresh_annots
Gud Boi 2026-01-30 15:39:25 -05:00
parent eb78437994
commit bac8317a4a
4 changed files with 72 additions and 46 deletions

View File

@ -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,
)

View File

@ -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:

View File

@ -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

View File

@ -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:
'''