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_connectionpre_viz_calls
							parent
							
								
									42e0048b7c
								
							
						
					
					
						commit
						47b9e59655
					
				| 
						 | 
				
			
			@ -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,7 +274,7 @@ class ContentsLabels:
 | 
			
		|||
    ) -> None:
 | 
			
		||||
        for chart, name, label, update in self._labels:
 | 
			
		||||
 | 
			
		||||
            flow = chart._flows[name]
 | 
			
		||||
            viz = chart.get_viz(name)
 | 
			
		||||
            array = flow.shm.array
 | 
			
		||||
 | 
			
		||||
            if not (
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -502,7 +502,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
 | 
			
		||||
| 
						 | 
				
			
			@ -806,7 +806,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:
 | 
			
		||||
| 
						 | 
				
			
			@ -897,7 +897,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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -933,7 +933,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