Move plotitem overlaying into a `.overlay_plotitem()`
							parent
							
								
									d0693e2967
								
							
						
					
					
						commit
						ca9973e619
					
				| 
						 | 
					@ -19,8 +19,6 @@ High level chart-widget apis.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
'''
 | 
					'''
 | 
				
			||||||
from __future__ import annotations
 | 
					from __future__ import annotations
 | 
				
			||||||
from functools import partial
 | 
					 | 
				
			||||||
from dataclasses import dataclass
 | 
					 | 
				
			||||||
from typing import Optional
 | 
					from typing import Optional
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from PyQt5 import QtCore, QtWidgets
 | 
					from PyQt5 import QtCore, QtWidgets
 | 
				
			||||||
| 
						 | 
					@ -383,8 +381,9 @@ class LinkedSplits(QWidget):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        style: str = 'bar',
 | 
					        style: str = 'bar',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ) -> 'ChartPlotWidget':
 | 
					    ) -> ChartPlotWidget:
 | 
				
			||||||
        '''Start up and show main (price) chart and all linked subcharts.
 | 
					        '''
 | 
				
			||||||
 | 
					        Start up and show main (price) chart and all linked subcharts.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        The data input struct array must include OHLC fields.
 | 
					        The data input struct array must include OHLC fields.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -537,7 +536,7 @@ class LinkedSplits(QWidget):
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            self.cursor.contents_labels.add_label(
 | 
					            self.cursor.contents_labels.add_label(
 | 
				
			||||||
                cpw,
 | 
					                cpw,
 | 
				
			||||||
                'ohlc',
 | 
					                name,
 | 
				
			||||||
                anchor_at=('top', 'left'),
 | 
					                anchor_at=('top', 'left'),
 | 
				
			||||||
                update_func=ContentsLabel.update_from_ohlc,
 | 
					                update_func=ContentsLabel.update_from_ohlc,
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
| 
						 | 
					@ -611,11 +610,11 @@ class LinkedSplits(QWidget):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# import pydantic
 | 
					# import pydantic
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# class ArrayScene(pydantic.BaseModel):
 | 
					# class Graphics(pydantic.BaseModel):
 | 
				
			||||||
#     '''
 | 
					#     '''
 | 
				
			||||||
#     Data-AGGRegate: high level API onto multiple (categorized)
 | 
					#     Data-AGGRegate: high level API onto multiple (categorized)
 | 
				
			||||||
#     ``ShmArray``s with high level processing routines mostly for
 | 
					#     ``ShmArray``s with high level processing routines for
 | 
				
			||||||
#     graphics summary and display.
 | 
					#     graphics computations and display.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#     '''
 | 
					#     '''
 | 
				
			||||||
#     arrays: dict[str, np.ndarray] = {}
 | 
					#     arrays: dict[str, np.ndarray] = {}
 | 
				
			||||||
| 
						 | 
					@ -738,7 +737,7 @@ class ChartPlotWidget(pg.PlotWidget):
 | 
				
			||||||
        self.default_view()
 | 
					        self.default_view()
 | 
				
			||||||
        self.cv.enable_auto_yrange()
 | 
					        self.cv.enable_auto_yrange()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.overlay: PlotItemOverlay = PlotItemOverlay(self.plotItem)
 | 
					        self.pi_overlay: PlotItemOverlay = PlotItemOverlay(self.plotItem)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def resume_all_feeds(self):
 | 
					    def resume_all_feeds(self):
 | 
				
			||||||
        for feed in self._feeds.values():
 | 
					        for feed in self._feeds.values():
 | 
				
			||||||
| 
						 | 
					@ -849,7 +848,7 @@ class ChartPlotWidget(pg.PlotWidget):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # adds all bar/candle graphics objects for each data point in
 | 
					        # adds all bar/candle graphics objects for each data point in
 | 
				
			||||||
        # the np array buffer to be drawn on next render cycle
 | 
					        # the np array buffer to be drawn on next render cycle
 | 
				
			||||||
        self.addItem(graphics)
 | 
					        self.plotItem.addItem(graphics)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # draw after to allow self.scene() to work...
 | 
					        # draw after to allow self.scene() to work...
 | 
				
			||||||
        graphics.draw_from_data(data)
 | 
					        graphics.draw_from_data(data)
 | 
				
			||||||
| 
						 | 
					@ -860,6 +859,57 @@ class ChartPlotWidget(pg.PlotWidget):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return graphics, data_key
 | 
					        return graphics, data_key
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def overlay_plotitem(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        name: str,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ) -> pg.PlotItem:
 | 
				
			||||||
 | 
					        # Custom viewbox impl
 | 
				
			||||||
 | 
					        cv = self.mk_vb(name)
 | 
				
			||||||
 | 
					        cv.chart = self
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # xaxis = DynamicDateAxis(
 | 
				
			||||||
 | 
					        #     orientation='bottom',
 | 
				
			||||||
 | 
					        #     linkedsplits=self.linked,
 | 
				
			||||||
 | 
					        # )
 | 
				
			||||||
 | 
					        yaxis = PriceAxis(
 | 
				
			||||||
 | 
					            orientation='right',
 | 
				
			||||||
 | 
					            linkedsplits=self.linked,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        plotitem = pg.PlotItem(
 | 
				
			||||||
 | 
					            parent=self.plotItem,
 | 
				
			||||||
 | 
					            name=name,
 | 
				
			||||||
 | 
					            enableMenu=False,
 | 
				
			||||||
 | 
					            viewBox=cv,
 | 
				
			||||||
 | 
					            axisItems={
 | 
				
			||||||
 | 
					                # 'bottom': xaxis,
 | 
				
			||||||
 | 
					                'right': yaxis,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            default_axes=[],
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        # plotitem.setAxisItems(
 | 
				
			||||||
 | 
					        #     add_to_layout=False,
 | 
				
			||||||
 | 
					        #     axisItems={
 | 
				
			||||||
 | 
					        #         'bottom': xaxis,
 | 
				
			||||||
 | 
					        #         'right': yaxis,
 | 
				
			||||||
 | 
					        #     },
 | 
				
			||||||
 | 
					        # )
 | 
				
			||||||
 | 
					        # plotite.hideAxis('right')
 | 
				
			||||||
 | 
					        # plotite.hideAxis('bottom')
 | 
				
			||||||
 | 
					        # plotitem.addItem(curve)
 | 
				
			||||||
 | 
					        cv.enable_auto_yrange()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # plotitem.enableAutoRange(axis='y')
 | 
				
			||||||
 | 
					        plotitem.hideButtons()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.pi_overlay.add_plotitem(
 | 
				
			||||||
 | 
					            plotitem,
 | 
				
			||||||
 | 
					            # only link x-axes,
 | 
				
			||||||
 | 
					            link_axes=(0,),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        return plotitem
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def draw_curve(
 | 
					    def draw_curve(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -868,7 +918,6 @@ class ChartPlotWidget(pg.PlotWidget):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        array_key: Optional[str] = None,
 | 
					        array_key: Optional[str] = None,
 | 
				
			||||||
        overlay: bool = False,
 | 
					        overlay: bool = False,
 | 
				
			||||||
        separate_axes: bool = False,
 | 
					 | 
				
			||||||
        color: Optional[str] = None,
 | 
					        color: Optional[str] = None,
 | 
				
			||||||
        add_label: bool = True,
 | 
					        add_label: bool = True,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -907,11 +956,11 @@ class ChartPlotWidget(pg.PlotWidget):
 | 
				
			||||||
            **pdi_kwargs,
 | 
					            **pdi_kwargs,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # XXX: see explanation for differenct caching modes:
 | 
					        # XXX: see explanation for different caching modes:
 | 
				
			||||||
        # https://stackoverflow.com/a/39410081
 | 
					        # https://stackoverflow.com/a/39410081
 | 
				
			||||||
        # seems to only be useful if we don't re-generate the entire
 | 
					        # seems to only be useful if we don't re-generate the entire
 | 
				
			||||||
        # QPainterPath every time
 | 
					        # QPainterPath every time
 | 
				
			||||||
        # curve.curve.setCacheMode(QtWidgets.QGraphicsItem.DeviceCoordinateCache)
 | 
					        # curve.setCacheMode(QtWidgets.QGraphicsItem.DeviceCoordinateCache)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # don't ever use this - it's a colossal nightmare of artefacts
 | 
					        # don't ever use this - it's a colossal nightmare of artefacts
 | 
				
			||||||
        # and is disastrous for performance.
 | 
					        # and is disastrous for performance.
 | 
				
			||||||
| 
						 | 
					@ -921,83 +970,28 @@ class ChartPlotWidget(pg.PlotWidget):
 | 
				
			||||||
        self._graphics[name] = curve
 | 
					        self._graphics[name] = curve
 | 
				
			||||||
        self._arrays[data_key] = data
 | 
					        self._arrays[data_key] = data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        pi = self.plotItem
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # TODO: this probably needs its own method?
 | 
				
			||||||
        if overlay:
 | 
					        if overlay:
 | 
				
			||||||
            # anchor_at = ('bottom', 'left')
 | 
					            # anchor_at = ('bottom', 'left')
 | 
				
			||||||
            self._overlays[name] = None
 | 
					            self._overlays[name] = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if separate_axes:
 | 
					            if isinstance(overlay, pg.PlotItem):
 | 
				
			||||||
 | 
					                if overlay not in self.pi_overlay.overlays:
 | 
				
			||||||
                # Custom viewbox impl
 | 
					                    raise RuntimeError(
 | 
				
			||||||
                cv = self.mk_vb(name)
 | 
					                            f'{overlay} must be from `.plotitem_overlay()`'
 | 
				
			||||||
 | 
					 | 
				
			||||||
                def maxmin():
 | 
					 | 
				
			||||||
                    return self.maxmin(name=data_key)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                # ensure view maxmin is computed from correct array
 | 
					 | 
				
			||||||
                # cv._maxmin = partial(self.maxmin, name=data_key)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                cv._maxmin = maxmin
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                cv.chart = self
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                # xaxis = DynamicDateAxis(
 | 
					 | 
				
			||||||
                #     orientation='bottom',
 | 
					 | 
				
			||||||
                #     linkedsplits=self.linked,
 | 
					 | 
				
			||||||
                # )
 | 
					 | 
				
			||||||
                yaxis = PriceAxis(
 | 
					 | 
				
			||||||
                    orientation='right',
 | 
					 | 
				
			||||||
                    linkedsplits=self.linked,
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                plotitem = pg.PlotItem(
 | 
					 | 
				
			||||||
                    parent=self.plotItem,
 | 
					 | 
				
			||||||
                    name=name,
 | 
					 | 
				
			||||||
                    enableMenu=False,
 | 
					 | 
				
			||||||
                    viewBox=cv,
 | 
					 | 
				
			||||||
                    axisItems={
 | 
					 | 
				
			||||||
                        # 'bottom': xaxis,
 | 
					 | 
				
			||||||
                        'right': yaxis,
 | 
					 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                    default_axes=[],
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
                # plotitem.setAxisItems(
 | 
					 | 
				
			||||||
                #     add_to_layout=False,
 | 
					 | 
				
			||||||
                #     axisItems={
 | 
					 | 
				
			||||||
                #         'bottom': xaxis,
 | 
					 | 
				
			||||||
                #         'right': yaxis,
 | 
					 | 
				
			||||||
                #     },
 | 
					 | 
				
			||||||
                # )
 | 
					 | 
				
			||||||
                # plotite.hideAxis('right')
 | 
					 | 
				
			||||||
                # plotite.hideAxis('bottom')
 | 
					 | 
				
			||||||
                plotitem.addItem(curve)
 | 
					 | 
				
			||||||
                cv.enable_auto_yrange()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                # config
 | 
					 | 
				
			||||||
                # plotitem.setAutoVisible(y=True)
 | 
					 | 
				
			||||||
                # plotitem.enableAutoRange(axis='y')
 | 
					 | 
				
			||||||
                plotitem.hideButtons()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                self.overlay.add_plotitem(
 | 
					 | 
				
			||||||
                    plotitem,
 | 
					 | 
				
			||||||
                    # only link x-axes,
 | 
					 | 
				
			||||||
                    link_axes=(0,),
 | 
					 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
 | 
					                pi = overlay
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
                # this intnernally calls `PlotItem.addItem()` on the
 | 
					 | 
				
			||||||
                # graphics object
 | 
					 | 
				
			||||||
                self.addItem(curve)
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            # this intnernally calls `PlotItem.addItem()` on the
 | 
					 | 
				
			||||||
            # graphics object
 | 
					 | 
				
			||||||
            self.addItem(curve)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # anchor_at = ('top', 'left')
 | 
					            # anchor_at = ('top', 'left')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # TODO: something instead of stickies for overlays
 | 
					            # TODO: something instead of stickies for overlays
 | 
				
			||||||
            # (we need something that avoids clutter on x-axis).
 | 
					            # (we need something that avoids clutter on x-axis).
 | 
				
			||||||
            self._add_sticky(name, bg_color=color)
 | 
					            self._add_sticky(name, bg_color=color)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        pi.addItem(curve)
 | 
				
			||||||
        return curve, data_key
 | 
					        return curve, data_key
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # TODO: make this a ctx mngr
 | 
					    # TODO: make this a ctx mngr
 | 
				
			||||||
| 
						 | 
					@ -1036,7 +1030,8 @@ class ChartPlotWidget(pg.PlotWidget):
 | 
				
			||||||
        **kwargs,
 | 
					        **kwargs,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ) -> pg.GraphicsObject:
 | 
					    ) -> pg.GraphicsObject:
 | 
				
			||||||
        '''Update the named internal graphics from ``array``.
 | 
					        '''
 | 
				
			||||||
 | 
					        Update the named internal graphics from ``array``.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        self._arrays[self.name] = array
 | 
					        self._arrays[self.name] = array
 | 
				
			||||||
| 
						 | 
					@ -1053,7 +1048,8 @@ class ChartPlotWidget(pg.PlotWidget):
 | 
				
			||||||
        **kwargs,
 | 
					        **kwargs,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ) -> pg.GraphicsObject:
 | 
					    ) -> pg.GraphicsObject:
 | 
				
			||||||
        '''Update the named internal graphics from ``array``.
 | 
					        '''
 | 
				
			||||||
 | 
					        Update the named internal graphics from ``array``.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        assert len(array)
 | 
					        assert len(array)
 | 
				
			||||||
| 
						 | 
					@ -1123,7 +1119,7 @@ class ChartPlotWidget(pg.PlotWidget):
 | 
				
			||||||
    def get_index(self, time: float) -> int:
 | 
					    def get_index(self, time: float) -> int:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # TODO: this should go onto some sort of
 | 
					        # TODO: this should go onto some sort of
 | 
				
			||||||
        # data-view strimg thinger..right?
 | 
					        # data-view thinger..right?
 | 
				
			||||||
        ohlc = self._shm.array
 | 
					        ohlc = self._shm.array
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # XXX: not sure why the time is so off here
 | 
					        # XXX: not sure why the time is so off here
 | 
				
			||||||
| 
						 | 
					@ -1178,15 +1174,9 @@ class ChartPlotWidget(pg.PlotWidget):
 | 
				
			||||||
            yhigh = np.nanmax(bars['high'])
 | 
					            yhigh = np.nanmax(bars['high'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            try:
 | 
					 | 
				
			||||||
            view = bars[name or self.data_key]
 | 
					            view = bars[name or self.data_key]
 | 
				
			||||||
            except:
 | 
					 | 
				
			||||||
                breakpoint()
 | 
					 | 
				
			||||||
            # if self.data_key != 'volume':
 | 
					 | 
				
			||||||
            # else:
 | 
					 | 
				
			||||||
            #     view = bars
 | 
					 | 
				
			||||||
            ylow = np.nanmin(view)
 | 
					            ylow = np.nanmin(view)
 | 
				
			||||||
            yhigh = np.nanmax(view)
 | 
					            yhigh = np.nanmax(view)
 | 
				
			||||||
            # print(f'{(ylow, yhigh)}')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # print(f'{(ylow, yhigh)}')
 | 
				
			||||||
        return ylow, yhigh
 | 
					        return ylow, yhigh
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue