Rename `Flow` -> `Viz`

The type is better described as a "data visualization":
https://en.wikipedia.org/wiki/Data_and_information_visualization

Add `ChartPlotWidget.get_viz()` to start working towards not accessing
the private table directly XD

We'll probably end up using the name `Flow` for a type that tracks
a collection of composed/cascaded `Flume`s:
https://en.wikipedia.org/wiki/Two-port_network#Cascade_connection
epoch_indexing_and_dataviz_layer
Tyler Goodlet 2022-11-24 15:33:58 -05:00
parent 9ace053aaf
commit 86d09d9305
11 changed files with 132 additions and 195 deletions

View File

@ -302,7 +302,7 @@ class DynamicDateAxis(Axis):
# XX: ARGGGGG AG:LKSKDJF:LKJSDFD
chart = self.pi.chart_widget
flow = chart._flows[chart.name]
flow = chart._vizs[chart.name]
shm = flow.shm
bars = shm.array
first = shm._first.value

View File

@ -72,7 +72,7 @@ from ._interaction import ChartView
from ._forms import FieldsForm
from .._profile import pg_profile_enabled, ms_slower_then
from ._overlay import PlotItemOverlay
from ._flows import Flow
from ._flows import Viz
from ._search import SearchWidget
from . import _pg_overrides as pgo
from .._profile import Profiler
@ -711,7 +711,7 @@ class LinkedSplits(QWidget):
if style == 'ohlc_bar':
# graphics, data_key = cpw.draw_ohlc(
flow = cpw.draw_ohlc(
viz = cpw.draw_ohlc(
name,
shm,
flume=flume,
@ -727,7 +727,7 @@ class LinkedSplits(QWidget):
elif style == 'line':
add_label = True
# graphics, data_key = cpw.draw_curve(
flow = cpw.draw_curve(
viz = cpw.draw_curve(
name,
shm,
flume,
@ -738,7 +738,7 @@ class LinkedSplits(QWidget):
elif style == 'step':
add_label = True
# graphics, data_key = cpw.draw_curve(
flow = cpw.draw_curve(
viz = cpw.draw_curve(
name,
shm,
flume,
@ -751,8 +751,8 @@ class LinkedSplits(QWidget):
else:
raise ValueError(f"Chart style {style} is currently unsupported")
graphics = flow.graphics
data_key = flow.name
graphics = viz.graphics
data_key = viz.name
if _is_main:
assert style == 'ohlc_bar', 'main chart must be OHLC'
@ -908,7 +908,7 @@ class ChartPlotWidget(pg.PlotWidget):
# self.setViewportMargins(0, 0, 0, 0)
# registry of overlay curve names
self._flows: dict[str, Flow] = {}
self._vizs: dict[str, Viz] = {}
self.feed: Feed | None = None
@ -974,7 +974,7 @@ class ChartPlotWidget(pg.PlotWidget):
Return a range tuple for the bars present in view.
'''
main_flow = self._flows[self.name]
main_flow = self._vizs[self.name]
ifirst, l, lbar, rbar, r, ilast = main_flow.datums_range()
return l, lbar, rbar, r
@ -1038,9 +1038,9 @@ class ChartPlotWidget(pg.PlotWidget):
Set the view box to the "default" startup view of the scene.
'''
flow = self._flows.get(self.name)
flow = self._vizs.get(self.name)
if not flow:
log.warning(f'`Flow` for {self.name} not loaded yet?')
log.warning(f'`Viz` for {self.name} not loaded yet?')
return
arr = flow.shm.array
@ -1220,7 +1220,7 @@ class ChartPlotWidget(pg.PlotWidget):
**graphics_kwargs,
) -> Flow:
) -> Viz:
'''
Draw a "curve" (line plot graphics) for the provided data in
the input shm array ``shm``.
@ -1254,7 +1254,7 @@ class ChartPlotWidget(pg.PlotWidget):
**graphics_kwargs,
)
flow = self._flows[data_key] = Flow(
flow = self._vizs[data_key] = Viz(
data_key,
pi,
shm,
@ -1332,7 +1332,7 @@ class ChartPlotWidget(pg.PlotWidget):
array_key: Optional[str] = None,
**draw_curve_kwargs,
) -> Flow:
) -> Viz:
'''
Draw OHLC datums to chart.
@ -1358,7 +1358,7 @@ class ChartPlotWidget(pg.PlotWidget):
Update the named internal graphics from ``array``.
'''
flow = self._flows[array_key or graphics_name]
flow = self._vizs[array_key or graphics_name]
return flow.update_graphics(
array_key=array_key,
**kwargs,
@ -1426,15 +1426,15 @@ class ChartPlotWidget(pg.PlotWidget):
delayed=True,
)
# TODO: here we should instead look up the ``Flow.shm.array``
# TODO: here we should instead look up the ``Viz.shm.array``
# and read directly from shm to avoid copying to memory first
# and then reading it again here.
flow_key = name or self.name
flow = self._flows.get(flow_key)
viz = self._vizs.get(flow_key)
if (
flow is None
viz is None
):
log.error(f"flow {flow_key} doesn't exist in chart {self.name} !?")
log.error(f"viz {flow_key} doesn't exist in chart {self.name} !?")
key = res = 0, 0
else:
@ -1445,11 +1445,11 @@ class ChartPlotWidget(pg.PlotWidget):
rbar,
r,
last,
) = bars_range or flow.datums_range()
) = bars_range or viz.datums_range()
profiler(f'{self.name} got bars range')
key = round(lbar), round(rbar)
res = flow.maxmin(*key)
res = viz.maxmin(*key)
if (
res is None
@ -1465,3 +1465,9 @@ class ChartPlotWidget(pg.PlotWidget):
profiler(f'yrange mxmn: {key} -> {res}')
# print(f'{flow_key} yrange mxmn: {key} -> {res}')
return res
def get_viz(
self,
key: str,
) -> Viz:
return self._vizs[key]

View File

@ -274,8 +274,8 @@ class ContentsLabels:
) -> None:
for chart, name, label, update in self._labels:
flow = chart._flows[name]
array = flow.shm.array
viz = chart.get_viz(name)
array = viz.shm.array
if not (
index >= 0
@ -482,25 +482,32 @@ class Cursor(pg.GraphicsObject):
def add_curve_cursor(
self,
plot: ChartPlotWidget, # noqa
chart: ChartPlotWidget, # noqa
curve: 'PlotCurveItem', # noqa
) -> LineDot:
# if this plot contains curves add line dot "cursors" to denote
# if this chart contains curves add line dot "cursors" to denote
# the current sample under the mouse
main_flow = plot._flows[plot.name]
main_viz = chart.get_viz(chart.name)
# read out last index
i = main_flow.shm.array[-1]['index']
i = main_viz.shm.array[-1]['index']
cursor = LineDot(
curve,
index=i,
plot=plot
plot=chart
)
plot.addItem(cursor)
self.graphics[plot].setdefault('cursors', []).append(cursor)
chart.addItem(cursor)
self.graphics[chart].setdefault('cursors', []).append(cursor)
return cursor
def mouseAction(self, action, plot): # noqa
def mouseAction(
self,
action: str,
plot: ChartPlotWidget,
) -> None: # noqa
log.debug(f"{(action, plot.name)}")
if action == 'Enter':
self.active_plot = plot

View File

@ -78,7 +78,7 @@ from .._profile import Profiler
log = get_logger(__name__)
# TODO: delegate this to each `Flow.maxmin()` which includes
# TODO: delegate this to each `Viz.maxmin()` which includes
# caching and further we should implement the following stream based
# approach, likely with ``numba``:
# https://arxiv.org/abs/cs/0610046
@ -113,7 +113,7 @@ def chart_maxmin(
# TODO: we need to NOT call this to avoid a manual
# np.max/min trigger and especially on the vlm_chart
# flows which aren't shown.. like vlm?
# vizs which aren't shown.. like vlm?
if vlm_chart:
out = vlm_chart.maxmin()
if out:
@ -220,7 +220,7 @@ class DisplayState(Struct):
_, _, _, r = chart.bars_range()
liv = r >= shm.index
# update the "last datum" (aka extending the flow graphic with
# update the "last datum" (aka extending the vizs graphic with
# new data) only if the number of unit steps is >= the number of
# such unit steps per pixel (aka uppx). Iow, if the zoom level
# is such that a datum(s) update to graphics wouldn't span
@ -301,14 +301,14 @@ async def graphics_update_loop(
fqsn = symbol.fqsn
# update last price sticky
fast_pi = fast_chart._flows[fqsn].plot
fast_pi = fast_chart._vizs[fqsn].plot
last_price_sticky = fast_pi.getAxis('right')._stickies[fqsn]
last_price_sticky.update_from_data(
*ohlcv.array[-1][['index', 'close']]
)
last_price_sticky.show()
slow_pi = hist_chart._flows[fqsn].plot
slow_pi = hist_chart._vizs[fqsn].plot
hist_last_price_sticky = slow_pi.getAxis('right')._stickies[fqsn]
hist_last_price_sticky.update_from_data(
*hist_ohlcv.array[-1][['index', 'close']]
@ -383,7 +383,7 @@ async def graphics_update_loop(
})
if vlm_chart:
vlm_pi = vlm_chart._flows['volume'].plot
vlm_pi = vlm_chart._vizs['volume'].plot
vlm_sticky = vlm_pi.getAxis('right')._stickies['volume']
ds.vlm_chart = vlm_chart
ds.vlm_sticky = vlm_sticky
@ -441,8 +441,8 @@ async def graphics_update_loop(
and liv
):
# hist_chart.increment_view(steps=i_diff)
flow = hist_chart._flows[fqsn]
flow.plot.vb._set_yrange(
viz = hist_chart._vizs[fqsn]
viz.plot.vb._set_yrange(
# yrange=hist_chart.maxmin(name=fqsn)
)
# hist_chart.view._set_yrange(yrange=hist_chart.maxmin())
@ -518,7 +518,7 @@ def graphics_update_cycle(
flume = ds.flume
sym = flume.symbol
fqsn = sym.fqsn
main_flow = chart._flows[fqsn]
main_viz = chart._vizs[fqsn]
profiler = Profiler(
msg=f'Graphics loop cycle for: `{chart.name}`',
@ -562,7 +562,7 @@ def graphics_update_cycle(
):
# print(f'INCREMENTING {fqsn}')
chart.increment_view(steps=i_diff)
main_flow.plot.vb._set_yrange(
main_viz.plot.vb._set_yrange(
# yrange=(mn, mx),
)
@ -629,7 +629,7 @@ def graphics_update_cycle(
# chart.name,
# do_append=do_append,
)
main_flow.draw_last(array_key=fqsn)
main_viz.draw_last(array_key=fqsn)
hist_chart.update_graphics_from_flow(
fqsn,
@ -748,7 +748,7 @@ def graphics_update_cycle(
and not chart._static_yrange == 'axis'
):
# main_vb = chart.view
main_vb = chart._flows[fqsn].plot.vb
main_vb = chart._vizs[fqsn].plot.vb
if (
main_vb._ic is None
or not main_vb._ic.is_set()
@ -779,26 +779,26 @@ def graphics_update_cycle(
is_1m=True,
)
if hist_liv:
flow = hist_chart._flows[fqsn]
flow.plot.vb._set_yrange(
viz = hist_chart._vizs[fqsn]
viz.plot.vb._set_yrange(
# yrange=hist_chart.maxmin(name=fqsn),
)
# XXX: update this every draw cycle to make L1-always-in-view work.
vars['last_mx'], vars['last_mn'] = mx, mn
# run synchronous update on all linked flows
# TODO: should the "main" (aka source) flow be special?
for curve_name, flow in chart._flows.items():
# run synchronous update on all linked viz
# TODO: should the "main" (aka source) viz be special?
for curve_name, viz in chart._vizs.items():
# update any overlayed fsp flows
if (
# curve_name != chart.data_key
curve_name != fqsn
and not flow.is_ohlc
and not viz.is_ohlc
):
update_fsp_chart(
chart,
flow,
viz,
curve_name,
array_key=curve_name,
)
@ -812,7 +812,7 @@ def graphics_update_cycle(
# and not do_append
# and not do_rt_update
):
flow.draw_last(
viz.draw_last(
array_key=curve_name,
only_last_uppx=True,
)
@ -821,7 +821,7 @@ def graphics_update_cycle(
# TODO: can we unify this with the above loop?
if vlm_chart:
# print(f"DOING VLM {fqsn}")
vlm_flows = vlm_chart._flows
vlm_vizs = vlm_chart._vizs
# always update y-label
ds.vlm_sticky.update_from_data(
@ -866,21 +866,21 @@ def graphics_update_cycle(
vars['last_mx_vlm'] = mx_vlm_in_view
# update all downstream FSPs
for curve_name, flow in vlm_flows.items():
for curve_name, viz in vlm_vizs.items():
if (
curve_name not in {'volume', fqsn}
and flow.render
and viz.render
and (
liv and do_rt_update
or do_append
)
# and not flow.is_ohlc
# and not viz.is_ohlc
# and curve_name != fqsn
):
update_fsp_chart(
vlm_chart,
flow,
viz,
curve_name,
array_key=curve_name,
# do_append=uppx < update_uppx,
@ -889,7 +889,7 @@ def graphics_update_cycle(
# is this even doing anything?
# (pretty sure it's the real-time
# resizing from last quote?)
fvb = flow.plot.vb
fvb = viz.plot.vb
fvb._set_yrange(
name=curve_name,
)
@ -905,9 +905,9 @@ def graphics_update_cycle(
# range of that set.
):
# always update the last datum-element
# graphic for all flows
# print(f'drawing last {flow.name}')
flow.draw_last(array_key=curve_name)
# graphic for all vizs
# print(f'drawing last {viz.name}')
viz.draw_last(array_key=curve_name)
async def link_views_with_region(
@ -937,12 +937,12 @@ async def link_views_with_region(
hist_pi.addItem(region, ignoreBounds=True)
region.setOpacity(6/16)
flow = rt_chart._flows[flume.symbol.fqsn]
assert flow
viz = rt_chart._vizs[flume.symbol.fqsn]
assert viz
# XXX: no idea why this doesn't work but it's causing
# a weird placement of the region on the way-far-left..
# region.setClipItem(flow.graphics)
# region.setClipItem(viz.graphics)
# poll for datums load and timestep detection
for _ in range(100):
@ -1052,11 +1052,11 @@ def multi_maxmin(
) -> tuple[float, float]:
'''
Flows "group" maxmin loop; assumes all named flows
Viz "group" maxmin loop; assumes all named vizs
are in the same co-domain and thus can be sorted
as one set.
Iterates all the named flows and calls the chart
Iterates all the named vizs and calls the chart
api to find their range values and return.
TODO: really we should probably have a more built-in API
@ -1279,7 +1279,7 @@ async def display_symbol_data(
hist_pi.hideAxis('left')
hist_pi.hideAxis('bottom')
flow = hist_chart.draw_curve(
viz = hist_chart.draw_curve(
fqsn,
hist_ohlcv,
flume,
@ -1300,8 +1300,8 @@ async def display_symbol_data(
# specially store ref to shm for lookup in display loop
# since only a placeholder of `None` is entered in
# ``.draw_curve()``.
flow = hist_chart._flows[fqsn]
assert flow.plot is hist_pi
viz = hist_chart._vizs[fqsn]
assert viz.plot is hist_pi
pis.setdefault(fqsn, [None, None])[1] = hist_pi
rt_pi = rt_chart.overlay_plotitem(
@ -1312,7 +1312,7 @@ async def display_symbol_data(
rt_pi.hideAxis('left')
rt_pi.hideAxis('bottom')
flow = rt_chart.draw_curve(
viz = rt_chart.draw_curve(
fqsn,
ohlcv,
flume,
@ -1333,8 +1333,8 @@ async def display_symbol_data(
# specially store ref to shm for lookup in display loop
# since only a placeholder of `None` is entered in
# ``.draw_curve()``.
flow = rt_chart._flows[fqsn]
assert flow.plot is rt_pi
viz = rt_chart._vizs[fqsn]
assert viz.plot is rt_pi
pis.setdefault(fqsn, [None, None])[0] = rt_pi
rt_chart.setFocus()

View File

@ -377,7 +377,7 @@ class SelectRect(QtWidgets.QGraphicsRectItem):
nbars = ixmx - ixmn + 1
chart = self._chart
data = chart._flows[chart.name].shm.array[ixmn:ixmx]
data = chart.get_viz(chart.name).shm.array[ixmn:ixmx]
if len(data):
std = data['close'].std()

View File

@ -65,7 +65,7 @@ log = get_logger(__name__)
def render_baritems(
flow: Flow,
viz: Viz,
graphics: BarItems,
read: tuple[
int, int, np.ndarray,
@ -89,7 +89,7 @@ def render_baritems(
bars = graphics
# if no source data renderer exists create one.
self = flow
self = viz
show_bars: bool = False
r = self._src_r
@ -98,28 +98,28 @@ def render_baritems(
# OHLC bars path renderer
r = self._src_r = Renderer(
flow=self,
viz=self,
fmtr=OHLCBarsFmtr(
shm=flow.shm,
flow=flow,
shm=viz.shm,
viz=viz,
_last_read=read,
),
)
ds_curve_r = Renderer(
flow=self,
viz=self,
fmtr=OHLCBarsAsCurveFmtr(
shm=flow.shm,
flow=flow,
shm=viz.shm,
viz=viz,
_last_read=read,
),
)
curve = FlattenedOHLC(
name=f'{flow.name}_ds_ohlc',
name=f'{viz.name}_ds_ohlc',
color=bars._color,
)
flow.ds_graphics = curve
viz.ds_graphics = curve
curve.hide()
self.plot.addItem(curve)
@ -142,7 +142,7 @@ def render_baritems(
):
# print('FLIPPING TO BARS')
should_line = False
flow._in_ds = False
viz._in_ds = False
elif (
not in_line
@ -150,7 +150,7 @@ def render_baritems(
):
# print('FLIPPING TO LINE')
should_line = True
flow._in_ds = True
viz._in_ds = True
profiler(f'ds logic complete line={should_line}')
@ -196,9 +196,9 @@ def render_baritems(
)
class Flow(msgspec.Struct): # , frozen=True):
class Viz(msgspec.Struct): # , frozen=True):
'''
(Financial Signal-)Flow compound type which wraps a real-time
(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.
@ -216,7 +216,7 @@ class Flow(msgspec.Struct): # , frozen=True):
# for tracking y-mn/mx for y-axis auto-ranging
yrange: tuple[float, float] = None
# in some cases a flow may want to change its
# 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
@ -251,12 +251,6 @@ class Flow(msgspec.Struct): # , frozen=True):
def shm(self) -> ShmArray:
return self._shm
# TODO: remove this and only allow setting through
# private ``._shm`` attr?
# @shm.setter
# def shm(self, shm: ShmArray) -> ShmArray:
# self._shm = shm
def maxmin(
self,
lbar: int,
@ -318,7 +312,7 @@ class Flow(msgspec.Struct): # , frozen=True):
def view_range(self) -> tuple[int, int]:
'''
Return the indexes in view for the associated
plot displaying this flow's data.
plot displaying this viz's data.
'''
vr = self.plot.viewRect()
@ -344,7 +338,7 @@ class Flow(msgspec.Struct): # , frozen=True):
# TODO: avoid this and have shm passed
# in earlier.
if self.shm is None:
# haven't initialized the flow yet
# haven't initialized the viz yet
return (0, l, 0, 0, r, 0)
array = self.shm.array
@ -420,7 +414,7 @@ class Flow(msgspec.Struct): # , frozen=True):
'''
profiler = Profiler(
msg=f'Flow.update_graphics() for {self.name}',
msg=f'Viz.update_graphics() for {self.name}',
disabled=not pg_profile_enabled(),
ms_threshold=4,
# ms_threshold=ms_slower_then,
@ -475,10 +469,10 @@ class Flow(msgspec.Struct): # , frozen=True):
if isinstance(graphics, StepCurve):
r = self._src_r = Renderer(
flow=self,
viz=self,
fmtr=StepCurveFmtr(
shm=self.shm,
flow=self,
viz=self,
_last_read=read,
),
)
@ -493,10 +487,10 @@ class Flow(msgspec.Struct): # , frozen=True):
if not r:
# just using for ``.diff()`` atm..
r = self._src_r = Renderer(
flow=self,
viz=self,
fmtr=IncrementalFormatter(
shm=self.shm,
flow=self,
viz=self,
_last_read=read,
),
)
@ -581,7 +575,7 @@ class Flow(msgspec.Struct): # , frozen=True):
path, data, reset = out
# if self.yrange:
# print(f'flow {self.name} yrange from m4: {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..
@ -691,7 +685,7 @@ class Flow(msgspec.Struct): # , frozen=True):
class Renderer(msgspec.Struct):
flow: Flow
viz: Viz
fmtr: IncrementalFormatter
# output graphics rendering, the main object
@ -794,7 +788,7 @@ class Renderer(msgspec.Struct):
- blah blah blah (from notes)
'''
# TODO: can the renderer just call ``Flow.read()`` directly?
# TODO: can the renderer just call ``Viz.read()`` directly?
# unpack latest source data read
fmtr = self.fmtr
@ -858,7 +852,7 @@ class Renderer(msgspec.Struct):
path is None
or should_redraw
):
# print(f"{self.flow.name} -> REDRAWING BRUH")
# print(f"{self.viz.name} -> REDRAWING BRUH")
if new_sample_rate and showing_src_data:
log.info(f'DEDOWN -> {array_key}')
self._in_ds = False
@ -870,8 +864,8 @@ class Renderer(msgspec.Struct):
y_1d,
uppx,
)
self.flow.yrange = ymn, ymx
# print(f'{self.flow.name} post ds: ymn, ymx: {ymn},{ymx}')
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}')
@ -942,7 +936,7 @@ class Renderer(msgspec.Struct):
profiler('generated append qpath')
if use_fpath:
# print(f'{self.flow.name}: FAST PATH')
# print(f'{self.viz.name}: FAST PATH')
# an attempt at trying to make append-updates faster..
if fast_path is None:
fast_path = append_path

View File

@ -289,7 +289,7 @@ async def run_fsp_ui(
# first UI update, usually from shm pushed history
update_fsp_chart(
chart,
chart._flows[array_key],
chart.get_viz(array_key),
name,
array_key=array_key,
)
@ -357,7 +357,7 @@ async def run_fsp_ui(
# last = time.time()
# TODO: maybe this should be our ``Flow`` type since it maps
# TODO: maybe this should be our ``Viz`` type since it maps
# one flume to the next? The machinery for task/actor mgmt should
# be part of the instantiation API?
class FspAdmin:
@ -386,7 +386,7 @@ class FspAdmin:
# TODO: make this a `.src_flume` and add
# a `dst_flume`?
# (=> but then wouldn't this be the most basic `Flow`?)
# (=> but then wouldn't this be the most basic `Viz`?)
self.flume = flume
def rr_next_portal(self) -> tractor.Portal:
@ -694,7 +694,7 @@ async def open_vlm_displays(
) -> tuple[float, float]:
'''
Flows "group" maxmin loop; assumes all named flows
Viz "group" maxmin loop; assumes all named flows
are in the same co-domain and thus can be sorted
as one set.
@ -865,7 +865,7 @@ async def open_vlm_displays(
# specially store ref to shm for lookup in display loop
# since only a placeholder of `None` is entered in
# ``.draw_curve()``.
# flow = chart._flows[name]
# viz = chart._vizs[name]
assert flow.plot is pi
chart_curves(
@ -901,7 +901,7 @@ async def open_vlm_displays(
# liquidity events (well at least on low OHLC periods - 1s).
vlm_curve.hide()
chart.removeItem(vlm_curve)
vflow = chart._flows['volume']
vflow = chart._vizs['volume']
vflow.render = False
# avoid range sorting on volume once disabled

View File

@ -504,7 +504,7 @@ class ChartView(ViewBox):
# if (
# ev.delta() < 0
# and vl >= len(chart._flows[chart.name].shm.array) + 666
# and vl >= len(chart._vizs[chart.name].shm.array) + 666
# ):
# log.debug("Min zoom bruh...")
# return
@ -821,7 +821,7 @@ class ChartView(ViewBox):
# XXX: only compute the mxmn range
# if none is provided as input!
if not yrange:
# flow = chart._flows[name]
# flow = chart._vizs[name]
yrange = self._maxmin()
if yrange is None:
@ -912,7 +912,7 @@ class ChartView(ViewBox):
graphics items which are our children.
'''
graphics = [f.graphics for f in self._chart._flows.values()]
graphics = [f.graphics for f in self._chart._vizs.values()]
if not graphics:
return 0
@ -948,7 +948,7 @@ class ChartView(ViewBox):
plots |= linked.subplots
for chart_name, chart in plots.items():
for name, flow in chart._flows.items():
for name, flow in chart._vizs.items():
if (
not flow.render

View File

@ -43,7 +43,7 @@ from ._compression import (
if TYPE_CHECKING:
from ._flows import (
Renderer,
Flow,
Viz,
)
from .._profile import Profiler
@ -73,7 +73,7 @@ class IncrementalFormatter(msgspec.Struct):
'''
shm: ShmArray
flow: Flow
viz: Viz
# last read from shm (usually due to an update call)
_last_read: tuple[
@ -90,7 +90,7 @@ class IncrementalFormatter(msgspec.Struct):
def __repr__(self) -> str:
msg = (
f'{type(self)}: ->\n\n'
f'fqsn={self.flow.name}\n'
f'fqsn={self.viz.name}\n'
f'shm_name={self.shm.token["shm_name"]}\n\n'
f'last_vr={self._last_vr}\n'
@ -130,7 +130,7 @@ class IncrementalFormatter(msgspec.Struct):
last_in_view,
) = self.last_read
# TODO: can the renderer just call ``Flow.read()`` directly?
# TODO: can the renderer just call ``Viz.read()`` directly?
# unpack latest source data read
(
xfirst,
@ -337,7 +337,7 @@ class IncrementalFormatter(msgspec.Struct):
if slice_to_inview:
view_changed = self._track_inview_range(view_range)
array = in_view
profiler(f'{self.flow.name} view range slice {view_range}')
profiler(f'{self.viz.name} view range slice {view_range}')
hist = array[:slice_to_head]
@ -370,9 +370,9 @@ class IncrementalFormatter(msgspec.Struct):
# # assert (len(appended) - 1) == append_len
# # assert len(appended) == append_len
# print(
# f'{self.flow.name} APPEND LEN: {append_len}\n'
# f'{self.flow.name} APPENDED: {appended}\n'
# f'{self.flow.name} app_tres: {app_tres}\n'
# f'{self.viz.name} APPEND LEN: {append_len}\n'
# f'{self.viz.name} APPENDED: {appended}\n'
# f'{self.viz.name} app_tres: {app_tres}\n'
# )
# update the last "in view data range"

View File

@ -1,3 +0,0 @@
"""
Super hawt Qt UI components
"""

View File

@ -1,67 +0,0 @@
import sys
from PySide2.QtCharts import QtCharts
from PySide2.QtWidgets import QApplication, QMainWindow
from PySide2.QtCore import Qt, QPointF
from PySide2 import QtGui
import qdarkstyle
data = ((1, 7380, 7520, 7380, 7510, 7324),
(2, 7520, 7580, 7410, 7440, 7372),
(3, 7440, 7650, 7310, 7520, 7434),
(4, 7450, 7640, 7450, 7550, 7480),
(5, 7510, 7590, 7460, 7490, 7502),
(6, 7500, 7590, 7480, 7560, 7512),
(7, 7560, 7830, 7540, 7800, 7584))
app = QApplication([])
# set dark stylesheet
# import pdb; pdb.set_trace()
app.setStyleSheet(qdarkstyle.load_stylesheet_pyside())
series = QtCharts.QCandlestickSeries()
series.setDecreasingColor(Qt.darkRed)
series.setIncreasingColor(Qt.darkGreen)
ma5 = QtCharts.QLineSeries() # 5-days average data line
tm = [] # stores str type data
# in a loop, series and ma5 append corresponding data
for num, o, h, l, c, m in data:
candle = QtCharts.QCandlestickSet(o, h, l, c)
series.append(candle)
ma5.append(QPointF(num, m))
tm.append(str(num))
pen = candle.pen()
# import pdb; pdb.set_trace()
chart = QtCharts.QChart()
# import pdb; pdb.set_trace()
series.setBodyOutlineVisible(False)
series.setCapsVisible(False)
# brush = QtGui.QBrush()
# brush.setColor(Qt.green)
# series.setBrush(brush)
chart.addSeries(series) # candle
chart.addSeries(ma5) # ma5 line
chart.setAnimationOptions(QtCharts.QChart.SeriesAnimations)
chart.createDefaultAxes()
chart.legend().hide()
chart.axisX(series).setCategories(tm)
chart.axisX(ma5).setVisible(False)
view = QtCharts.QChartView(chart)
view.chart().setTheme(QtCharts.QChart.ChartTheme.ChartThemeDark)
view.setRubberBand(QtCharts.QChartView.HorizontalRubberBand)
# chartview.chart().setTheme(QtCharts.QChart.ChartTheme.ChartThemeBlueCerulean)
ui = QMainWindow()
# ui.setGeometry(50, 50, 500, 300)
ui.setCentralWidget(view)
ui.show()
sys.exit(app.exec_())