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