Add `Viz.reset_graphics()` for "force re-render"
Since we're now using it multiple layers probably makes sense to impl and wrap it more correctly / publicly. The main (recent) use case is where editing an underlying time series and then wanting to refresh the graphics layers to reflect the changes in a chart. Part of this also obviously includes wiping the y-range mx/mn cache. Also ensure that `force_redraw` is proxying through to any `BarItems` via the new `render_baritems()` func kwarg even when switching between downsampled-line vs. bars modes.distribute_dis
parent
661805695e
commit
a7ad50cf8f
|
@ -36,6 +36,9 @@ from msgspec import (
|
||||||
field,
|
field,
|
||||||
)
|
)
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from numpy import (
|
||||||
|
ndarray,
|
||||||
|
)
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
from PyQt5.QtCore import QLineF
|
from PyQt5.QtCore import QLineF
|
||||||
|
|
||||||
|
@ -82,10 +85,11 @@ def render_baritems(
|
||||||
viz: Viz,
|
viz: Viz,
|
||||||
graphics: BarItems,
|
graphics: BarItems,
|
||||||
read: tuple[
|
read: tuple[
|
||||||
int, int, np.ndarray,
|
int, int, ndarray,
|
||||||
int, int, np.ndarray,
|
int, int, ndarray,
|
||||||
],
|
],
|
||||||
profiler: Profiler,
|
profiler: Profiler,
|
||||||
|
force_redraw: bool = False,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
|
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -216,9 +220,11 @@ def render_baritems(
|
||||||
viz._in_ds = should_line
|
viz._in_ds = should_line
|
||||||
|
|
||||||
should_redraw = (
|
should_redraw = (
|
||||||
changed_to_line
|
force_redraw
|
||||||
|
or changed_to_line
|
||||||
or not should_line
|
or not should_line
|
||||||
)
|
)
|
||||||
|
# print(f'should_redraw: {should_redraw}')
|
||||||
return (
|
return (
|
||||||
graphics,
|
graphics,
|
||||||
r,
|
r,
|
||||||
|
@ -250,7 +256,7 @@ class ViewState(Struct):
|
||||||
] | None = None
|
] | None = None
|
||||||
|
|
||||||
# last in view ``ShmArray.array[read_slc]`` data
|
# last in view ``ShmArray.array[read_slc]`` data
|
||||||
in_view: np.ndarray | None = None
|
in_view: ndarray | None = None
|
||||||
|
|
||||||
|
|
||||||
class Viz(Struct):
|
class Viz(Struct):
|
||||||
|
@ -375,11 +381,11 @@ class Viz(Struct):
|
||||||
self._index_step is None
|
self._index_step is None
|
||||||
or index_field is not None
|
or index_field is not None
|
||||||
):
|
):
|
||||||
index: np.ndarray = self.shm.array[
|
index: ndarray = self.shm.array[
|
||||||
index_field
|
index_field
|
||||||
or self.index_field
|
or self.index_field
|
||||||
]
|
]
|
||||||
isample: np.ndarray = index[-16:]
|
isample: ndarray = index[-16:]
|
||||||
|
|
||||||
mxdiff: None | float = None
|
mxdiff: None | float = None
|
||||||
for step in np.diff(isample):
|
for step in np.diff(isample):
|
||||||
|
@ -430,6 +436,9 @@ class Viz(Struct):
|
||||||
i_read_range: tuple[int, int] | None = None,
|
i_read_range: tuple[int, int] | None = None,
|
||||||
use_caching: bool = True,
|
use_caching: bool = True,
|
||||||
|
|
||||||
|
# XXX: internal debug
|
||||||
|
_do_print: bool = False
|
||||||
|
|
||||||
) -> tuple[float, float] | None:
|
) -> tuple[float, float] | None:
|
||||||
'''
|
'''
|
||||||
Compute the cached max and min y-range values for a given
|
Compute the cached max and min y-range values for a given
|
||||||
|
@ -449,15 +458,14 @@ class Viz(Struct):
|
||||||
if shm is None:
|
if shm is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
do_print: bool = False
|
arr: ndarray = shm.array
|
||||||
arr = shm.array
|
|
||||||
|
|
||||||
if i_read_range is not None:
|
if i_read_range is not None:
|
||||||
read_slc = slice(*i_read_range)
|
read_slc = slice(*i_read_range)
|
||||||
index = arr[read_slc][self.index_field]
|
index: float | int = arr[read_slc][self.index_field]
|
||||||
if not index.size:
|
if not index.size:
|
||||||
return None
|
return None
|
||||||
ixrng = (index[0], index[-1])
|
ixrng: tuple[int, int] = (index[0], index[-1])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if x_range is None:
|
if x_range is None:
|
||||||
|
@ -475,15 +483,24 @@ class Viz(Struct):
|
||||||
|
|
||||||
# TODO: hash the slice instead maybe?
|
# TODO: hash the slice instead maybe?
|
||||||
# https://stackoverflow.com/a/29980872
|
# https://stackoverflow.com/a/29980872
|
||||||
ixrng = lbar, rbar = round(x_range[0]), round(x_range[1])
|
ixrng = lbar, rbar = (
|
||||||
|
round(x_range[0]),
|
||||||
|
round(x_range[1]),
|
||||||
|
)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
use_caching
|
use_caching
|
||||||
and self._mxmn_cache_enabled
|
and self._mxmn_cache_enabled
|
||||||
):
|
):
|
||||||
|
# TODO: is there a way to ONLY clear ranges containing
|
||||||
|
# a certain sub-range?
|
||||||
|
# -[ ] currently we have a problem where a previously
|
||||||
|
# cached mxmn will persist even if the viz is "hard
|
||||||
|
# re-rendered" (usually bc underlying data was
|
||||||
|
# corrected)
|
||||||
cached_result = self._mxmns.get(ixrng)
|
cached_result = self._mxmns.get(ixrng)
|
||||||
if cached_result:
|
if cached_result:
|
||||||
if do_print:
|
if _do_print:
|
||||||
print(
|
print(
|
||||||
f'{self.name} CACHED maxmin\n'
|
f'{self.name} CACHED maxmin\n'
|
||||||
f'{ixrng} -> {cached_result}'
|
f'{ixrng} -> {cached_result}'
|
||||||
|
@ -513,7 +530,7 @@ class Viz(Struct):
|
||||||
(rbar - ifirst) + 1
|
(rbar - ifirst) + 1
|
||||||
)
|
)
|
||||||
|
|
||||||
slice_view = arr[read_slc]
|
slice_view: ndarray = arr[read_slc]
|
||||||
|
|
||||||
if not slice_view.size:
|
if not slice_view.size:
|
||||||
log.warning(
|
log.warning(
|
||||||
|
@ -524,7 +541,7 @@ class Viz(Struct):
|
||||||
|
|
||||||
elif self.ds_yrange:
|
elif self.ds_yrange:
|
||||||
mxmn = self.ds_yrange
|
mxmn = self.ds_yrange
|
||||||
if do_print:
|
if _do_print:
|
||||||
print(
|
print(
|
||||||
f'{self.name} M4 maxmin:\n'
|
f'{self.name} M4 maxmin:\n'
|
||||||
f'{ixrng} -> {mxmn}'
|
f'{ixrng} -> {mxmn}'
|
||||||
|
@ -541,7 +558,7 @@ class Viz(Struct):
|
||||||
|
|
||||||
mxmn = ylow, yhigh
|
mxmn = ylow, yhigh
|
||||||
if (
|
if (
|
||||||
do_print
|
_do_print
|
||||||
):
|
):
|
||||||
s = 3
|
s = 3
|
||||||
print(
|
print(
|
||||||
|
@ -555,14 +572,23 @@ class Viz(Struct):
|
||||||
|
|
||||||
# cache result for input range
|
# cache result for input range
|
||||||
ylow, yhi = mxmn
|
ylow, yhi = mxmn
|
||||||
|
diff: float = yhi - ylow
|
||||||
|
|
||||||
|
# order-of-magnitude check
|
||||||
|
# TODO: really we should be checking the hi or low
|
||||||
|
# against the previous sample to catch stuff like,
|
||||||
|
# - rando stock (reverse-)split
|
||||||
|
# - null-segments written by some prior
|
||||||
|
# crash-during-backfil
|
||||||
|
if diff > 0:
|
||||||
|
omg: float = abs(logf(diff, 10))
|
||||||
|
else:
|
||||||
|
omg: float = 0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
prolly_anomaly: bool = (
|
prolly_anomaly: bool = (
|
||||||
(
|
# diff == 0
|
||||||
abs(logf(ylow, 10)) > 16
|
(ylow and omg > 10)
|
||||||
if ylow
|
|
||||||
else False
|
|
||||||
)
|
|
||||||
or (
|
or (
|
||||||
isnan(ylow) or isnan(yhi)
|
isnan(ylow) or isnan(yhi)
|
||||||
)
|
)
|
||||||
|
@ -603,7 +629,7 @@ class Viz(Struct):
|
||||||
self,
|
self,
|
||||||
view_range: None | tuple[float, float] = None,
|
view_range: None | tuple[float, float] = None,
|
||||||
index_field: str | None = None,
|
index_field: str | None = None,
|
||||||
array: np.ndarray | None = None,
|
array: ndarray | None = None,
|
||||||
|
|
||||||
) -> tuple[
|
) -> tuple[
|
||||||
int, int, int, int, int, int
|
int, int, int, int, int, int
|
||||||
|
@ -674,8 +700,8 @@ class Viz(Struct):
|
||||||
profiler: None | Profiler = None,
|
profiler: None | Profiler = None,
|
||||||
|
|
||||||
) -> tuple[
|
) -> tuple[
|
||||||
int, int, np.ndarray,
|
int, int, ndarray,
|
||||||
int, int, np.ndarray,
|
int, int, ndarray,
|
||||||
]:
|
]:
|
||||||
'''
|
'''
|
||||||
Read the underlying shm array buffer and
|
Read the underlying shm array buffer and
|
||||||
|
@ -845,6 +871,10 @@ class Viz(Struct):
|
||||||
graphics,
|
graphics,
|
||||||
read,
|
read,
|
||||||
profiler,
|
profiler,
|
||||||
|
|
||||||
|
# NOTE: only set when caller says to
|
||||||
|
force_redraw=should_redraw,
|
||||||
|
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1007,6 +1037,39 @@ class Viz(Struct):
|
||||||
graphics,
|
graphics,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def reset_graphics(
|
||||||
|
self,
|
||||||
|
|
||||||
|
# TODO: allow only resetting within some x-domain range?
|
||||||
|
# ixrng: tuple[int, int] | None = None,
|
||||||
|
|
||||||
|
) -> None:
|
||||||
|
'''
|
||||||
|
Hard reset all graphics (rendering) layers for this
|
||||||
|
data viz including clearing the mxmn auto-y-range
|
||||||
|
cache.
|
||||||
|
|
||||||
|
Normally called when the underlying data set is modified
|
||||||
|
(probably by some `.tsp` correcting/editing routine) and
|
||||||
|
the (now cached) graphics need to be fully re-rendered from
|
||||||
|
source.
|
||||||
|
|
||||||
|
'''
|
||||||
|
log.warning(
|
||||||
|
f'Forcing hard Viz graphihcs RESET:\n'
|
||||||
|
f'.name: {self.name}\n'
|
||||||
|
f'.index_field: {self.index_field}\n'
|
||||||
|
f'.index_step(): {self.index_step()}\n'
|
||||||
|
f'.time_step(): {self.time_step()}\n'
|
||||||
|
)
|
||||||
|
# XXX: always clear the mxn y-range cache
|
||||||
|
# to avoid old data (anomalies) from being
|
||||||
|
# retained in auto-yrange output.
|
||||||
|
self._mxmn_cache_enabled = False
|
||||||
|
self._mxmns.clear()
|
||||||
|
self.update_graphics(force_redraw=True)
|
||||||
|
self._mxmn_cache_enabled = True
|
||||||
|
|
||||||
def draw_last(
|
def draw_last(
|
||||||
self,
|
self,
|
||||||
array_key: str | None = None,
|
array_key: str | None = None,
|
||||||
|
@ -1099,7 +1162,7 @@ class Viz(Struct):
|
||||||
|
|
||||||
'''
|
'''
|
||||||
shm: ShmArray = self.shm
|
shm: ShmArray = self.shm
|
||||||
array: np.ndarray = shm.array
|
array: ndarray = shm.array
|
||||||
view: ChartView = self.plot.vb
|
view: ChartView = self.plot.vb
|
||||||
(
|
(
|
||||||
vl,
|
vl,
|
||||||
|
|
Loading…
Reference in New Issue