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_connectionmultichartz
parent
d3a40678ff
commit
190c792515
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -76,7 +76,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
|
||||
|
@ -111,7 +111,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:
|
||||
|
@ -218,7 +218,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
|
||||
|
@ -299,14 +299,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']]
|
||||
|
@ -381,7 +381,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
|
||||
|
@ -439,8 +439,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())
|
||||
|
@ -516,7 +516,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}`',
|
||||
|
@ -560,7 +560,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),
|
||||
)
|
||||
|
||||
|
@ -627,7 +627,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,
|
||||
|
@ -746,7 +746,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()
|
||||
|
@ -777,26 +777,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,
|
||||
)
|
||||
|
@ -810,7 +810,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,
|
||||
)
|
||||
|
@ -819,7 +819,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(
|
||||
|
@ -864,21 +864,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,
|
||||
|
@ -887,7 +887,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,
|
||||
)
|
||||
|
@ -903,9 +903,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(
|
||||
|
@ -935,12 +935,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):
|
||||
|
@ -1050,11 +1050,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
|
||||
|
@ -1277,7 +1277,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,
|
||||
|
@ -1298,8 +1298,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(
|
||||
|
@ -1310,7 +1310,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,
|
||||
|
@ -1331,8 +1331,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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -42,7 +42,7 @@ from ._compression import (
|
|||
if TYPE_CHECKING:
|
||||
from ._flows import (
|
||||
Renderer,
|
||||
Flow,
|
||||
Viz,
|
||||
)
|
||||
from .._profile import Profiler
|
||||
|
||||
|
@ -72,7 +72,7 @@ class IncrementalFormatter(msgspec.Struct):
|
|||
|
||||
'''
|
||||
shm: ShmArray
|
||||
flow: Flow
|
||||
viz: Viz
|
||||
|
||||
# last read from shm (usually due to an update call)
|
||||
_last_read: tuple[
|
||||
|
@ -89,7 +89,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'
|
||||
|
@ -129,7 +129,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,
|
||||
|
@ -336,7 +336,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]
|
||||
|
||||
|
@ -369,9 +369,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"
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
"""
|
||||
Super hawt Qt UI components
|
||||
"""
|
|
@ -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_())
|
Loading…
Reference in New Issue