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 more
m4_corrections
Tyler Goodlet 2022-03-20 12:53:44 -04:00
parent e95896722f
commit 6bf4cdaa24
4 changed files with 58 additions and 42 deletions

View File

@ -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

View File

@ -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(

View File

@ -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,

View File

@ -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