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
|
||||
|
||||
# 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
|
||||
def reset_cache(self) -> None:
|
||||
self.setCacheMode(QtWidgets.QGraphicsItem.NoCache)
|
||||
try:
|
||||
log.debug(f'{self._name} -> CACHE DISABLE')
|
||||
none = QGraphicsItem.NoCache
|
||||
log.debug(
|
||||
f'{self._name} -> CACHE DISABLE: {none}'
|
||||
)
|
||||
self.setCacheMode(none)
|
||||
yield
|
||||
finally:
|
||||
log.debug(f'{self._name} -> CACHE ENABLE')
|
||||
self.setCacheMode(self.cache_mode)
|
||||
mode = self.cache_mode
|
||||
log.debug(f'{self._name} -> CACHE ENABLE {mode}')
|
||||
self.setCacheMode(mode)
|
||||
|
||||
|
||||
class Curve(FlowGraphic):
|
||||
'''
|
||||
A faster, simpler, append friendly version of
|
||||
``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
|
||||
"graphics object" in the sense that the internal lower level
|
||||
|
@ -385,7 +395,6 @@ class Curve(FlowGraphic):
|
|||
|
||||
) -> None:
|
||||
# default line draw last call
|
||||
# with self.reset_cache():
|
||||
x = src_data[index_field]
|
||||
y = src_data[array_key]
|
||||
|
||||
|
@ -413,12 +422,20 @@ class Curve(FlowGraphic):
|
|||
# element such that the current datum in view can be shown
|
||||
# (via it's max / min) even when highly zoomed out.
|
||||
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
|
||||
# as well as mouse over artefacts when the vlm chart series
|
||||
# is "shorter" then some overlay..
|
||||
# cache_mode: int = QGraphicsItem.NoCache
|
||||
The main implementation different is that ``.draw_last_datum()``
|
||||
expects an underlying OHLC array for the ``src_data`` input.
|
||||
|
||||
'''
|
||||
def draw_last_datum(
|
||||
self,
|
||||
path: QPainterPath,
|
||||
|
@ -443,12 +460,19 @@ class FlattenedOHLC(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(
|
||||
self,
|
||||
) -> None:
|
||||
|
|
|
@ -473,7 +473,7 @@ async def graphics_update_loop(
|
|||
fast_chart.pause_all_feeds()
|
||||
continue
|
||||
|
||||
ic = fast_chart.view._ic
|
||||
ic = fast_chart.view._in_interact
|
||||
if ic:
|
||||
fast_chart.pause_all_feeds()
|
||||
print(f'{fqsn} PAUSING DURING INTERACTION')
|
||||
|
@ -756,8 +756,8 @@ def graphics_update_cycle(
|
|||
mx = max(mx, lmx)
|
||||
|
||||
if (
|
||||
main_vb._ic is None
|
||||
or not main_vb._ic.is_set()
|
||||
main_vb._in_interact is None
|
||||
or not main_vb._in_interact.is_set()
|
||||
):
|
||||
# print(f'SETTING Y-mnmx -> {main_viz.name}: {(mn, mx)}')
|
||||
this_vb.interact_graphics_cycle(
|
||||
|
|
|
@ -19,7 +19,10 @@ Chart view box primitives
|
|||
|
||||
'''
|
||||
from __future__ import annotations
|
||||
from contextlib import asynccontextmanager
|
||||
from contextlib import (
|
||||
asynccontextmanager,
|
||||
ExitStack,
|
||||
)
|
||||
import time
|
||||
from typing import (
|
||||
Callable,
|
||||
|
@ -405,7 +408,8 @@ class ChartView(ViewBox):
|
|||
self.order_mode: bool = False
|
||||
|
||||
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
|
||||
# allocated since they're 1to1 with views..
|
||||
|
@ -420,10 +424,20 @@ class ChartView(ViewBox):
|
|||
to any interested task waiters.
|
||||
|
||||
'''
|
||||
if self._ic is None:
|
||||
if self._in_interact is None:
|
||||
chart = self.chart
|
||||
try:
|
||||
self.chart.pause_all_feeds()
|
||||
self._ic = trio.Event()
|
||||
chart.pause_all_feeds()
|
||||
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:
|
||||
pass
|
||||
|
||||
|
@ -437,10 +451,11 @@ class ChartView(ViewBox):
|
|||
to any waiters.
|
||||
|
||||
'''
|
||||
if self._ic:
|
||||
if self._in_interact:
|
||||
try:
|
||||
self._ic.set()
|
||||
self._ic = None
|
||||
self._in_interact.set()
|
||||
self._in_interact = None
|
||||
self._interact_stack.close()
|
||||
self.chart.resume_all_feeds()
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
@ -667,9 +682,6 @@ class ChartView(ViewBox):
|
|||
self.start_ic()
|
||||
except RuntimeError:
|
||||
pass
|
||||
# if self._ic is None:
|
||||
# self.chart.pause_all_feeds()
|
||||
# self._ic = trio.Event()
|
||||
|
||||
if axis == 1:
|
||||
self.chart._static_yrange = 'axis'
|
||||
|
@ -693,8 +705,8 @@ class ChartView(ViewBox):
|
|||
|
||||
if ev.isFinish():
|
||||
self.signal_ic()
|
||||
# self._ic.set()
|
||||
# self._ic = None
|
||||
# self._in_interact.set()
|
||||
# self._in_interact = None
|
||||
# self.chart.resume_all_feeds()
|
||||
|
||||
# # XXX: WHY
|
||||
|
|
|
@ -28,7 +28,6 @@ from PyQt5.QtCore import (
|
|||
QLineF,
|
||||
QRectF,
|
||||
)
|
||||
from PyQt5.QtWidgets import QGraphicsItem
|
||||
from PyQt5.QtGui import QPainterPath
|
||||
|
||||
from ._curve import FlowGraphic
|
||||
|
@ -91,10 +90,6 @@ class BarItems(FlowGraphic):
|
|||
"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__(
|
||||
self,
|
||||
*args,
|
||||
|
|
Loading…
Reference in New Issue