Disable coordinate caching during interaction
This finally seems to mitigate all the "smearing" and "jitter" artifacts when using Qt's "coordinate cache" graphics-mode: - whenever we're in a mouse interaction (as per calls to `ChartView.start/signal_ic()`) we simply disable the caching mode (set `.NoCache` until the interaction is complete. - only do this (for now) during a pan since it doesn't seem to be an issue when zooming? - ensure disabling all `Viz.graphics` and `.ds_graphics` to be agnostic to any case where there's both a zoom and a pan simultaneously (not that it's easy to do manually XD) as well as solving the problem whenever an OHLC series is in traced-and-downsampled mode (during low zoom). Impl deatz: - rename `ChartView._ic` -> `._in_interact: trio.Event` - add `.ChartView._interact_stack: ExitStack` which we use to open. and close the `FlowGraphics.reset_cache()` mngrs from mouse handlers. - drop all the commented per-subtype overrides for `.cache_mode: int`. - write up much better doc strings for `FlattenedOHLC` and `StepCurve` including some very basic ASCII-art diagrams.storage_cli
parent
e0dd8ae3cf
commit
eecae69076
|
@ -163,22 +163,32 @@ class FlowGraphic(pg.GraphicsObject):
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# XXX: due to a variety of weird jitter bugs and "smearing"
|
||||||
|
# artifacts when click-drag panning and viewing history time series,
|
||||||
|
# we offer this ctx-mngr interface to allow temporarily disabling
|
||||||
|
# Qt's graphics caching mode; this is now currently used from
|
||||||
|
# ``ChartView.start/signal_ic()`` methods which also disable the
|
||||||
|
# rt-display loop when the user is moving around a view.
|
||||||
@cm
|
@cm
|
||||||
def reset_cache(self) -> None:
|
def reset_cache(self) -> None:
|
||||||
self.setCacheMode(QtWidgets.QGraphicsItem.NoCache)
|
|
||||||
try:
|
try:
|
||||||
log.debug(f'{self._name} -> CACHE DISABLE')
|
none = QGraphicsItem.NoCache
|
||||||
|
log.debug(
|
||||||
|
f'{self._name} -> CACHE DISABLE: {none}'
|
||||||
|
)
|
||||||
|
self.setCacheMode(none)
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
log.debug(f'{self._name} -> CACHE ENABLE')
|
mode = self.cache_mode
|
||||||
self.setCacheMode(self.cache_mode)
|
log.debug(f'{self._name} -> CACHE ENABLE {mode}')
|
||||||
|
self.setCacheMode(mode)
|
||||||
|
|
||||||
|
|
||||||
class Curve(FlowGraphic):
|
class Curve(FlowGraphic):
|
||||||
'''
|
'''
|
||||||
A faster, simpler, append friendly version of
|
A faster, simpler, append friendly version of
|
||||||
``pyqtgraph.PlotCurveItem`` built for highly customizable real-time
|
``pyqtgraph.PlotCurveItem`` built for highly customizable real-time
|
||||||
updates.
|
updates; a graphics object to render a simple "line" plot.
|
||||||
|
|
||||||
This type is a much stripped down version of a ``pyqtgraph`` style
|
This type is a much stripped down version of a ``pyqtgraph`` style
|
||||||
"graphics object" in the sense that the internal lower level
|
"graphics object" in the sense that the internal lower level
|
||||||
|
@ -385,7 +395,6 @@ class Curve(FlowGraphic):
|
||||||
|
|
||||||
) -> None:
|
) -> None:
|
||||||
# default line draw last call
|
# default line draw last call
|
||||||
# with self.reset_cache():
|
|
||||||
x = src_data[index_field]
|
x = src_data[index_field]
|
||||||
y = src_data[array_key]
|
y = src_data[array_key]
|
||||||
|
|
||||||
|
@ -413,12 +422,20 @@ class Curve(FlowGraphic):
|
||||||
# element such that the current datum in view can be shown
|
# element such that the current datum in view can be shown
|
||||||
# (via it's max / min) even when highly zoomed out.
|
# (via it's max / min) even when highly zoomed out.
|
||||||
class FlattenedOHLC(Curve):
|
class FlattenedOHLC(Curve):
|
||||||
|
'''
|
||||||
|
More or less the exact same as a standard line ``Curve`` above
|
||||||
|
but meant to handle a traced-and-downsampled OHLC time series.
|
||||||
|
_
|
||||||
|
_| | _
|
||||||
|
|_ | |_ | |
|
||||||
|
_| => |_| |
|
||||||
|
| |
|
||||||
|
|_ |_
|
||||||
|
|
||||||
# avoids strange dragging/smearing artifacts when panning
|
The main implementation different is that ``.draw_last_datum()``
|
||||||
# as well as mouse over artefacts when the vlm chart series
|
expects an underlying OHLC array for the ``src_data`` input.
|
||||||
# is "shorter" then some overlay..
|
|
||||||
# cache_mode: int = QGraphicsItem.NoCache
|
|
||||||
|
|
||||||
|
'''
|
||||||
def draw_last_datum(
|
def draw_last_datum(
|
||||||
self,
|
self,
|
||||||
path: QPainterPath,
|
path: QPainterPath,
|
||||||
|
@ -443,12 +460,19 @@ class FlattenedOHLC(Curve):
|
||||||
|
|
||||||
|
|
||||||
class StepCurve(Curve):
|
class StepCurve(Curve):
|
||||||
|
'''
|
||||||
|
A familiar rectangle-with-y-height-per-datum type curve:
|
||||||
|
|
||||||
# avoids strange dragging/smearing artifacts when panning
|
||
|
||||||
# as well as mouse over artefacts when the vlm chart series
|
|| ||
|
||||||
# is "shorter" then some overlay..
|
|| || ||||
|
||||||
# cache_mode: int = QGraphicsItem.NoCache
|
_||_||_||_||||_ where each datum's y-value is drawn as
|
||||||
|
a nearly full rectangle, each "level" spans some x-step size.
|
||||||
|
|
||||||
|
This is most often used for vlm and option OI style curves and/or
|
||||||
|
the very popular "bar chart".
|
||||||
|
|
||||||
|
'''
|
||||||
def declare_paintables(
|
def declare_paintables(
|
||||||
self,
|
self,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
|
@ -473,7 +473,7 @@ async def graphics_update_loop(
|
||||||
fast_chart.pause_all_feeds()
|
fast_chart.pause_all_feeds()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
ic = fast_chart.view._ic
|
ic = fast_chart.view._in_interact
|
||||||
if ic:
|
if ic:
|
||||||
fast_chart.pause_all_feeds()
|
fast_chart.pause_all_feeds()
|
||||||
print(f'{fqsn} PAUSING DURING INTERACTION')
|
print(f'{fqsn} PAUSING DURING INTERACTION')
|
||||||
|
@ -756,8 +756,8 @@ def graphics_update_cycle(
|
||||||
mx = max(mx, lmx)
|
mx = max(mx, lmx)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
main_vb._ic is None
|
main_vb._in_interact is None
|
||||||
or not main_vb._ic.is_set()
|
or not main_vb._in_interact.is_set()
|
||||||
):
|
):
|
||||||
# print(f'SETTING Y-mnmx -> {main_viz.name}: {(mn, mx)}')
|
# print(f'SETTING Y-mnmx -> {main_viz.name}: {(mn, mx)}')
|
||||||
this_vb.interact_graphics_cycle(
|
this_vb.interact_graphics_cycle(
|
||||||
|
|
|
@ -19,7 +19,10 @@ Chart view box primitives
|
||||||
|
|
||||||
'''
|
'''
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import (
|
||||||
|
asynccontextmanager,
|
||||||
|
ExitStack,
|
||||||
|
)
|
||||||
import time
|
import time
|
||||||
from typing import (
|
from typing import (
|
||||||
Callable,
|
Callable,
|
||||||
|
@ -405,7 +408,8 @@ class ChartView(ViewBox):
|
||||||
self.order_mode: bool = False
|
self.order_mode: bool = False
|
||||||
|
|
||||||
self.setFocusPolicy(QtCore.Qt.StrongFocus)
|
self.setFocusPolicy(QtCore.Qt.StrongFocus)
|
||||||
self._ic = None
|
self._in_interact: trio.Event | None = None
|
||||||
|
self._interact_stack: ExitStack = ExitStack()
|
||||||
|
|
||||||
# TODO: probably just assign this whenever a new `PlotItem` is
|
# TODO: probably just assign this whenever a new `PlotItem` is
|
||||||
# allocated since they're 1to1 with views..
|
# allocated since they're 1to1 with views..
|
||||||
|
@ -420,10 +424,20 @@ class ChartView(ViewBox):
|
||||||
to any interested task waiters.
|
to any interested task waiters.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
if self._ic is None:
|
if self._in_interact is None:
|
||||||
|
chart = self.chart
|
||||||
try:
|
try:
|
||||||
self.chart.pause_all_feeds()
|
chart.pause_all_feeds()
|
||||||
self._ic = trio.Event()
|
self._in_interact = trio.Event()
|
||||||
|
for viz in chart.iter_vizs():
|
||||||
|
self._interact_stack.enter_context(
|
||||||
|
viz.graphics.reset_cache(),
|
||||||
|
)
|
||||||
|
dsg = viz.ds_graphics
|
||||||
|
if dsg:
|
||||||
|
self._interact_stack.enter_context(
|
||||||
|
dsg.reset_cache(),
|
||||||
|
)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -437,10 +451,11 @@ class ChartView(ViewBox):
|
||||||
to any waiters.
|
to any waiters.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
if self._ic:
|
if self._in_interact:
|
||||||
try:
|
try:
|
||||||
self._ic.set()
|
self._in_interact.set()
|
||||||
self._ic = None
|
self._in_interact = None
|
||||||
|
self._interact_stack.close()
|
||||||
self.chart.resume_all_feeds()
|
self.chart.resume_all_feeds()
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
pass
|
pass
|
||||||
|
@ -667,9 +682,6 @@ class ChartView(ViewBox):
|
||||||
self.start_ic()
|
self.start_ic()
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
pass
|
pass
|
||||||
# if self._ic is None:
|
|
||||||
# self.chart.pause_all_feeds()
|
|
||||||
# self._ic = trio.Event()
|
|
||||||
|
|
||||||
if axis == 1:
|
if axis == 1:
|
||||||
self.chart._static_yrange = 'axis'
|
self.chart._static_yrange = 'axis'
|
||||||
|
@ -693,8 +705,8 @@ class ChartView(ViewBox):
|
||||||
|
|
||||||
if ev.isFinish():
|
if ev.isFinish():
|
||||||
self.signal_ic()
|
self.signal_ic()
|
||||||
# self._ic.set()
|
# self._in_interact.set()
|
||||||
# self._ic = None
|
# self._in_interact = None
|
||||||
# self.chart.resume_all_feeds()
|
# self.chart.resume_all_feeds()
|
||||||
|
|
||||||
# # XXX: WHY
|
# # XXX: WHY
|
||||||
|
|
|
@ -28,7 +28,6 @@ from PyQt5.QtCore import (
|
||||||
QLineF,
|
QLineF,
|
||||||
QRectF,
|
QRectF,
|
||||||
)
|
)
|
||||||
from PyQt5.QtWidgets import QGraphicsItem
|
|
||||||
from PyQt5.QtGui import QPainterPath
|
from PyQt5.QtGui import QPainterPath
|
||||||
|
|
||||||
from ._curve import FlowGraphic
|
from ._curve import FlowGraphic
|
||||||
|
@ -91,10 +90,6 @@ class BarItems(FlowGraphic):
|
||||||
"Price range" bars graphics rendered from a OHLC sampled sequence.
|
"Price range" bars graphics rendered from a OHLC sampled sequence.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
# XXX: causes this weird jitter bug when click-drag panning
|
|
||||||
# where the path curve will awkwardly flicker back and forth?
|
|
||||||
# cache_mode: int = QGraphicsItem.NoCache
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*args,
|
*args,
|
||||||
|
|
Loading…
Reference in New Issue