Drop x-range query from `ChartPlotWidget.maxmin()`
Move the `Viz.datums_range()` call into `Viz.maxmin()` itself thus minimizing the chart `.maxmin()` method to an ultra light wrapper around the viz call. Also move all profiling into the `Viz` method. Adjust `Viz.maxmin()` to return both the (rounded) x-range values which correspond to the range containing the y-domain min and max so that it can be used for up and coming overlay group maxmin calcs.multichartz
							parent
							
								
									cec5ef1d26
								
							
						
					
					
						commit
						aa4a48cb98
					
				| 
						 | 
					@ -50,7 +50,6 @@ from ._cursor import (
 | 
				
			||||||
    ContentsLabel,
 | 
					    ContentsLabel,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from ..data._sharedmem import ShmArray
 | 
					from ..data._sharedmem import ShmArray
 | 
				
			||||||
from ._l1 import L1Labels
 | 
					 | 
				
			||||||
from ._ohlc import BarItems
 | 
					from ._ohlc import BarItems
 | 
				
			||||||
from ._curve import (
 | 
					from ._curve import (
 | 
				
			||||||
    Curve,
 | 
					    Curve,
 | 
				
			||||||
| 
						 | 
					@ -70,12 +69,10 @@ from ..data._source import Symbol
 | 
				
			||||||
from ..log import get_logger
 | 
					from ..log import get_logger
 | 
				
			||||||
from ._interaction import ChartView
 | 
					from ._interaction import ChartView
 | 
				
			||||||
from ._forms import FieldsForm
 | 
					from ._forms import FieldsForm
 | 
				
			||||||
from .._profile import pg_profile_enabled, ms_slower_then
 | 
					 | 
				
			||||||
from ._overlay import PlotItemOverlay
 | 
					from ._overlay import PlotItemOverlay
 | 
				
			||||||
from ._dataviz import Viz
 | 
					from ._dataviz import Viz
 | 
				
			||||||
from ._search import SearchWidget
 | 
					from ._search import SearchWidget
 | 
				
			||||||
from . import _pg_overrides as pgo
 | 
					from . import _pg_overrides as pgo
 | 
				
			||||||
from .._profile import Profiler
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
if TYPE_CHECKING:
 | 
					if TYPE_CHECKING:
 | 
				
			||||||
    from ._display import DisplayState
 | 
					    from ._display import DisplayState
 | 
				
			||||||
| 
						 | 
					@ -857,17 +854,17 @@ class ChartPlotWidget(pg.PlotWidget):
 | 
				
			||||||
        self.sidepane: Optional[FieldsForm] = None
 | 
					        self.sidepane: Optional[FieldsForm] = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # source of our custom interactions
 | 
					        # source of our custom interactions
 | 
				
			||||||
        self.cv = cv = self.mk_vb(name)
 | 
					        self.cv = self.mk_vb(name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pi = pgo.PlotItem(
 | 
					        pi = pgo.PlotItem(
 | 
				
			||||||
            viewBox=cv,
 | 
					            viewBox=self.cv,
 | 
				
			||||||
            name=name,
 | 
					            name=name,
 | 
				
			||||||
            **kwargs,
 | 
					            **kwargs,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        pi.chart_widget = self
 | 
					        pi.chart_widget = self
 | 
				
			||||||
        super().__init__(
 | 
					        super().__init__(
 | 
				
			||||||
            background=hcolor(view_color),
 | 
					            background=hcolor(view_color),
 | 
				
			||||||
            viewBox=cv,
 | 
					            viewBox=self.cv,
 | 
				
			||||||
            # parent=None,
 | 
					            # parent=None,
 | 
				
			||||||
            # plotItem=None,
 | 
					            # plotItem=None,
 | 
				
			||||||
            # antialias=True,
 | 
					            # antialias=True,
 | 
				
			||||||
| 
						 | 
					@ -878,7 +875,9 @@ class ChartPlotWidget(pg.PlotWidget):
 | 
				
			||||||
        # give viewbox as reference to chart
 | 
					        # give viewbox as reference to chart
 | 
				
			||||||
        # allowing for kb controls and interactions on **this** widget
 | 
					        # allowing for kb controls and interactions on **this** widget
 | 
				
			||||||
        # (see our custom view mode in `._interactions.py`)
 | 
					        # (see our custom view mode in `._interactions.py`)
 | 
				
			||||||
        cv.chart = self
 | 
					        self.cv.chart = self
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.pi_overlay: PlotItemOverlay = PlotItemOverlay(self.plotItem)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # ensure internal pi matches
 | 
					        # ensure internal pi matches
 | 
				
			||||||
        assert self.cv is self.plotItem.vb
 | 
					        assert self.cv is self.plotItem.vb
 | 
				
			||||||
| 
						 | 
					@ -907,8 +906,6 @@ class ChartPlotWidget(pg.PlotWidget):
 | 
				
			||||||
        # show background grid
 | 
					        # show background grid
 | 
				
			||||||
        self.showGrid(x=False, y=True, alpha=0.3)
 | 
					        self.showGrid(x=False, y=True, alpha=0.3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.pi_overlay: PlotItemOverlay = PlotItemOverlay(self.plotItem)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # indempotent startup flag for auto-yrange subsys
 | 
					        # indempotent startup flag for auto-yrange subsys
 | 
				
			||||||
        # to detect the "first time" y-domain graphics begin
 | 
					        # to detect the "first time" y-domain graphics begin
 | 
				
			||||||
        # to be shown in the (main) graphics view.
 | 
					        # to be shown in the (main) graphics view.
 | 
				
			||||||
| 
						 | 
					@ -1302,13 +1299,6 @@ class ChartPlotWidget(pg.PlotWidget):
 | 
				
			||||||
        If ``bars_range`` is provided use that range.
 | 
					        If ``bars_range`` is provided use that range.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        profiler = Profiler(
 | 
					 | 
				
			||||||
            msg=f'`{str(self)}.maxmin(name={name})`: `{self.name}`',
 | 
					 | 
				
			||||||
            disabled=not pg_profile_enabled(),
 | 
					 | 
				
			||||||
            ms_threshold=ms_slower_then,
 | 
					 | 
				
			||||||
            delayed=True,
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # TODO: here we should instead look up the ``Viz.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 read directly from shm to avoid copying to memory first
 | 
				
			||||||
        # and then reading it again here.
 | 
					        # and then reading it again here.
 | 
				
			||||||
| 
						 | 
					@ -1316,36 +1306,21 @@ class ChartPlotWidget(pg.PlotWidget):
 | 
				
			||||||
        viz = self._vizs.get(viz_key)
 | 
					        viz = self._vizs.get(viz_key)
 | 
				
			||||||
        if viz is None:
 | 
					        if viz is None:
 | 
				
			||||||
            log.error(f"viz {viz_key} doesn't exist in chart {self.name} !?")
 | 
					            log.error(f"viz {viz_key} doesn't exist in chart {self.name} !?")
 | 
				
			||||||
            key = res = 0, 0
 | 
					            return 0, 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        res = viz.maxmin()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					            res is None
 | 
				
			||||||
 | 
					        ):
 | 
				
			||||||
 | 
					            mxmn = 0, 0
 | 
				
			||||||
 | 
					            if not self._on_screen:
 | 
				
			||||||
 | 
					                self.default_view(do_ds=False)
 | 
				
			||||||
 | 
					                self._on_screen = True
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            (
 | 
					            x_range, mxmn = res
 | 
				
			||||||
                l,
 | 
					 | 
				
			||||||
                _,
 | 
					 | 
				
			||||||
                lbar,
 | 
					 | 
				
			||||||
                rbar,
 | 
					 | 
				
			||||||
                _,
 | 
					 | 
				
			||||||
                r,
 | 
					 | 
				
			||||||
            ) = bars_range or viz.datums_range()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            profiler(f'{self.name} got bars range')
 | 
					        return mxmn
 | 
				
			||||||
            key = lbar, rbar
 | 
					 | 
				
			||||||
            res = viz.maxmin(*key)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (
 | 
					 | 
				
			||||||
                res is None
 | 
					 | 
				
			||||||
            ):
 | 
					 | 
				
			||||||
                log.warning(
 | 
					 | 
				
			||||||
                    f"{viz_key} no mxmn for bars_range => {key} !?"
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
                res = 0, 0
 | 
					 | 
				
			||||||
                if not self._on_screen:
 | 
					 | 
				
			||||||
                    self.default_view(do_ds=False)
 | 
					 | 
				
			||||||
                    self._on_screen = True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        profiler(f'yrange mxmn: {key} -> {res}')
 | 
					 | 
				
			||||||
        # print(f'{viz_key} yrange mxmn: {key} -> {res}')
 | 
					 | 
				
			||||||
        return res
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_viz(
 | 
					    def get_viz(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,6 +60,7 @@ from ..log import get_logger
 | 
				
			||||||
from .._profile import (
 | 
					from .._profile import (
 | 
				
			||||||
    Profiler,
 | 
					    Profiler,
 | 
				
			||||||
    pg_profile_enabled,
 | 
					    pg_profile_enabled,
 | 
				
			||||||
 | 
					    ms_slower_then,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -276,7 +277,10 @@ class Viz(msgspec.Struct):  # , frozen=True):
 | 
				
			||||||
    ] = (None, None)
 | 
					    ] = (None, None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # cache of y-range values per x-range input.
 | 
					    # cache of y-range values per x-range input.
 | 
				
			||||||
    _mxmns: dict[tuple[int, int], tuple[float, float]] = {}
 | 
					    _mxmns: dict[
 | 
				
			||||||
 | 
					        tuple[int, int],
 | 
				
			||||||
 | 
					        tuple[float, float],
 | 
				
			||||||
 | 
					    ] = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def shm(self) -> ShmArray:
 | 
					    def shm(self) -> ShmArray:
 | 
				
			||||||
| 
						 | 
					@ -324,32 +328,29 @@ class Viz(msgspec.Struct):  # , frozen=True):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def maxmin(
 | 
					    def maxmin(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
        lbar: int,
 | 
					 | 
				
			||||||
        rbar: int,
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # TODO: drop this right?
 | 
				
			||||||
 | 
					        bars_range: Optional[tuple[
 | 
				
			||||||
 | 
					            int, int, int, int, int, int
 | 
				
			||||||
 | 
					        ]] = None,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        x_range: slice | tuple[int, int] | None = None,
 | 
				
			||||||
        use_caching: bool = True,
 | 
					        use_caching: bool = True,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ) -> Optional[tuple[float, float]]:
 | 
					    ) -> tuple[float, float] | None:
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        Compute the cached max and min y-range values for a given
 | 
					        Compute the cached max and min y-range values for a given
 | 
				
			||||||
        x-range determined by ``lbar`` and ``rbar`` or ``None``
 | 
					        x-range determined by ``lbar`` and ``rbar`` or ``None``
 | 
				
			||||||
        if no range can be determined (yet).
 | 
					        if no range can be determined (yet).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        # TODO: hash the slice instead maybe?
 | 
					        name = self.name
 | 
				
			||||||
        # https://stackoverflow.com/a/29980872
 | 
					        profiler = Profiler(
 | 
				
			||||||
        rkey = (round(lbar), round(rbar))
 | 
					            msg=f'{name} -> `{str(self)}.maxmin()`',
 | 
				
			||||||
 | 
					            disabled=not pg_profile_enabled(),
 | 
				
			||||||
        do_print: bool = False
 | 
					            ms_threshold=ms_slower_then,
 | 
				
			||||||
        if use_caching:
 | 
					            delayed=True,
 | 
				
			||||||
            cached_result = self._mxmns.get(rkey)
 | 
					        )
 | 
				
			||||||
            if cached_result:
 | 
					 | 
				
			||||||
                if do_print:
 | 
					 | 
				
			||||||
                    print(
 | 
					 | 
				
			||||||
                        f'{self.name} CACHED maxmin\n'
 | 
					 | 
				
			||||||
                        f'{rkey} -> {cached_result}'
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                return cached_result
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        shm = self.shm
 | 
					        shm = self.shm
 | 
				
			||||||
        if shm is None:
 | 
					        if shm is None:
 | 
				
			||||||
| 
						 | 
					@ -357,6 +358,41 @@ class Viz(msgspec.Struct):  # , frozen=True):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        arr = shm.array
 | 
					        arr = shm.array
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if x_range is None:
 | 
				
			||||||
 | 
					            (
 | 
				
			||||||
 | 
					                l,
 | 
				
			||||||
 | 
					                _,
 | 
				
			||||||
 | 
					                lbar,
 | 
				
			||||||
 | 
					                rbar,
 | 
				
			||||||
 | 
					                _,
 | 
				
			||||||
 | 
					                r,
 | 
				
			||||||
 | 
					            ) = (
 | 
				
			||||||
 | 
					                # TODO: drop this yah?
 | 
				
			||||||
 | 
					                bars_range
 | 
				
			||||||
 | 
					                or self.datums_range()
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            profiler(f'{self.name} got bars range')
 | 
				
			||||||
 | 
					            x_range = lbar, rbar
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # TODO: hash the slice instead maybe?
 | 
				
			||||||
 | 
					        # https://stackoverflow.com/a/29980872
 | 
				
			||||||
 | 
					        ixrng = (round(lbar), round(rbar))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        do_print: bool = False
 | 
				
			||||||
 | 
					        if use_caching:
 | 
				
			||||||
 | 
					            cached_result = self._mxmns.get(ixrng)
 | 
				
			||||||
 | 
					            if cached_result:
 | 
				
			||||||
 | 
					                if do_print:
 | 
				
			||||||
 | 
					                    print(
 | 
				
			||||||
 | 
					                        f'{self.name} CACHED maxmin\n'
 | 
				
			||||||
 | 
					                        f'{ixrng} -> {cached_result}'
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                return (
 | 
				
			||||||
 | 
					                    ixrng,
 | 
				
			||||||
 | 
					                    cached_result,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # get relative slice indexes into array
 | 
					        # get relative slice indexes into array
 | 
				
			||||||
        if self.index_field == 'time':
 | 
					        if self.index_field == 'time':
 | 
				
			||||||
            read_slc = slice_from_time(
 | 
					            read_slc = slice_from_time(
 | 
				
			||||||
| 
						 | 
					@ -376,7 +412,10 @@ class Viz(msgspec.Struct):  # , frozen=True):
 | 
				
			||||||
        slice_view = arr[read_slc]
 | 
					        slice_view = arr[read_slc]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not slice_view.size:
 | 
					        if not slice_view.size:
 | 
				
			||||||
            log.warning(f'{self.name} no maxmin in view?')
 | 
					            log.warning(
 | 
				
			||||||
 | 
					                f'{self.name} no maxmin in view?\n'
 | 
				
			||||||
 | 
					                f"{name} no mxmn for bars_range => {ixrng} !?"
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            return None
 | 
					            return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        elif self.yrange:
 | 
					        elif self.yrange:
 | 
				
			||||||
| 
						 | 
					@ -384,9 +423,8 @@ class Viz(msgspec.Struct):  # , frozen=True):
 | 
				
			||||||
            if do_print:
 | 
					            if do_print:
 | 
				
			||||||
                print(
 | 
					                print(
 | 
				
			||||||
                    f'{self.name} M4 maxmin:\n'
 | 
					                    f'{self.name} M4 maxmin:\n'
 | 
				
			||||||
                    f'{rkey} -> {mxmn}'
 | 
					                    f'{ixrng} -> {mxmn}'
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
 | 
					 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            if self.is_ohlc:
 | 
					            if self.is_ohlc:
 | 
				
			||||||
                ylow = np.min(slice_view['low'])
 | 
					                ylow = np.min(slice_view['low'])
 | 
				
			||||||
| 
						 | 
					@ -404,7 +442,7 @@ class Viz(msgspec.Struct):  # , frozen=True):
 | 
				
			||||||
                s = 3
 | 
					                s = 3
 | 
				
			||||||
                print(
 | 
					                print(
 | 
				
			||||||
                    f'{self.name} MANUAL ohlc={self.is_ohlc} maxmin:\n'
 | 
					                    f'{self.name} MANUAL ohlc={self.is_ohlc} maxmin:\n'
 | 
				
			||||||
                    f'{rkey} -> {mxmn}\n'
 | 
					                    f'{ixrng} -> {mxmn}\n'
 | 
				
			||||||
                    f'read_slc: {read_slc}\n'
 | 
					                    f'read_slc: {read_slc}\n'
 | 
				
			||||||
                    # f'abs_slc: {slice_view["index"]}\n'
 | 
					                    # f'abs_slc: {slice_view["index"]}\n'
 | 
				
			||||||
                    f'first {s}:\n{slice_view[:s]}\n'
 | 
					                    f'first {s}:\n{slice_view[:s]}\n'
 | 
				
			||||||
| 
						 | 
					@ -413,9 +451,12 @@ class Viz(msgspec.Struct):  # , frozen=True):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # cache result for input range
 | 
					        # cache result for input range
 | 
				
			||||||
        assert mxmn
 | 
					        assert mxmn
 | 
				
			||||||
        self._mxmns[rkey] = mxmn
 | 
					        self._mxmns[ixrng] = mxmn
 | 
				
			||||||
 | 
					        profiler(f'yrange mxmn cacheing: {x_range} -> {mxmn}')
 | 
				
			||||||
        return mxmn
 | 
					        return (
 | 
				
			||||||
 | 
					            ixrng,
 | 
				
			||||||
 | 
					            mxmn,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def view_range(self) -> tuple[int, int]:
 | 
					    def view_range(self) -> tuple[int, int]:
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue