Make `ChartPlotWidget.default_view()` pin to L1
Instead of using a guess about how many x-indexes to reset the last datum in-view to, calculate and shift the latest index such that it's just before any L1 spread labels on the y-axis. This makes the view placement "widget aware" and gives a much more cross-display UX. Summary: - add `ChartPlotWidget.pre_l1_x()` which returns a `tuple` of x view-coord points for the absolute x-pos and length of any L1 line/labels - make `.default_view()` only shift to see the xlast just outside the l1 but keep whatever view range xfirst as the first datum in view - drop `LevelLine.right_point()` since this is now just a `.pre_l1_x()` call and can be retrieved from the line's internal chart ref - drop `._style.bars_from/to_..` vars since we aren't using hard coded offsets any moremkts_backup
							parent
							
								
									01b594e828
								
							
						
					
					
						commit
						4af941566a
					
				| 
						 | 
					@ -25,7 +25,6 @@ from PyQt5.QtCore import QPointF
 | 
				
			||||||
from PyQt5.QtWidgets import QGraphicsPathItem
 | 
					from PyQt5.QtWidgets import QGraphicsPathItem
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if TYPE_CHECKING:
 | 
					if TYPE_CHECKING:
 | 
				
			||||||
    from ._axes import PriceAxis
 | 
					 | 
				
			||||||
    from ._chart import ChartPlotWidget
 | 
					    from ._chart import ChartPlotWidget
 | 
				
			||||||
    from ._label import Label
 | 
					    from ._label import Label
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,7 +25,7 @@ from PyQt5 import QtCore, QtWidgets
 | 
				
			||||||
