Actually support resize events..

Turns out god widget resizes aren't triggered implicitly by window
resizes, so instead, hook into the window by moving what was our useless
method to that class. Further we explicitly define and declare that our
window has a `.godwidget: GodWidget` and set it up in the bootstrap
phase - in `run_qutractor()` during `trio` guest mode configuration.

Further deatz:
- retype the runtime/bootstrap routines to take a qwidget "type" not an
  instance, and drop the whole implicit `.main_widget` stuff.
- delegate into the `GodWidget.on_win_resize()` for any window resize
  which then triggers all the custom resize callbacks we already had in
  place.
- privatize `ChartnPane.sidepane` so that it can't be mutated willy
  nilly without calling `.set_sidepane()`.
- always adjust splitter sizes inside `LinkeSplits.add_plot()`.
history_view
Tyler Goodlet 2022-09-08 20:00:50 -04:00
parent 256bcf36d3
commit 40a9761943
4 changed files with 79 additions and 28 deletions

View File

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

View File

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

View File

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

View File

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