piker/piker/ui/_render.py

1084 lines
30 KiB
Python
Raw Normal View History

Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
# piker: trading gear for hackers
# Copyright (C) Tyler Goodlet (in stewardship for pikers)
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
High level streaming graphics primitives.
This is an intermediate layer which associates real-time low latency
graphics primitives with underlying FSP related data structures for fast
incremental update.
'''
2022-04-16 19:22:11 +00:00
from __future__ import annotations
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
from typing import (
Optional,
)
import msgspec
import numpy as np
import pyqtgraph as pg
from PyQt5.QtGui import QPainterPath
from PyQt5.QtCore import QLineF
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
from ..data._sharedmem import (
ShmArray,
)
from ..data.feed import Flume
from ..data._formatters import (
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
IncrementalFormatter,
OHLCBarsFmtr, # Plain OHLC renderer
OHLCBarsAsCurveFmtr, # OHLC converted to line
StepCurveFmtr, # "step" curve (like for vlm)
)
from ..data._pathops import xy_downsample
2022-04-16 19:22:11 +00:00
from ._ohlc import (
BarItems,
# bar_from_ohlc_row,
2022-04-16 19:22:11 +00:00
)
from ._curve import (
2022-06-02 22:11:59 +00:00
Curve,
StepCurve,
FlattenedOHLC,
2022-04-16 19:22:11 +00:00
)
from ..log import get_logger
from .._profile import (
Profiler,
pg_profile_enabled,
)
log = get_logger(__name__)
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
def render_baritems(
viz: Viz,
graphics: BarItems,
read: tuple[
int, int, np.ndarray,
int, int, np.ndarray,
],
profiler: Profiler,
**kwargs,
) -> None:
'''
Graphics management logic for a ``BarItems`` object.
Mostly just logic to determine when and how to downsample an OHLC
lines curve into a flattened line graphic and when to display one
graphic or the other.
TODO: this should likely be moved into some kind of better abstraction
layer, if not a `Renderer` then something just above it?
'''
bars = graphics
# if no source data renderer exists create one.
self = viz
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
show_bars: bool = False
r = self._src_r
if not r:
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
show_bars = True
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
# OHLC bars path renderer
r = self._src_r = Renderer(
viz=self,
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
fmtr=OHLCBarsFmtr(
shm=viz.shm,
viz=viz,
# index_field=viz.index_field,
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
),
)
ds_curve_r = Renderer(
viz=self,
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
fmtr=OHLCBarsAsCurveFmtr(
shm=viz.shm,
viz=viz,
# index_field=viz.index_field,
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
),
)
curve = FlattenedOHLC(
name=f'{viz.name}_ds_ohlc',
color=bars._color,
)
viz.ds_graphics = curve
curve.hide()
self.plot.addItem(curve)
# baseline "line" downsampled OHLC curve that should
# kick on only when we reach a certain uppx threshold.
self._render_table = (ds_curve_r, curve)
ds_r, curve = self._render_table
# print(
# f'r: {r.fmtr.xy_slice}\n'
# f'ds_r: {ds_r.fmtr.xy_slice}\n'
# )
# do checks for whether or not we require downsampling:
# - if we're **not** downsampling then we simply want to
# render the bars graphics curve and update..
# - if instead we are in a downsamplig state then we to
x_gt = 6
uppx = curve.x_uppx()
# print(f'BARS UPPX: {uppx}')
in_line = should_line = curve.isVisible()
if (
in_line
and uppx < x_gt
):
# print('FLIPPING TO BARS')
should_line = False
viz._in_ds = False
elif (
not in_line
and uppx >= x_gt
):
# print('FLIPPING TO LINE')
should_line = True
viz._in_ds = True
profiler(f'ds logic complete line={should_line}')
# do graphics updates
if should_line:
r = ds_r
graphics = curve
profiler('updated ds curve')
else:
graphics = bars
if show_bars:
bars.show()
changed_to_line = False
if (
not in_line
and should_line
):
# change to line graphic
log.info(
f'downsampling to line graphic {self.name}'
)
bars.hide()
curve.show()
curve.update()
changed_to_line = True
elif in_line and not should_line:
# change to bars graphic
log.info(f'showing bars graphic {self.name}')
curve.hide()
bars.show()
bars.update()
# breakpoint()
return (
graphics,
r,
{'read_from_key': False},
should_line,
changed_to_line,
)
class Viz(msgspec.Struct): # , frozen=True):
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
'''
(Data) "Visualization" compound type which wraps a real-time
shm array stream with displayed graphics (curves, charts)
for high level access and control as well as efficient incremental
update.
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
The intention is for this type to eventually be capable of shm-passing
of incrementally updated graphics stream data between actors.
'''
name: str
plot: pg.PlotItem
_shm: ShmArray
flume: Flume
graphics: Curve | BarItems
# for tracking y-mn/mx for y-axis auto-ranging
yrange: tuple[float, float] = None
# in some cases a viz may want to change its
# graphical "type" or, "form" when downsampling, to
# start this is only ever an interpolation line.
ds_graphics: Optional[Curve] = None
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
is_ohlc: bool = False
render: bool = True # toggle for display loop
_index_field: str = 'index'
# downsampling state
2022-04-16 19:22:11 +00:00
_last_uppx: float = 0
_in_ds: bool = False
# map from uppx -> (downsampled data, incremental graphics)
_src_r: Optional[Renderer] = None
2022-04-16 19:22:11 +00:00
_render_table: dict[
Optional[int],
tuple[Renderer, pg.GraphicsItem],
] = (None, None)
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
# TODO: hackery to be able to set a shm later
# but whilst also allowing this type to hashable,
# likely will require serializable token that is used to attach
# to the underlying shm ref after startup?
2022-04-16 19:22:11 +00:00
# _shm: Optional[ShmArray] = None # currently, may be filled in "later"
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
# cache of y-range values per x-range input.
_mxmns: dict[tuple[int, int], tuple[float, float]] = {}
@property
def shm(self) -> ShmArray:
return self._shm
@property
def index_field(self) -> str:
return self._index_field
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
def maxmin(
self,
lbar: int,
rbar: int,
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
2022-06-28 13:41:49 +00:00
) -> Optional[tuple[float, float]]:
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
'''
Compute the cached max and min y-range values for a given
2022-06-28 13:41:49 +00:00
x-range determined by ``lbar`` and ``rbar`` or ``None``
if no range can be determined (yet).
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
'''
# TODO: hash the slice instead maybe?
# https://stackoverflow.com/a/29980872
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
rkey = (lbar, rbar)
cached_result = self._mxmns.get(rkey)
if cached_result:
return cached_result
shm = self.shm
if shm is None:
2022-06-28 13:41:49 +00:00
return None
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
2022-06-28 13:41:49 +00:00
arr = shm.array
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
# get relative slice indexes into array
if self.index_field == 'time':
(
abs_slc,
read_slc,
mask,
) = self.flume.slice_from_time(
arr,
start_t=lbar,
stop_t=rbar,
)
slice_view = arr[mask]
else:
ifirst = arr[0]['index']
slice_view = arr[
lbar - ifirst:
(rbar - ifirst) + 1
]
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
2022-06-28 13:41:49 +00:00
if not slice_view.size:
return None
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
2022-06-28 13:41:49 +00:00
elif self.yrange:
mxmn = self.yrange
# print(f'{self.name} M4 maxmin: {mxmn}')
2022-06-28 13:41:49 +00:00
else:
if self.is_ohlc:
ylow = np.min(slice_view['low'])
yhigh = np.max(slice_view['high'])
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
2022-06-28 13:41:49 +00:00
else:
view = slice_view[self.name]
ylow = np.min(view)
yhigh = np.max(view)
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
2022-06-28 13:41:49 +00:00
mxmn = ylow, yhigh
# print(f'{self.name} MANUAL maxmin: {mxmn}')
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
2022-06-28 13:41:49 +00:00
# cache result for input range
assert mxmn
self._mxmns[rkey] = mxmn
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
2022-06-28 13:41:49 +00:00
return mxmn
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
def view_range(self) -> tuple[int, int]:
'''
Return the indexes in view for the associated
plot displaying this viz's data.
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
'''
vr = self.plot.viewRect()
return (
vr.left(),
vr.right(),
)
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
def bars_range(self) -> tuple[int, int, int, int]:
'''
Return a range tuple for the bars present in view.
'''
start, l, datum_start, datum_stop, r, stop = self.datums_range()
return l, datum_start, datum_stop, r
def datums_range(
self,
index_field: str | None = None,
) -> tuple[
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
int, int, int, int, int, int
]:
'''
Return a range tuple for the datums present in view.
'''
l, r = self.view_range()
index_field: str = index_field or self.index_field
if index_field == 'index':
l, r = round(l), round(r)
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
array = self.shm.array
index = array[index_field]
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
start = index[0]
stop = index[-1]
if (
l < 0
or r < l
or l < start
):
datum_start = start
datum_stop = stop
else:
datum_start = max(l, start)
datum_stop = r
if l < stop:
datum_stop = min(r, stop)
assert datum_start < datum_stop
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
return (
start,
l, # left x-in-view
datum_start,
datum_stop,
r, # right-x-in-view
stop,
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
)
def read(
self,
array_field: Optional[str] = None,
index_field: str | None = None,
profiler: None | Profiler = None,
) -> tuple[
int, int, np.ndarray,
int, int, np.ndarray,
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
]:
'''
Read the underlying shm array buffer and
return the data plus indexes for the first
and last
which has been written to.
'''
index_field: str = index_field or self.index_field
# readable data
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
array = self.shm.array
if profiler:
profiler('self.shm.array READ')
(
ifirst,
l,
lbar,
rbar,
r,
ilast,
) = self.datums_range(index_field=index_field)
# if rbar < lbar:
# breakpoint()
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
if profiler:
profiler('self.datums_range()')
abs_slc = slice(ifirst, ilast)
# TODO: support time slicing
if index_field == 'time':
(
abs_slc,
read_slc,
mask,
) = self.flume.slice_from_time(
array,
start_t=lbar,
stop_t=rbar,
)
in_view = array[read_slc]
# diff = rbar - lbar
# if (
# 'btc' in self.name
# and 'hist' not in self.shm.token
# ):
# print(
# f'{self.name}: len(iv) = {len(in_view)}\n'
# f'start/stop: {lbar},{rbar}\n',
# f'diff: {diff}\n',
# )
if profiler:
profiler(
'`Flume.slice_from_time('
f'start_t={lbar}'
f'stop_t={rbar})'
)
# array-index slicing
# TODO: can we do time based indexing using arithmetic presuming
# a uniform time stamp step size?
else:
# get read-relative indices adjusting for master shm index.
lbar_i = max(l, ifirst) - ifirst
rbar_i = min(r, ilast) - ifirst
# NOTE: the slice here does NOT include the extra ``+ 1``
# BUT the ``in_view`` slice DOES..
read_slc = slice(lbar_i, rbar_i)
in_view = array[lbar_i: rbar_i + 1]
# XXX: same as ^
# to_draw = array[lbar - ifirst:(rbar - ifirst) + 1]
if profiler:
profiler('index arithmetic for slicing')
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
if array_field:
array = array[array_field]
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
return (
# abs indices + full data set
abs_slc.start,
abs_slc.stop,
array,
# relative (read) indices + in view data
read_slc.start,
read_slc.stop,
in_view,
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
)
def update_graphics(
self,
use_vr: bool = True,
render: bool = True,
array_key: Optional[str] = None,
profiler: Optional[Profiler] = None,
do_append: bool = True,
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
**kwargs,
) -> pg.GraphicsObject:
'''
Read latest datums from shm and render to (incrementally)
render to graphics.
'''
profiler = Profiler(
msg=f'Viz.update_graphics() for {self.name}',
disabled=not pg_profile_enabled(),
ms_threshold=4,
# ms_threshold=ms_slower_then,
)
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
# shm read and slice to view
read = (
xfirst, xlast, src_array,
ivl, ivr, in_view,
) = self.read(profiler=profiler)
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
profiler('read src shm data')
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
graphics = self.graphics
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
if (
not in_view.size
or not render
):
# print('exiting early')
return graphics
Add new `ui._flows` module This begins the removal of data processing / analysis methods from the chart widget and instead moving them to our new `Flow` API (in the new module introduce here) and delegating the old chart methods to the respective internal flow. Most importantly is no longer storing the "last read" of an array from shm in an internal chart table (was `._arrays`) and instead the `ShmArray` instance is passed as input and stored in the `Flow` instance. This greatly simplifies lookup logic such that the display loop now doesn't have to worry about reading shm, it can be done by internal graphics logic as desired. Generally speaking, all previous `._arrays`/`._graphics` lookups are now delegated to the entries in the chart's `._flows` table. The new `Flow` methods are generally better factored and provide more detailed output regarding data-stream <-> graphics inter-relations for the future purpose of allowing much more efficient update calls in the display loop as well as supporting low latency interaction UX. The concept here is that we're introducing an intermediary layer that ties together graphics and real-time data flows such that widget code is oriented around plot layout and the flow apis are oriented around real-time low latency updates and providing an efficient high level metric layer for the UX. The summary api transition is something like: - `update_graphics_from_array()` -> `.update_graphics_from_flow()` - `.bars_range()` -> `Flow.datums_range()` - `.bars_range()` -> `Flow.datums_range()`
2022-04-14 13:38:25 +00:00
should_redraw: bool = False
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
should_line: bool = False
rkwargs = {}
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
# TODO: probably specialize ``Renderer`` types instead of
# these logic checks?
# - put these blocks into a `.load_renderer()` meth?
# - consider a OHLCRenderer, StepCurveRenderer, Renderer?
r = self._src_r
if isinstance(graphics, BarItems):
# XXX: special case where we change out graphics
# to a line after a certain uppx threshold.
(
graphics,
r,
rkwargs,
should_line,
changed_to_line,
) = render_baritems(
self,
graphics,
read,
profiler,
**kwargs,
)
should_redraw = changed_to_line or not should_line
self._in_ds = should_line
2022-04-16 19:22:11 +00:00
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
elif not r:
if isinstance(graphics, StepCurve):
r = self._src_r = Renderer(
viz=self,
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
fmtr=StepCurveFmtr(
shm=self.shm,
viz=self,
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
),
)
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
# TODO: append logic inside ``.render()`` isn't
# correct yet for step curves.. remove this to see it.
should_redraw = True
else:
r = self._src_r
if not r:
# just using for ``.diff()`` atm..
r = self._src_r = Renderer(
viz=self,
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
fmtr=IncrementalFormatter(
shm=self.shm,
viz=self,
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
),
)
# ``Curve`` derivative case(s):
array_key = array_key or self.name
# print(array_key)
# ds update config
new_sample_rate: bool = False
should_ds: bool = r._in_ds
showing_src_data: bool = not r._in_ds
# downsampling incremental state checking
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
# check for and set std m4 downsample conditions
uppx = graphics.x_uppx()
uppx_diff = (uppx - self._last_uppx)
profiler(f'diffed uppx {uppx}')
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
if (
uppx > 1
and abs(uppx_diff) >= 1
):
log.debug(
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
f'{array_key} sampler change: {self._last_uppx} -> {uppx}'
)
self._last_uppx = uppx
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
new_sample_rate = True
showing_src_data = False
should_ds = True
should_redraw = True
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
elif (
uppx <= 2
and self._in_ds
):
# we should de-downsample back to our original
# source data so we clear our path data in prep
# to generate a new one from original source data.
new_sample_rate = True
should_ds = False
should_redraw = True
showing_src_data = True
# reset yrange to be computed from source data
self.yrange = None
# MAIN RENDER LOGIC:
# - determine in view data and redraw on range change
# - determine downsampling ops if needed
# - (incrementally) update ``QPainterPath``
out = r.render(
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
read,
array_key,
profiler,
uppx=uppx,
# use_vr=True,
# TODO: better way to detect and pass this?
# if we want to eventually cache renderers for a given uppx
# we should probably use this as a key + state?
should_redraw=should_redraw,
new_sample_rate=new_sample_rate,
should_ds=should_ds,
showing_src_data=showing_src_data,
do_append=do_append,
**rkwargs,
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
)
if showing_src_data:
2022-06-27 22:22:51 +00:00
# print(f"{self.name} SHOWING SOURCE")
# reset yrange to be computed from source data
self.yrange = None
if not out:
log.warning(f'{self.name} failed to render!?')
return graphics
path, data, reset = out
# if self.yrange:
# print(f'viz {self.name} yrange from m4: {self.yrange}')
# XXX: SUPER UGGGHHH... without this we get stale cache
# graphics that don't update until you downsampler again..
# reset = False
# if reset:
# with graphics.reset_cache():
# # assign output paths to graphicis obj
# graphics.path = r.path
# graphics.fast_path = r.fast_path
# # XXX: we don't need this right?
# # graphics.draw_last_datum(
# # path,
# # src_array,
# # data,
# # reset,
# # array_key,
# # )
# # graphics.update()
# # profiler('.update()')
# else:
# assign output paths to graphicis obj
graphics.path = r.path
graphics.fast_path = r.fast_path
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
graphics.draw_last_datum(
path,
src_array,
data,
reset,
array_key,
index_field=self.index_field,
)
graphics.update()
profiler('.update()')
# TODO: does this actuallly help us in any way (prolly should
# look at the source / ask ogi). I think it avoid artifacts on
# wheel-scroll downsampling curve updates?
# TODO: is this ever better?
# graphics.prepareGeometryChange()
# profiler('.prepareGeometryChange()')
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
# track downsampled state
self._in_ds = r._in_ds
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
return graphics
def draw_last(
self,
array_key: Optional[str] = None,
only_last_uppx: bool = False,
) -> None:
# shm read and slice to view
(
xfirst, xlast, src_array,
ivl, ivr, in_view,
) = self.read()
g = self.graphics
array_key = array_key or self.name
x, y = g.draw_last_datum(
g.path,
src_array,
src_array,
False, # never reset path
array_key,
self.index_field,
)
# the renderer is downsampling we choose
# to always try and updadte a single (interpolating)
# line segment that spans and tries to display
# the las uppx's worth of datums.
# we only care about the last pixel's
# worth of data since that's all the screen
# can represent on the last column where
# the most recent datum is being drawn.
if self._in_ds or only_last_uppx:
dsg = self.ds_graphics or self.graphics
# XXX: pretty sure we don't need this?
# if isinstance(g, Curve):
# with dsg.reset_cache():
uppx = self._last_uppx
y = y[-uppx:]
ymn, ymx = y.min(), y.max()
# print(f'drawing uppx={uppx} mxmn line: {ymn}, {ymx}')
2022-06-15 12:58:01 +00:00
try:
iuppx = x[-uppx]
except IndexError:
# we're less then an x-px wide so just grab the start
# datum index.
iuppx = x[0]
2022-06-15 12:58:01 +00:00
dsg._last_line = QLineF(
2022-06-15 12:58:01 +00:00
iuppx, ymn,
x[-1], ymx,
)
# print(f'updating DS curve {self.name}')
dsg.update()
else:
# print(f'updating NOT DS curve {self.name}')
g.update()
def curve_width_pxs(
self,
) -> float:
'''
Return the width of the current datums in view in pixel units.
'''
_, lbar, rbar, _ = self.bars_range()
return self.view.mapViewToDevice(
QLineF(
lbar, 0,
rbar, 0
)
).length()
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
class Renderer(msgspec.Struct):
viz: Viz
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
fmtr: IncrementalFormatter
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
# output graphics rendering, the main object
# processed in ``QGraphicsObject.paint()``
path: Optional[QPainterPath] = None
fast_path: Optional[QPainterPath] = None
# XXX: just ideas..
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
# called on the final data (transform) output to convert
# to "graphical data form" a format that can be passed to
# the ``.draw()`` implementation.
# graphics_t: Optional[Callable[ShmArray, np.ndarray]] = None
# graphics_t_shm: Optional[ShmArray] = None
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
# path graphics update implementation methods
# prepend_fn: Optional[Callable[QPainterPath, QPainterPath]] = None
# append_fn: Optional[Callable[QPainterPath, QPainterPath]] = None
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
# downsampling state
_last_uppx: float = 0
_in_ds: bool = False
def draw_path(
self,
x: np.ndarray,
y: np.ndarray,
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
connect: str | np.ndarray = 'all',
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
path: Optional[QPainterPath] = None,
redraw: bool = False,
) -> QPainterPath:
path_was_none = path is None
if redraw and path:
path.clear()
# TODO: avoid this?
if self.fast_path:
self.fast_path.clear()
# profiler('cleared paths due to `should_redraw=True`')
path = pg.functions.arrayToQPath(
x,
y,
connect=connect,
finiteCheck=False,
# reserve mem allocs see:
# - https://doc.qt.io/qt-5/qpainterpath.html#reserve
# - https://doc.qt.io/qt-5/qpainterpath.html#capacity
# - https://doc.qt.io/qt-5/qpainterpath.html#clear
# XXX: right now this is based on had hoc checks on a
# hidpi 3840x2160 4k monitor but we should optimize for
# the target display(s) on the sys.
# if no_path_yet:
# graphics.path.reserve(int(500e3))
# path=path, # path re-use / reserving
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
)
# avoid mem allocs if possible
if path_was_none:
path.reserve(path.capacity())
return path
def render(
self,
new_read,
array_key: str,
profiler: Profiler,
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
uppx: float = 1,
# redraw and ds flags
should_redraw: bool = False,
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
new_sample_rate: bool = False,
should_ds: bool = False,
showing_src_data: bool = True,
do_append: bool = True,
use_fpath: bool = True,
# only render datums "in view" of the ``ChartView``
use_vr: bool = True,
read_from_key: bool = True,
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
) -> list[QPainterPath]:
'''
Render the current graphics path(s)
There are (at least) 3 stages from source data to graphics data:
- a data transform (which can be stored in additional shm)
- a graphics transform which converts discrete basis data to
a `float`-basis view-coords graphics basis. (eg. ``ohlc_flatten()``,
``step_path_arrays_from_1d()``, etc.)
- blah blah blah (from notes)
'''
# TODO: can the renderer just call ``Viz.read()`` directly?
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
# unpack latest source data read
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
fmtr = self.fmtr
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
(
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
_,
_,
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
array,
ivl,
ivr,
in_view,
) = new_read
# xy-path data transform: convert source data to a format
# able to be passed to a `QPainterPath` rendering routine.
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
fmt_out = fmtr.format_to_1d(
new_read,
array_key,
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
profiler,
read_src_from_key=read_from_key,
slice_to_inview=use_vr,
)
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
# no history in view case
if not fmt_out:
# XXX: this might be why the profiler only has exits?
return
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
(
x_1d,
y_1d,
connect,
prepend_length,
append_length,
view_changed,
# append_tres,
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
) = fmt_out
WIP get incremental step curve updates working This took longer then i care to admit XD but it definitely adds a huge speedup and with only a few outstanding correctness bugs: - panning from left to right causes strange trailing artifacts in the flows fsp (vlm) sub-plot but only when some data is off-screen on the left but doesn't appear to be an issue if we keep the `._set_yrange()` handler hooked up to the `.sigXRangeChanged` signal (but we aren't going to because this makes panning way slower). i've got a feeling this is a bug todo with the device coordinate cache stuff and we may need to report to Qt core? - factoring out the step curve logic from `FastAppendCurve.update_from_array()` (un)fortunately required some logic branch uncoupling but also meant we needed special input controls to avoid things like redraws and curve appends for special cases, this will hopefully all be better rectified in code when the core of this method is moved into a renderer type/implementation. - the `tina_vwap` fsp curve now somehow causes hangs when doing erratic scrolling on downsampled graphics data. i have no idea why or how but disabling it makes the issue go away (ui will literally just freeze and gobble CPU on a `.paint()` call until you ctrl-c the hell out of it). my guess is that something in the logic for standard line curves and appends on large data sets is the issue? Code related changes/hacks: - drop use of `step_path_arrays_from_1d()`, it was always a bit hacky (being based on `pyqtgraph` internals) and was generally hard to understand since it returns 1d data instead of the more expected (N,2) array of "step levels"; instead this is now implemented (uglily) in the `Flow.update_graphics()` block for step curves (which will obviously get cleaned up and factored elsewhere). - add a bunch of new flags to the update method on the fast append curve: `draw_last: bool`, `slice_to_head: int`, `do_append: bool`, `should_redraw: bool` which are all controls to aid with previously mentioned issues specific to getting step curve updates working correctly. - add a ton of commented tinkering related code (that we may end up using) to both the flow and append curve methods that was written as part of the effort to get this all working. - implement all step curve updating inline in `Flow.update_graphics()` including prepend and append logic for pre-graphics incremental step data maintenance and in-view slicing as well as "last step" graphics updating. Obviously clean up commits coming stat B)
2022-04-26 12:34:53 +00:00
# redraw conditions
if (
prepend_length > 0
or new_sample_rate
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
or view_changed
# NOTE: comment this to try and make "append paths"
# work below..
or append_length > 0
):
should_redraw = True
WIP get incremental step curve updates working This took longer then i care to admit XD but it definitely adds a huge speedup and with only a few outstanding correctness bugs: - panning from left to right causes strange trailing artifacts in the flows fsp (vlm) sub-plot but only when some data is off-screen on the left but doesn't appear to be an issue if we keep the `._set_yrange()` handler hooked up to the `.sigXRangeChanged` signal (but we aren't going to because this makes panning way slower). i've got a feeling this is a bug todo with the device coordinate cache stuff and we may need to report to Qt core? - factoring out the step curve logic from `FastAppendCurve.update_from_array()` (un)fortunately required some logic branch uncoupling but also meant we needed special input controls to avoid things like redraws and curve appends for special cases, this will hopefully all be better rectified in code when the core of this method is moved into a renderer type/implementation. - the `tina_vwap` fsp curve now somehow causes hangs when doing erratic scrolling on downsampled graphics data. i have no idea why or how but disabling it makes the issue go away (ui will literally just freeze and gobble CPU on a `.paint()` call until you ctrl-c the hell out of it). my guess is that something in the logic for standard line curves and appends on large data sets is the issue? Code related changes/hacks: - drop use of `step_path_arrays_from_1d()`, it was always a bit hacky (being based on `pyqtgraph` internals) and was generally hard to understand since it returns 1d data instead of the more expected (N,2) array of "step levels"; instead this is now implemented (uglily) in the `Flow.update_graphics()` block for step curves (which will obviously get cleaned up and factored elsewhere). - add a bunch of new flags to the update method on the fast append curve: `draw_last: bool`, `slice_to_head: int`, `do_append: bool`, `should_redraw: bool` which are all controls to aid with previously mentioned issues specific to getting step curve updates working correctly. - add a ton of commented tinkering related code (that we may end up using) to both the flow and append curve methods that was written as part of the effort to get this all working. - implement all step curve updating inline in `Flow.update_graphics()` including prepend and append logic for pre-graphics incremental step data maintenance and in-view slicing as well as "last step" graphics updating. Obviously clean up commits coming stat B)
2022-04-26 12:34:53 +00:00
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
path = self.path
fast_path = self.fast_path
reset = False
# redraw the entire source data if we have either of:
# - no prior path graphic rendered or,
# - we always intend to re-render the data only in view
if (
2022-05-18 12:53:35 +00:00
path is None
or should_redraw
):
# print(f"{self.viz.name} -> REDRAWING BRUH")
if new_sample_rate and showing_src_data:
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
log.info(f'DEDOWN -> {array_key}')
self._in_ds = False
elif should_ds and uppx > 1:
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
x_1d, y_1d, ymn, ymx = xy_downsample(
x_1d,
y_1d,
uppx,
WIP get incremental step curve updates working This took longer then i care to admit XD but it definitely adds a huge speedup and with only a few outstanding correctness bugs: - panning from left to right causes strange trailing artifacts in the flows fsp (vlm) sub-plot but only when some data is off-screen on the left but doesn't appear to be an issue if we keep the `._set_yrange()` handler hooked up to the `.sigXRangeChanged` signal (but we aren't going to because this makes panning way slower). i've got a feeling this is a bug todo with the device coordinate cache stuff and we may need to report to Qt core? - factoring out the step curve logic from `FastAppendCurve.update_from_array()` (un)fortunately required some logic branch uncoupling but also meant we needed special input controls to avoid things like redraws and curve appends for special cases, this will hopefully all be better rectified in code when the core of this method is moved into a renderer type/implementation. - the `tina_vwap` fsp curve now somehow causes hangs when doing erratic scrolling on downsampled graphics data. i have no idea why or how but disabling it makes the issue go away (ui will literally just freeze and gobble CPU on a `.paint()` call until you ctrl-c the hell out of it). my guess is that something in the logic for standard line curves and appends on large data sets is the issue? Code related changes/hacks: - drop use of `step_path_arrays_from_1d()`, it was always a bit hacky (being based on `pyqtgraph` internals) and was generally hard to understand since it returns 1d data instead of the more expected (N,2) array of "step levels"; instead this is now implemented (uglily) in the `Flow.update_graphics()` block for step curves (which will obviously get cleaned up and factored elsewhere). - add a bunch of new flags to the update method on the fast append curve: `draw_last: bool`, `slice_to_head: int`, `do_append: bool`, `should_redraw: bool` which are all controls to aid with previously mentioned issues specific to getting step curve updates working correctly. - add a ton of commented tinkering related code (that we may end up using) to both the flow and append curve methods that was written as part of the effort to get this all working. - implement all step curve updating inline in `Flow.update_graphics()` including prepend and append logic for pre-graphics incremental step data maintenance and in-view slicing as well as "last step" graphics updating. Obviously clean up commits coming stat B)
2022-04-26 12:34:53 +00:00
)
self.viz.yrange = ymn, ymx
# print(f'{self.viz.name} post ds: ymn, ymx: {ymn},{ymx}')
reset = True
profiler(f'FULL PATH downsample redraw={should_ds}')
self._in_ds = True
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
path = self.draw_path(
Add graphics incr-updated "formatter" subsys After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curves
2022-11-22 22:28:58 +00:00
x=x_1d,
y=y_1d,
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
connect=connect,
2022-05-18 12:53:35 +00:00
path=path,
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
redraw=True,
)
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
profiler(
'generated fresh path. '
f'(should_redraw: {should_redraw} '
f'should_ds: {should_ds} new_sample_rate: {new_sample_rate})'
)
# TODO: get this piecewise prepend working - right now it's
# giving heck on vwap...
# elif prepend_length:
# prepend_path = pg.functions.arrayToQPath(
# x[0:prepend_length],
# y[0:prepend_length],
# connect='all'
# )
# # swap prepend path in "front"
# old_path = graphics.path
# graphics.path = prepend_path
# # graphics.path.moveTo(new_x[0], new_y[0])
# graphics.path.connectPath(old_path)
elif (
append_length > 0
and do_append
):
print(f'{array_key} append len: {append_length}')
# new_x = x_1d[-append_length - 2:] # slice_to_head]
# new_y = y_1d[-append_length - 2:] # slice_to_head]
profiler('sliced append path')
# (
# x_1d,
# y_1d,
# connect,
# ) = append_tres
profiler(
f'diffed array input, append_length={append_length}'
)
WIP get incremental step curve updates working This took longer then i care to admit XD but it definitely adds a huge speedup and with only a few outstanding correctness bugs: - panning from left to right causes strange trailing artifacts in the flows fsp (vlm) sub-plot but only when some data is off-screen on the left but doesn't appear to be an issue if we keep the `._set_yrange()` handler hooked up to the `.sigXRangeChanged` signal (but we aren't going to because this makes panning way slower). i've got a feeling this is a bug todo with the device coordinate cache stuff and we may need to report to Qt core? - factoring out the step curve logic from `FastAppendCurve.update_from_array()` (un)fortunately required some logic branch uncoupling but also meant we needed special input controls to avoid things like redraws and curve appends for special cases, this will hopefully all be better rectified in code when the core of this method is moved into a renderer type/implementation. - the `tina_vwap` fsp curve now somehow causes hangs when doing erratic scrolling on downsampled graphics data. i have no idea why or how but disabling it makes the issue go away (ui will literally just freeze and gobble CPU on a `.paint()` call until you ctrl-c the hell out of it). my guess is that something in the logic for standard line curves and appends on large data sets is the issue? Code related changes/hacks: - drop use of `step_path_arrays_from_1d()`, it was always a bit hacky (being based on `pyqtgraph` internals) and was generally hard to understand since it returns 1d data instead of the more expected (N,2) array of "step levels"; instead this is now implemented (uglily) in the `Flow.update_graphics()` block for step curves (which will obviously get cleaned up and factored elsewhere). - add a bunch of new flags to the update method on the fast append curve: `draw_last: bool`, `slice_to_head: int`, `do_append: bool`, `should_redraw: bool` which are all controls to aid with previously mentioned issues specific to getting step curve updates working correctly. - add a ton of commented tinkering related code (that we may end up using) to both the flow and append curve methods that was written as part of the effort to get this all working. - implement all step curve updating inline in `Flow.update_graphics()` including prepend and append logic for pre-graphics incremental step data maintenance and in-view slicing as well as "last step" graphics updating. Obviously clean up commits coming stat B)
2022-04-26 12:34:53 +00:00
# if should_ds and uppx > 1:
# new_x, new_y = xy_downsample(
# new_x,
# new_y,
# uppx,
# )
# profiler(f'fast path downsample redraw={should_ds}')
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
append_path = self.draw_path(
x=x_1d,
y=y_1d,
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
connect=connect,
path=fast_path,
)
profiler('generated append qpath')
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
if use_fpath:
# print(f'{self.viz.name}: FAST PATH')
# an attempt at trying to make append-updates faster..
if fast_path is None:
2022-05-18 12:53:35 +00:00
fast_path = append_path
# fast_path.reserve(int(6e3))
else:
fast_path.connectPath(append_path)
size = fast_path.capacity()
profiler(f'connected fast path w size: {size}')
print(
f"append_path br: {append_path.boundingRect()}\n"
f"path size: {size}\n"
f"append_path len: {append_path.length()}\n"
f"fast_path len: {fast_path.length()}\n"
)
# graphics.path.moveTo(new_x[0], new_y[0])
# path.connectPath(append_path)
# XXX: lol this causes a hang..
# graphics.path = graphics.path.simplified()
else:
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
size = path.capacity()
profiler(f'connected history path w size: {size}')
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
path.connectPath(append_path)
Move graphics update logic into `Renderer.render()` Finally this gets us much closer to a generic incremental update system for graphics wherein the input array diffing, pre-graphical format data processing, downsampler activation and incremental update and storage of any of these data flow stages can be managed in one modular sub-system :surfer_boi:. Dirty deatz: - reorg and move all path logic into `Renderer.render()` and have it take in pretty much the same flags as the old `FastAppendCurve.update_from_array()` and instead storing all update state vars (even copies of the downsampler related ones) on the renderer instance: - new state vars: `._last_uppx, ._in_ds, ._vr, ._avr` - `.render()` input bools: `new_sample_rate, should_redraw, should_ds, showing_src_data` - add a hack-around for passing in incremental update data (for now) via a `input_data: tuple` of numpy arrays - a default `uppx: float = 1` - add new render interface attrs: - `.format_xy()` which takes in the source data array and produces out x, y arrays (and maybe a `connect` array) that can be passed to `.draw_path()` (the default for this is just to slice out the index and `array_key: str` columns from the input struct array), - `.draw_path()` which takes in the x, y, connect arrays and generates a `QPainterPath` - `.fast_path`, for "appendable" updates like there was on the fast append curve - move redraw (aka `.clear()` calls) into `.draw_path()` and trigger via `redraw: bool` flag. - our graphics objects no longer set their own `.path` state, it's done by the `Flow.update_graphics()` method using output from `Renderer.render()` (and it's state if necessary)
2022-05-18 19:23:14 +00:00
self.path = path
self.fast_path = fast_path
return self.path, array, reset