diff --git a/piker/ui/_app.py b/piker/ui/_app.py index 3fa9d1b4..c99e2866 100644 --- a/piker/ui/_app.py +++ b/piker/ui/_app.py @@ -177,6 +177,6 @@ def _main( run_qtractor( func=_async_main, args=(sym, brokernames, piker_loglevel), - main_widget=GodWidget, + main_widget_type=GodWidget, tractor_kwargs=tractor_kwargs, ) diff --git a/piker/ui/_chart.py b/piker/ui/_chart.py index dd52699f..e03f7fe0 100644 --- a/piker/ui/_chart.py +++ b/piker/ui/_chart.py @@ -91,6 +91,7 @@ class GodWidget(QWidget): ''' search: SearchWidget + mode_name: str = 'god' def __init__( @@ -135,6 +136,10 @@ class GodWidget(QWidget): self._widgets: dict[str, QWidget] = {} self._resizing: bool = False + # TODO: do we need this, when would god get resized + # and the window does not? Never right?! + # self.reg_for_resize(self) + @property def linkedsplits(self) -> LinkedSplits: return self.rt_linked @@ -203,11 +208,14 @@ class GodWidget(QWidget): if not self.vbox.isEmpty(): + qframe = self.hist_linked.chart.qframe + if qframe.sidepane is self.search: + qframe.hbox.removeWidget(self.search) + for linked in [self.rt_linked, self.hist_linked]: # XXX: this is CRITICAL especially with pixel buffer caching linked.hide() linked.unfocus() - # self.hist_linked.hide() # self.hist_linked.unfocus() # XXX: pretty sure we don't need this @@ -244,7 +252,6 @@ class GodWidget(QWidget): linked.show() linked.focus() - self.search.focus() await trio.sleep(0) else: @@ -279,6 +286,17 @@ class GodWidget(QWidget): # do nothing yah? chart.default_view() + # if a history chart instance is already up then + # set the search widget as its sidepane. + hist_chart = self.hist_linked.chart + if hist_chart: + hist_chart.qframe.set_sidepane(self.search) + + # NOTE: this resizes the fast chart as well as all it's + # downstream fsp subcharts AND the slow chart which is part of + # the same splitter. + self.rt_linked.set_split_sizes() + # set window titlebar info symbol = self.rt_linked.symbol if symbol is not None: @@ -297,11 +315,23 @@ class GodWidget(QWidget): ''' # go back to view-mode focus (aka chart focus) self.clearFocus() - self.linkedsplits.chart.setFocus() + chart = self.rt_linked.chart + if chart: + chart.setFocus() - def resizeEvent(self, event: QtCore.QEvent) -> None: + def reg_for_resize( + self, + widget: QWidget, + ) -> None: + getattr(widget, 'on_resize') + self._widgets[widget.mode_name] = widget + + def on_win_resize(self, event: QtCore.QEvent) -> None: ''' - Top level god widget resize handler. + Top level god widget handler from window (the real yaweh) resize + events such that any registered widgets which wish to be + notified are invoked using our pythonic `.on_resize()` method + api. Where we do UX magic to make things not suck B) @@ -317,6 +347,8 @@ class GodWidget(QWidget): self._resizing = False + # on_resize = on_win_resize + def get_cursor(self) -> Cursor: return self._active_cursor @@ -336,9 +368,9 @@ class ChartnPane(QFrame): https://doc.qt.io/qt-5/qwidget.html#composite-widgets ''' - sidepane: FieldsForm + sidepane: FieldsForm | SearchWidget hbox: QHBoxLayout - chart: Optional['ChartPlotWidget'] = None + chart: Optional[ChartPlotWidget] = None def __init__( self, @@ -350,7 +382,7 @@ class ChartnPane(QFrame): super().__init__(parent) - self.sidepane = sidepane + self._sidepane = sidepane self.chart = None hbox = self.hbox = QHBoxLayout(self) @@ -360,7 +392,7 @@ class ChartnPane(QFrame): def set_sidepane( self, - sidepane: FieldsForm, + sidepane: FieldsForm | SearchWidget, ) -> None: # add sidepane **after** chart; place it on axis side @@ -368,6 +400,10 @@ class ChartnPane(QFrame): sidepane, alignment=Qt.AlignTop ) + self._sidepane = sidepane + + def sidepane(self) -> FieldsForm | SearchWidget: + return self._sidepane class LinkedSplits(QWidget): @@ -672,9 +708,6 @@ class LinkedSplits(QWidget): if qframe is not None: self.splitter.addWidget(qframe) - # scale split regions - self.set_split_sizes() - else: assert style == 'bar', 'main chart must be OHLC' @@ -694,6 +727,8 @@ class LinkedSplits(QWidget): anchor_at=anchor_at, ) + # scale split regions + self.set_split_sizes() self.resize_sidepanes() return cpw @@ -720,9 +755,6 @@ class LinkedSplits(QWidget): if from_linked: self.chart.sidepane.setMinimumWidth(sp_w) - self.chart.sidepane.setMaximumWidth(sp_w) - else: - self.godwidget.hist_linked.resize_sidepanes(from_linked=self) class ChartPlotWidget(pg.PlotWidget): diff --git a/piker/ui/_exec.py b/piker/ui/_exec.py index 1d1a9c3d..090b783a 100644 --- a/piker/ui/_exec.py +++ b/piker/ui/_exec.py @@ -20,13 +20,16 @@ Trio - Qt integration Run ``trio`` in guest mode on top of the Qt event loop. All global Qt runtime settings are mostly defined here. """ -from typing import Tuple, Callable, Dict, Any +from typing import ( + Callable, + Any, + Type, +) import platform import traceback # Qt specific import PyQt5 # noqa -import pyqtgraph as pg from pyqtgraph import QtGui from PyQt5 import QtCore # from PyQt5.QtGui import QLabel, QStatusBar @@ -37,7 +40,7 @@ from PyQt5.QtCore import ( ) import qdarkstyle from qdarkstyle import DarkPalette -# import qdarkgraystyle +# import qdarkgraystyle # TODO: play with it import trio from outcome import Error @@ -72,10 +75,11 @@ if platform.system() == "Windows": def run_qtractor( func: Callable, - args: Tuple, - main_widget: QtGui.QWidget, - tractor_kwargs: Dict[str, Any] = {}, + args: tuple, + main_widget_type: Type[QtGui.QWidget], + tractor_kwargs: dict[str, Any] = {}, window_type: QtGui.QMainWindow = None, + ) -> None: # avoids annoying message when entering debugger from qt loop pyqtRemoveInputHook() @@ -156,7 +160,7 @@ def run_qtractor( # hook into app focus change events app.focusChanged.connect(window.on_focus_change) - instance = main_widget() + instance = main_widget_type() instance.window = window # override tractor's defaults @@ -178,7 +182,7 @@ def run_qtractor( # restrict_keyboard_interrupt_to_checkpoints=True, ) - window.main_widget = main_widget + window.godwidget: GodWidget = instance window.setCentralWidget(instance) if is_windows: window.configure_to_desktop() diff --git a/piker/ui/_window.py b/piker/ui/_window.py index 6a39b0c5..5d720c0b 100644 --- a/piker/ui/_window.py +++ b/piker/ui/_window.py @@ -21,7 +21,11 @@ Qt main window singletons and stuff. import os import signal import time -from typing import Callable, Optional, Union +from typing import ( + Callable, + Optional, + Union, +) import uuid from pyqtgraph import QtGui @@ -30,6 +34,7 @@ from PyQt5.QtWidgets import QLabel, QStatusBar from ..log import get_logger from ._style import _font_small, hcolor +from ._chart import GodWidget log = get_logger(__name__) @@ -154,6 +159,7 @@ class MainWindow(QtGui.QMainWindow): # with the alloted window size. # TODO: detect for tiling and if untrue set some size? size = (300, 500) + godwidget: GodWidget title = 'piker chart (ur symbol is loading bby)' @@ -162,6 +168,9 @@ class MainWindow(QtGui.QMainWindow): # self.setMinimumSize(*self.size) self.setWindowTitle(self.title) + # set by runtime after `trio` is engaged. + self.godwidget: Optional[GodWidget] = None + self._status_bar: QStatusBar = None self._status_label: QLabel = None self._size: Optional[tuple[int, int]] = None @@ -248,9 +257,10 @@ class MainWindow(QtGui.QMainWindow): self.set_mode_name(name) def current_screen(self) -> QtGui.QScreen: - """Get a frickin screen (if we can, gawd). + ''' + Get a frickin screen (if we can, gawd). - """ + ''' app = QtGui.QApplication.instance() for _ in range(3): @@ -284,7 +294,7 @@ class MainWindow(QtGui.QMainWindow): ''' # https://stackoverflow.com/a/18975846 if not size and not self._size: - app = QtGui.QApplication.instance() + # app = QtGui.QApplication.instance() geo = self.current_screen().geometry() h, w = geo.height(), geo.width() # use approx 1/3 of the area of the screen by default @@ -292,6 +302,11 @@ class MainWindow(QtGui.QMainWindow): self.resize(*size or self._size) + def resizeEvent(self, event: QtCore.QEvent) -> None: + print('window resize') + # self.godwidget.resizeEvent(event) + self.godwidget.on_win_resize(event) + # singleton app per actor _qt_win: QtGui.QMainWindow = None