from PyQt5.QtCore import (
 | 
					from PyQt5.QtCore import (
 | 
				
			||||||
    Qt,
 | 
					    Qt,
 | 
				
			||||||
    QLineF,
 | 
					    QLineF,
 | 
				
			||||||
    QPointF,
 | 
					    # QPointF,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from PyQt5.QtWidgets import (
 | 
					from PyQt5.QtWidgets import (
 | 
				
			||||||
    QFrame,
 | 
					    QFrame,
 | 
				
			||||||
| 
						 | 
					@ -56,8 +56,6 @@ from ._style import (
 | 
				
			||||||
    CHART_MARGINS,
 | 
					    CHART_MARGINS,
 | 
				
			||||||
    _xaxis_at,
 | 
					    _xaxis_at,
 | 
				
			||||||
    _min_points_to_show,
 | 
					    _min_points_to_show,
 | 
				
			||||||
    _bars_from_right_in_follow_mode,
 | 
					 | 
				
			||||||
    _bars_to_left_in_follow_mode,
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from ..data.feed import Feed
 | 
					from ..data.feed import Feed
 | 
				
			||||||
from ..data._source import Symbol
 | 
					from ..data._source import Symbol
 | 
				
			||||||
| 
						 | 
					@ -846,29 +844,62 @@ class ChartPlotWidget(pg.PlotWidget):
 | 
				
			||||||
            QLineF(lbar, 0, rbar, 0)
 | 
					            QLineF(lbar, 0, rbar, 0)
 | 
				
			||||||
        ).length()
 | 
					        ).length()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def default_view(
 | 
					    def pre_l1_x(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
        index: int = -1,
 | 
					        view_coords: bool = False,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ) -> None:
 | 
					    ) -> tuple[float, float]:
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        Return the scene x-coord for the value just before
 | 
				
			||||||
 | 
					        the L1 labels on the y-axis as well as the length
 | 
				
			||||||
 | 
					        of that L1 label from the y-axis.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        l1_len = self._max_l1_line_len
 | 
				
			||||||
 | 
					        ryaxis = self.getAxis('right')
 | 
				
			||||||
 | 
					        ryaxis_x = ryaxis.pos().x()
 | 
				
			||||||
 | 
					        up_to_l1_sc = ryaxis_x - l1_len
 | 
				
			||||||
 | 
					        if not view_coords:
 | 
				
			||||||
 | 
					            return up_to_l1_sc, l1_len
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            view = self.view
 | 
				
			||||||
 | 
					            line = view.mapToView(
 | 
				
			||||||
 | 
					                QLineF(up_to_l1_sc, 0, ryaxis_x, 0)
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            return line.x1(), line.length()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def default_view(self) -> None:
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        Set the view box to the "default" startup view of the scene.
 | 
					        Set the view box to the "default" startup view of the scene.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            xlast = self._arrays[self.name][index]['index']
 | 
					            index = self._arrays[self.name]['index']
 | 
				
			||||||
        except IndexError:
 | 
					        except IndexError:
 | 
				
			||||||
            log.warning(f'array for {self.name} not loaded yet?')
 | 
					            log.warning(f'array for {self.name} not loaded yet?')
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        begin = xlast - 1000
 | 
					        xfirst, xlast = index[0], index[-1]
 | 
				
			||||||
        end = xlast + _bars_from_right_in_follow_mode
 | 
					        view = self.view
 | 
				
			||||||
 | 
					        vr = view.viewRange()
 | 
				
			||||||
 | 
					        marker_pos, l1_len = self.pre_l1_x(view_coords=True)
 | 
				
			||||||
 | 
					        end = xlast + l1_len
 | 
				
			||||||
 | 
					        xl = vr[0][0]
 | 
				
			||||||
 | 
					        begin = max(xl, xfirst)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # print(
 | 
				
			||||||
 | 
					        #     f'view range: {vr}\n'
 | 
				
			||||||
 | 
					        #     f'xlast: {xlast}\n'
 | 
				
			||||||
 | 
					        #     f'marker pos: {marker_pos}\n'
 | 
				
			||||||
 | 
					        #     f'l1 len: {l1_len}\n'
 | 
				
			||||||
 | 
					        #     f'begin: {begin}\n'
 | 
				
			||||||
 | 
					        #     f'end: {end}\n'
 | 
				
			||||||
 | 
					        # )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # remove any custom user yrange setttings
 | 
					        # remove any custom user yrange setttings
 | 
				
			||||||
        if self._static_yrange == 'axis':
 | 
					        if self._static_yrange == 'axis':
 | 
				
			||||||
            self._static_yrange = None
 | 
					            self._static_yrange = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        view = self.view
 | 
					 | 
				
			||||||
        view.setXRange(
 | 
					        view.setXRange(
 | 
				
			||||||
            min=begin,
 | 
					            min=begin,
 | 
				
			||||||
            max=end,
 | 
					            max=end,
 | 
				
			||||||
| 
						 | 
					@ -1228,7 +1259,6 @@ class ChartPlotWidget(pg.PlotWidget):
 | 
				
			||||||
        ifirst = array[0]['index']
 | 
					        ifirst = array[0]['index']
 | 
				
			||||||
        # slice data by offset from the first index
 | 
					        # slice data by offset from the first index
 | 
				
			||||||
        # available in the passed datum set.
 | 
					        # available in the passed datum set.
 | 
				
			||||||
        start = lbar - ifirst
 | 
					 | 
				
			||||||
        return array[lbar - ifirst:(rbar - ifirst) + 1]
 | 
					        return array[lbar - ifirst:(rbar - ifirst) + 1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def maxmin(
 | 
					    def maxmin(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,7 +20,7 @@ Lines for orders, alerts, L2.
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
from functools import partial
 | 
					from functools import partial
 | 
				
			||||||
from math import floor
 | 
					from math import floor
 | 
				
			||||||
from typing import Tuple, Optional, List, Callable
 | 
					from typing import Optional, Callable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pyqtgraph as pg
 | 
					import pyqtgraph as pg
 | 
				
			||||||
from pyqtgraph import Point, functions as fn
 | 
					from pyqtgraph import Point, functions as fn
 | 
				
			||||||
| 
						 | 
					@ -32,7 +32,6 @@ from ._anchors import (
 | 
				
			||||||
    marker_right_points,
 | 
					    marker_right_points,
 | 
				
			||||||
    vbr_left,
 | 
					    vbr_left,
 | 
				
			||||||
    right_axis,
 | 
					    right_axis,
 | 
				
			||||||
    # pp_tight_and_right,  # wanna keep it straight in the long run
 | 
					 | 
				
			||||||
    gpath_pin,
 | 
					    gpath_pin,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from ..calc import humanize
 | 
					from ..calc import humanize
 | 
				
			||||||
| 
						 | 
					@ -104,8 +103,8 @@ class LevelLine(pg.InfiniteLine):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # list of labels anchored at one of the 2 line endpoints
 | 
					        # list of labels anchored at one of the 2 line endpoints
 | 
				
			||||||
        # inside the viewbox
 | 
					        # inside the viewbox
 | 
				
			||||||
        self._labels: List[Label] = []
 | 
					        self._labels: list[Label] = []
 | 
				
			||||||
        self._markers: List[(int, Label)] = []
 | 
					        self._markers: list[(int, Label)] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # whenever this line is moved trigger label updates
 | 
					        # whenever this line is moved trigger label updates
 | 
				
			||||||
        self.sigPositionChanged.connect(self.on_pos_change)
 | 
					        self.sigPositionChanged.connect(self.on_pos_change)
 | 
				
			||||||
| 
						 | 
					@ -124,7 +123,7 @@ class LevelLine(pg.InfiniteLine):
 | 
				
			||||||
        self._y_incr_mult = 1 / chart.linked.symbol.tick_size
 | 
					        self._y_incr_mult = 1 / chart.linked.symbol.tick_size
 | 
				
			||||||
        self._right_end_sc: float = 0
 | 
					        self._right_end_sc: float = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def txt_offsets(self) -> Tuple[int, int]:
 | 
					    def txt_offsets(self) -> tuple[int, int]:
 | 
				
			||||||
        return 0, 0
 | 
					        return 0, 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
| 
						 | 
					@ -315,17 +314,6 @@ class LevelLine(pg.InfiniteLine):
 | 
				
			||||||
        # TODO: enter labels edit mode
 | 
					        # TODO: enter labels edit mode
 | 
				
			||||||
        print(f'double click {ev}')
 | 
					        print(f'double click {ev}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def right_point(
 | 
					 | 
				
			||||||
        self,
 | 
					 | 
				
			||||||
    ) -> float:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        chart = self._chart
 | 
					 | 
				
			||||||
        l1_len = chart._max_l1_line_len
 | 
					 | 
				
			||||||
        ryaxis = chart.getAxis('right')
 | 
					 | 
				
			||||||
        up_to_l1_sc = ryaxis.pos().x() - l1_len
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return up_to_l1_sc
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def paint(
 | 
					    def paint(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -422,23 +410,23 @@ class LevelLine(pg.InfiniteLine):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ) -> QtWidgets.QGraphicsPathItem:
 | 
					    ) -> QtWidgets.QGraphicsPathItem:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._marker = path
 | 
				
			||||||
 | 
					        self._marker.setPen(self.currentPen)
 | 
				
			||||||
 | 
					        self._marker.setBrush(fn.mkBrush(self.currentPen.color()))
 | 
				
			||||||
        # add path to scene
 | 
					        # add path to scene
 | 
				
			||||||
        self.getViewBox().scene().addItem(path)
 | 
					        self.getViewBox().scene().addItem(path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._marker = path
 | 
					        # place to just-left of L1 labels
 | 
				
			||||||
 | 
					        rsc = self._chart.pre_l1_x()[0]
 | 
				
			||||||
        rsc = self.right_point()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self._marker.setPen(self.currentPen)
 | 
					 | 
				
			||||||
        self._marker.setBrush(fn.mkBrush(self.currentPen.color()))
 | 
					 | 
				
			||||||
        path.setPos(QPointF(rsc, self.scene_y()))
 | 
					        path.setPos(QPointF(rsc, self.scene_y()))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return path
 | 
					        return path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def hoverEvent(self, ev):
 | 
					    def hoverEvent(self, ev):
 | 
				
			||||||
        """Mouse hover callback.
 | 
					        '''
 | 
				
			||||||
 | 
					        Mouse hover callback.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        """
 | 
					        '''
 | 
				
			||||||
        cur = self._chart.linked.cursor
 | 
					        cur = self._chart.linked.cursor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # hovered
 | 
					        # hovered
 | 
				
			||||||
| 
						 | 
					@ -614,7 +602,8 @@ def order_line(
 | 
				
			||||||
    **line_kwargs,
 | 
					    **line_kwargs,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
) -> LevelLine:
 | 
					) -> LevelLine:
 | 
				
			||||||
    '''Convenience routine to add a line graphic representing an order
 | 
					    '''
 | 
				
			||||||
 | 
					    Convenience routine to add a line graphic representing an order
 | 
				
			||||||
    execution submitted to the EMS via the chart's "order mode".
 | 
					    execution submitted to the EMS via the chart's "order mode".
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    '''
 | 
					    '''
 | 
				
			||||||
| 
						 | 
					@ -689,7 +678,6 @@ def order_line(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return f'{account}: '
 | 
					            return f'{account}: '
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
        label.fields = {
 | 
					        label.fields = {
 | 
				
			||||||
            'size': size,
 | 
					            'size': size,
 | 
				
			||||||
            'size_digits': 0,
 | 
					            'size_digits': 0,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,9 +14,10 @@
 | 
				
			||||||
# You should have received a copy of the GNU Affero General Public License
 | 
					# You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
					# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"""
 | 
					'''
 | 
				
			||||||
Qt UI styling.
 | 
					Qt UI styling.
 | 
				
			||||||
"""
 | 
					
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
from typing import Optional, Dict
 | 
					from typing import Optional, Dict
 | 
				
			||||||
import math
 | 
					import math
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -202,8 +203,6 @@ _xaxis_at = 'bottom'
 | 
				
			||||||
# charting config
 | 
					# charting config
 | 
				
			||||||
CHART_MARGINS = (0, 0, 2, 2)
 | 
					CHART_MARGINS = (0, 0, 2, 2)
 | 
				
			||||||
_min_points_to_show = 6
 | 
					_min_points_to_show = 6
 | 
				
			||||||
_bars_to_left_in_follow_mode = int(61*6)
 | 
					 | 
				
			||||||
_bars_from_right_in_follow_mode = round(0.16 * _bars_to_left_in_follow_mode)
 | 
					 | 
				
			||||||
_tina_mode = False
 | 
					_tina_mode = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue