Add some new hotkey maps for chart zoom and pane hiding
parent
fff610fa8d
commit
94ebe1e87e
|
@ -406,6 +406,7 @@ class ChartnPane(QFrame):
|
|||
)
|
||||
self._sidepane = sidepane
|
||||
|
||||
@property
|
||||
def sidepane(self) -> FieldsForm | SearchWidget:
|
||||
return self._sidepane
|
||||
|
||||
|
@ -495,7 +496,7 @@ class LinkedSplits(QWidget):
|
|||
Set the proportion of space allocated for linked subcharts.
|
||||
|
||||
'''
|
||||
ln = len(self.subplots) or 1
|
||||
ln: int = len(self.subplots) or 1
|
||||
|
||||
# proportion allocated to consumer subcharts
|
||||
if not prop:
|
||||
|
@ -925,6 +926,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
|
||||
|
||||
# scene-local placeholder for book graphics
|
||||
# sizing to avoid overlap with data contents
|
||||
|
|
|
@ -21,7 +21,6 @@ Text entry "forms" widgets (mostly for configuration and UI user input).
|
|||
from __future__ import annotations
|
||||
from contextlib import asynccontextmanager
|
||||
from functools import partial
|
||||
from math import floor
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
|
|
|
@ -283,6 +283,7 @@ async def run_fsp_ui(
|
|||
name,
|
||||
array_key=array_key,
|
||||
)
|
||||
assert chart.qframe
|
||||
|
||||
chart.linked.focus()
|
||||
|
||||
|
|
|
@ -32,8 +32,18 @@ from typing import (
|
|||
import pyqtgraph as pg
|
||||
# from pyqtgraph.GraphicsScene import mouseEvents
|
||||
from PyQt5.QtWidgets import QGraphicsSceneMouseEvent as gs_mouse
|
||||
from PyQt5.QtCore import Qt, QEvent
|
||||
from pyqtgraph import ViewBox, Point, QtCore
|
||||
from PyQt5.QtGui import (
|
||||
QWheelEvent,
|
||||
)
|
||||
from PyQt5.QtCore import (
|
||||
Qt,
|
||||
QEvent,
|
||||
)
|
||||
from pyqtgraph import (
|
||||
ViewBox,
|
||||
Point,
|
||||
QtCore,
|
||||
)
|
||||
from pyqtgraph import functions as fn
|
||||
import numpy as np
|
||||
import trio
|
||||
|
@ -50,8 +60,16 @@ from ._editors import SelectRect
|
|||
from . import _event
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ._chart import ChartPlotWidget
|
||||
# from ._search import (
|
||||
# SearchWidget,
|
||||
# )
|
||||
from ._chart import (
|
||||
ChartnPane,
|
||||
ChartPlotWidget,
|
||||
GodWidget,
|
||||
)
|
||||
from ._dataviz import Viz
|
||||
from .order_mode import OrderMode
|
||||
|
||||
|
||||
log = get_logger(__name__)
|
||||
|
@ -83,7 +101,8 @@ async def handle_viewmode_kb_inputs(
|
|||
|
||||
) -> None:
|
||||
|
||||
order_mode = view.order_mode
|
||||
order_mode: OrderMode = view.order_mode
|
||||
godw: GodWidget = order_mode.godw # noqa
|
||||
|
||||
# track edge triggered keys
|
||||
# (https://en.wikipedia.org/wiki/Interrupt#Triggering_methods)
|
||||
|
@ -147,14 +166,14 @@ async def handle_viewmode_kb_inputs(
|
|||
if mods == Qt.ControlModifier:
|
||||
ctrl = True
|
||||
|
||||
# UI REPL-shell
|
||||
# UI REPL-shell, with ctrl-p (for "pause")
|
||||
if (
|
||||
ctrl and key in {
|
||||
Qt.Key_U,
|
||||
ctrl
|
||||
and key in {
|
||||
Qt.Key_P,
|
||||
}
|
||||
):
|
||||
import tractor
|
||||
god = order_mode.godw # noqa
|
||||
feed = order_mode.feed # noqa
|
||||
chart = order_mode.chart # noqa
|
||||
viz = chart.main_viz # noqa
|
||||
|
@ -167,9 +186,10 @@ async def handle_viewmode_kb_inputs(
|
|||
# SEARCH MODE #
|
||||
# ctlr-<space>/<l> for "lookup", "search" -> open search tree
|
||||
if (
|
||||
ctrl and key in {
|
||||
ctrl
|
||||
and key in {
|
||||
Qt.Key_L,
|
||||
Qt.Key_Space,
|
||||
# Qt.Key_Space,
|
||||
}
|
||||
):
|
||||
godw = view._chart.linked.godwidget
|
||||
|
@ -177,19 +197,53 @@ async def handle_viewmode_kb_inputs(
|
|||
godw.search.focus()
|
||||
|
||||
# esc and ctrl-c
|
||||
if key == Qt.Key_Escape or (ctrl and key == Qt.Key_C):
|
||||
if (
|
||||
key == Qt.Key_Escape
|
||||
or (
|
||||
ctrl
|
||||
and key == Qt.Key_C
|
||||
)
|
||||
):
|
||||
# ctrl-c as cancel
|
||||
# https://forum.qt.io/topic/532/how-to-catch-ctrl-c-on-a-widget/9
|
||||
view.select_box.clear()
|
||||
view.linked.focus()
|
||||
|
||||
# cancel order or clear graphics
|
||||
if key == Qt.Key_C or key == Qt.Key_Delete:
|
||||
if (
|
||||
key == Qt.Key_C
|
||||
or key == Qt.Key_Delete
|
||||
):
|
||||
|
||||
order_mode.cancel_orders_under_cursor()
|
||||
|
||||
# View modes
|
||||
if key == Qt.Key_R:
|
||||
if (
|
||||
ctrl
|
||||
and (
|
||||
key == Qt.Key_Equal
|
||||
or key == Qt.Key_I
|
||||
)
|
||||
):
|
||||
view.wheelEvent(
|
||||
ev=None,
|
||||
axis=None,
|
||||
delta=view.def_delta,
|
||||
)
|
||||
elif (
|
||||
ctrl
|
||||
and (
|
||||
key == Qt.Key_Minus
|
||||
or key == Qt.Key_O
|
||||
)
|
||||
):
|
||||
view.wheelEvent(
|
||||
ev=None,
|
||||
axis=None,
|
||||
delta=-view.def_delta,
|
||||
)
|
||||
|
||||
elif key == Qt.Key_R:
|
||||
|
||||
# NOTE: seems that if we don't yield a Qt render
|
||||
# cycle then the m4 downsampled curves will show here
|
||||
|
@ -235,15 +289,47 @@ async def handle_viewmode_kb_inputs(
|
|||
|
||||
# Toggle position config pane
|
||||
if (
|
||||
ctrl and key in {
|
||||
Qt.Key_P,
|
||||
ctrl
|
||||
and key in {
|
||||
Qt.Key_Space,
|
||||
}
|
||||
):
|
||||
pp_pane = order_mode.current_pp.pane
|
||||
if pp_pane.isHidden():
|
||||
pp_pane.show()
|
||||
# searchw: SearchWidget = godw.search
|
||||
# pp_pane = order_mode.current_pp.pane
|
||||
qframes: list[ChartnPane] = []
|
||||
|
||||
for linked in (
|
||||
godw.rt_linked,
|
||||
godw.hist_linked,
|
||||
):
|
||||
for chartw in (
|
||||
[linked.chart]
|
||||
+
|
||||
list(linked.subplots.values())
|
||||
):
|
||||
qframes.append(
|
||||
chartw.qframe
|
||||
)
|
||||
|
||||
# NOTE: place priority on FIRST hiding all
|
||||
# panes before showing them.
|
||||
# TODO: make this more "fancy"?
|
||||
# - maybe look at majority of hidden states and then
|
||||
# flip based on that?
|
||||
# - move these loops into the chart APIs?
|
||||
# - store the UX-state for a given feed/symbol and
|
||||
# apply when opening a new one (eg. if panes were
|
||||
# hidden then also hide them on newly loaded mkt
|
||||
# feeds).
|
||||
if not any(
|
||||
qf.sidepane.isHidden() for qf in qframes
|
||||
):
|
||||
for qf in qframes:
|
||||
qf.sidepane.hide()
|
||||
|
||||
else:
|
||||
pp_pane.hide()
|
||||
for qf in qframes:
|
||||
qf.sidepane.show()
|
||||
|
||||
# ORDER MODE
|
||||
# ----------
|
||||
|
@ -378,6 +464,8 @@ class ChartView(ViewBox):
|
|||
|
||||
'''
|
||||
mode_name: str = 'view'
|
||||
def_delta: float = 616 * 6
|
||||
def_scale_factor: float = 1.016 ** (def_delta * -1 / 20)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
@ -502,8 +590,9 @@ class ChartView(ViewBox):
|
|||
|
||||
def wheelEvent(
|
||||
self,
|
||||
ev,
|
||||
axis=None,
|
||||
ev: QWheelEvent | None = None,
|
||||
axis: int | None = None,
|
||||
delta: float | None = None,
|
||||
):
|
||||
'''
|
||||
Override "center-point" location for scrolling.
|
||||
|
@ -514,6 +603,12 @@ class ChartView(ViewBox):
|
|||
TODO: PR a method into ``pyqtgraph`` to make this configurable
|
||||
|
||||
'''
|
||||
# NOTE: certain operations are only avail when this handler is
|
||||
# actually called on events.
|
||||
if ev is None:
|
||||
assert delta
|
||||
assert axis is None
|
||||
|
||||
linked = self.linked
|
||||
if (
|
||||
not linked
|
||||
|
@ -524,7 +619,7 @@ class ChartView(ViewBox):
|
|||
mask = [False, False]
|
||||
mask[axis] = self.state['mouseEnabled'][axis]
|
||||
else:
|
||||
mask = self.state['mouseEnabled'][:]
|
||||
mask: list[bool] = self.state['mouseEnabled'][:]
|
||||
|
||||
chart = self.linked.chart
|
||||
|
||||
|
@ -545,8 +640,15 @@ class ChartView(ViewBox):
|
|||
# return
|
||||
|
||||
# actual scaling factor
|
||||
s = 1.016 ** (ev.delta() * -1 / 20) # self.state['wheelScaleFactor'])
|
||||
s = [(None if m is False else s) for m in mask]
|
||||
delta: float = ev.delta() if ev else delta
|
||||
scale_factor: float = 1.016 ** (delta * -1 / 20)
|
||||
|
||||
# NOTE: if elem is False -> None meaning "do not scale that
|
||||
# axis".
|
||||
scales: list[float | bool] = [
|
||||
(None if m is False else scale_factor)
|
||||
for m in mask
|
||||
]
|
||||
|
||||
if (
|
||||
# zoom happened on axis
|
||||
|
@ -569,7 +671,7 @@ class ChartView(ViewBox):
|
|||
).map(ev.pos())
|
||||
)
|
||||
# scale_y = 1.3 ** (center.y() * -1 / 20)
|
||||
self.scaleBy(s, center)
|
||||
self.scaleBy(scales, center)
|
||||
|
||||
# zoom in view-box area
|
||||
else:
|
||||
|
@ -584,7 +686,7 @@ class ChartView(ViewBox):
|
|||
|
||||
# NOTE: scroll "around" the right most datum-element in view
|
||||
# gives the feeling of staying "pinned" in place.
|
||||
self.scaleBy(s, focal)
|
||||
self.scaleBy(scales, focal)
|
||||
|
||||
# XXX: the order of the next 2 lines i'm pretty sure
|
||||
# matters, we want the resize to trigger before the graphics
|
||||
|
@ -604,7 +706,8 @@ class ChartView(ViewBox):
|
|||
self.interact_graphics_cycle()
|
||||
self.interact_graphics_cycle()
|
||||
|
||||
ev.accept()
|
||||
if ev:
|
||||
ev.accept()
|
||||
|
||||
def mouseDragEvent(
|
||||
self,
|
||||
|
|
Loading…
Reference in New Issue