Move level marker to annotate module
parent
6cdb2fca41
commit
40a38284df
|
@ -18,18 +18,20 @@
|
||||||
Annotations for ur faces.
|
Annotations for ur faces.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import PyQt5
|
from typing import Callable
|
||||||
from PyQt5 import QtCore, QtGui
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
from PyQt5.QtCore import QPointF
|
||||||
from PyQt5.QtWidgets import QGraphicsPathItem
|
from PyQt5.QtWidgets import QGraphicsPathItem
|
||||||
from pyqtgraph import Point, functions as fn, Color
|
from pyqtgraph import Point, functions as fn, Color
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
from ._anchors import marker_right_points
|
||||||
|
|
||||||
|
|
||||||
def mk_marker_path(
|
def mk_marker_path(
|
||||||
|
|
||||||
style,
|
style: str,
|
||||||
# size: float = 20.0,
|
|
||||||
# use_path_type: type = QGraphicsPathItem
|
|
||||||
|
|
||||||
) -> QGraphicsPathItem:
|
) -> QGraphicsPathItem:
|
||||||
"""Add a marker to be displayed on the line wrapped in a ``QGraphicsPathItem``
|
"""Add a marker to be displayed on the line wrapped in a ``QGraphicsPathItem``
|
||||||
|
@ -83,13 +85,148 @@ def mk_marker_path(
|
||||||
|
|
||||||
# self._maxMarkerSize = max([m[2] / 2. for m in self.markers])
|
# self._maxMarkerSize = max([m[2] / 2. for m in self.markers])
|
||||||
|
|
||||||
# if use_path_type:
|
|
||||||
# path = use_path_type(path)
|
|
||||||
# path.scale(size, size)
|
|
||||||
|
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
class LevelMarker(QGraphicsPathItem):
|
||||||
|
'''An arrow marker path graphich which redraws itself
|
||||||
|
to the specified view coordinate level on each paint cycle.
|
||||||
|
|
||||||
|
'''
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
chart: 'ChartPlotWidget', # noqa
|
||||||
|
style: str,
|
||||||
|
get_level: Callable[..., float],
|
||||||
|
size: float = 20,
|
||||||
|
keep_in_view: bool = True,
|
||||||
|
|
||||||
|
) -> None:
|
||||||
|
|
||||||
|
# get polygon and scale
|
||||||
|
super().__init__()
|
||||||
|
self.scale(size, size)
|
||||||
|
|
||||||
|
# interally generates path
|
||||||
|
self._style = None
|
||||||
|
self.style = style
|
||||||
|
|
||||||
|
self.chart = chart
|
||||||
|
|
||||||
|
self.get_level = get_level
|
||||||
|
self.scene_x = lambda: marker_right_points(chart)[1]
|
||||||
|
self.level: float = 0
|
||||||
|
self.keep_in_view = keep_in_view
|
||||||
|
|
||||||
|
assert self.path_br
|
||||||
|
|
||||||
|
@property
|
||||||
|
def style(self) -> str:
|
||||||
|
return self._style
|
||||||
|
|
||||||
|
@style.setter
|
||||||
|
def style(self, value: str) -> None:
|
||||||
|
if self._style != value:
|
||||||
|
polygon = mk_marker_path(value)
|
||||||
|
self.setPath(polygon)
|
||||||
|
self._style = value
|
||||||
|
|
||||||
|
# get the path for the opaque path **without** weird
|
||||||
|
# surrounding margin
|
||||||
|
self.path_br = self.mapToScene(
|
||||||
|
self.path()
|
||||||
|
).boundingRect()
|
||||||
|
|
||||||
|
def delete(self) -> None:
|
||||||
|
self.scene().removeItem(self)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def h(self) -> float:
|
||||||
|
return self.path_br.height()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def w(self) -> float:
|
||||||
|
return self.path_br.width()
|
||||||
|
|
||||||
|
def position_in_view(
|
||||||
|
self,
|
||||||
|
# level: float,
|
||||||
|
|
||||||
|
) -> None:
|
||||||
|
'''Show a pp off-screen indicator for a level label.
|
||||||
|
|
||||||
|
This is like in fps games where you have a gps "nav" indicator
|
||||||
|
but your teammate is outside the range of view, except in 2D, on
|
||||||
|
the y-dimension.
|
||||||
|
|
||||||
|
'''
|
||||||
|
level = self.get_level()
|
||||||
|
|
||||||
|
view = self.chart.getViewBox()
|
||||||
|
vr = view.state['viewRange']
|
||||||
|
ymn, ymx = vr[1]
|
||||||
|
|
||||||
|
# _, marker_right, _ = marker_right_points(line._chart)
|
||||||
|
x = self.scene_x()
|
||||||
|
|
||||||
|
if level > ymx: # pin to top of view
|
||||||
|
self.setPos(
|
||||||
|
QPointF(
|
||||||
|
x,
|
||||||
|
self.h/3,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
elif level < ymn: # pin to bottom of view
|
||||||
|
|
||||||
|
self.setPos(
|
||||||
|
QPointF(
|
||||||
|
x,
|
||||||
|
view.height() - 4/3*self.h,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# pp line is viewable so show marker normally
|
||||||
|
self.setPos(
|
||||||
|
x,
|
||||||
|
self.chart.view.mapFromView(
|
||||||
|
QPointF(0, self.get_level())
|
||||||
|
).y()
|
||||||
|
)
|
||||||
|
|
||||||
|
# marker = line._marker
|
||||||
|
if getattr(self, 'label', None):
|
||||||
|
label = self.label
|
||||||
|
|
||||||
|
# re-anchor label (i.e. trigger call of ``arrow_tr()`` from above
|
||||||
|
label.update()
|
||||||
|
|
||||||
|
def paint(
|
||||||
|
self,
|
||||||
|
|
||||||
|
p: QtGui.QPainter,
|
||||||
|
opt: QtWidgets.QStyleOptionGraphicsItem,
|
||||||
|
w: QtWidgets.QWidget
|
||||||
|
|
||||||
|
) -> None:
|
||||||
|
'''Core paint which we override to always update
|
||||||
|
our marker position in scene coordinates from a
|
||||||
|
view cooridnate "level".
|
||||||
|
|
||||||
|
'''
|
||||||
|
if self.keep_in_view:
|
||||||
|
self.position_in_view()
|
||||||
|
|
||||||
|
else: # just place at desired level even if not in view
|
||||||
|
self.setPos(
|
||||||
|
self.scene_x(),
|
||||||
|
self.mapToScene(QPointF(0, self.get_level())).y()
|
||||||
|
)
|
||||||
|
|
||||||
|
return super().paint(p, opt, w)
|
||||||
|
|
||||||
|
|
||||||
def qgo_draw_markers(
|
def qgo_draw_markers(
|
||||||
|
|
||||||
markers: list,
|
markers: list,
|
||||||
|
|
|
@ -18,19 +18,18 @@
|
||||||
Position info and display
|
Position info and display
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Optional, Callable
|
from typing import Optional
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from math import floor
|
from math import floor
|
||||||
|
|
||||||
from pyqtgraph import functions as fn
|
from pyqtgraph import functions as fn
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from PyQt5 import QtGui, QtWidgets
|
|
||||||
from PyQt5.QtCore import QPointF
|
from PyQt5.QtCore import QPointF
|
||||||
from PyQt5.QtGui import QGraphicsPathItem
|
from PyQt5.QtGui import QGraphicsPathItem
|
||||||
|
|
||||||
from ._annotate import mk_marker_path
|
from ._annotate import LevelMarker
|
||||||
from ._anchors import (
|
from ._anchors import (
|
||||||
marker_right_points,
|
# marker_right_points,
|
||||||
gpath_pin,
|
gpath_pin,
|
||||||
# keep_marker_in_view,
|
# keep_marker_in_view,
|
||||||
)
|
)
|
||||||
|
@ -58,146 +57,6 @@ class Position(BaseModel):
|
||||||
fills: list[Status] = []
|
fills: list[Status] = []
|
||||||
|
|
||||||
|
|
||||||
class LevelMarker(QGraphicsPathItem):
|
|
||||||
'''An arrow marker path graphich which redraws itself
|
|
||||||
to the specified view coordinate level on each paint cycle.
|
|
||||||
|
|
||||||
'''
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
chart: 'ChartPlotWidget', # noqa
|
|
||||||
style: str,
|
|
||||||
get_level: Callable[..., float],
|
|
||||||
size: float = 20,
|
|
||||||
keep_in_view: bool = True,
|
|
||||||
|
|
||||||
) -> None:
|
|
||||||
|
|
||||||
# get polygon and scale
|
|
||||||
super().__init__()
|
|
||||||
self.scale(size, size)
|
|
||||||
|
|
||||||
# interally generates path
|
|
||||||
self._style = None
|
|
||||||
self.style = style
|
|
||||||
|
|
||||||
self.chart = chart
|
|
||||||
|
|
||||||
self.get_level = get_level
|
|
||||||
self.scene_x = lambda: marker_right_points(chart)[1]
|
|
||||||
self.level: float = 0
|
|
||||||
self.keep_in_view = keep_in_view
|
|
||||||
|
|
||||||
assert self.path_br
|
|
||||||
|
|
||||||
@property
|
|
||||||
def style(self) -> str:
|
|
||||||
return self._style
|
|
||||||
|
|
||||||
@style.setter
|
|
||||||
def style(self, value: str) -> None:
|
|
||||||
if self._style != value:
|
|
||||||
polygon = mk_marker_path(value)
|
|
||||||
self.setPath(polygon)
|
|
||||||
self._style = value
|
|
||||||
|
|
||||||
# get the path for the opaque path **without** weird
|
|
||||||
# surrounding margin
|
|
||||||
self.path_br = self.mapToScene(
|
|
||||||
self.path()
|
|
||||||
).boundingRect()
|
|
||||||
|
|
||||||
|
|
||||||
def delete(self) -> None:
|
|
||||||
self.scene().removeItem(self)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def h(self) -> float:
|
|
||||||
return self.path_br.height()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def w(self) -> float:
|
|
||||||
return self.path_br.width()
|
|
||||||
|
|
||||||
def position_in_view(
|
|
||||||
self,
|
|
||||||
# level: float,
|
|
||||||
|
|
||||||
) -> None:
|
|
||||||
'''Show a pp off-screen indicator for a level label.
|
|
||||||
|
|
||||||
This is like in fps games where you have a gps "nav" indicator
|
|
||||||
but your teammate is outside the range of view, except in 2D, on
|
|
||||||
the y-dimension.
|
|
||||||
|
|
||||||
'''
|
|
||||||
level = self.get_level()
|
|
||||||
|
|
||||||
view = self.chart.getViewBox()
|
|
||||||
vr = view.state['viewRange']
|
|
||||||
ymn, ymx = vr[1]
|
|
||||||
|
|
||||||
# _, marker_right, _ = marker_right_points(line._chart)
|
|
||||||
x = self.scene_x()
|
|
||||||
|
|
||||||
if level > ymx: # pin to top of view
|
|
||||||
self.setPos(
|
|
||||||
QPointF(
|
|
||||||
x,
|
|
||||||
self.h/3,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
elif level < ymn: # pin to bottom of view
|
|
||||||
|
|
||||||
self.setPos(
|
|
||||||
QPointF(
|
|
||||||
x,
|
|
||||||
view.height() - 4/3*self.h,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
|
||||||
# pp line is viewable so show marker normally
|
|
||||||
self.setPos(
|
|
||||||
x,
|
|
||||||
self.chart.view.mapFromView(
|
|
||||||
QPointF(0, self.get_level())
|
|
||||||
).y()
|
|
||||||
)
|
|
||||||
|
|
||||||
# marker = line._marker
|
|
||||||
if getattr(self, 'label', None):
|
|
||||||
label = self.label
|
|
||||||
|
|
||||||
# re-anchor label (i.e. trigger call of ``arrow_tr()`` from above
|
|
||||||
label.update()
|
|
||||||
|
|
||||||
def paint(
|
|
||||||
self,
|
|
||||||
|
|
||||||
p: QtGui.QPainter,
|
|
||||||
opt: QtWidgets.QStyleOptionGraphicsItem,
|
|
||||||
w: QtWidgets.QWidget
|
|
||||||
|
|
||||||
) -> None:
|
|
||||||
'''Core paint which we override to always update
|
|
||||||
our marker position in scene coordinates from a
|
|
||||||
view cooridnate "level".
|
|
||||||
|
|
||||||
'''
|
|
||||||
if self.keep_in_view:
|
|
||||||
self.position_in_view()
|
|
||||||
|
|
||||||
else: # just place at desired level even if not in view
|
|
||||||
self.setPos(
|
|
||||||
self.scene_x(),
|
|
||||||
self.mapToScene(QPointF(0, self.get_level())).y()
|
|
||||||
)
|
|
||||||
|
|
||||||
return super().paint(p, opt, w)
|
|
||||||
|
|
||||||
|
|
||||||
class PositionTracker:
|
class PositionTracker:
|
||||||
'''Track and display a real-time position for a single symbol
|
'''Track and display a real-time position for a single symbol
|
||||||
on a chart.
|
on a chart.
|
||||||
|
@ -366,11 +225,9 @@ class PositionTracker:
|
||||||
|
|
||||||
if size > 0:
|
if size > 0:
|
||||||
style = '|<'
|
style = '|<'
|
||||||
direction = 'up'
|
|
||||||
|
|
||||||
elif size < 0:
|
elif size < 0:
|
||||||
style = '>|'
|
style = '>|'
|
||||||
direction = 'down'
|
|
||||||
|
|
||||||
arrow = LevelMarker(
|
arrow = LevelMarker(
|
||||||
chart=self.chart,
|
chart=self.chart,
|
||||||
|
@ -378,13 +235,6 @@ class PositionTracker:
|
||||||
get_level=self.level,
|
get_level=self.level,
|
||||||
size=arrow_size,
|
size=arrow_size,
|
||||||
)
|
)
|
||||||
# _, marker_right, _ = marker_right_points(self.chart)
|
|
||||||
# arrow.scene_x = marker_right
|
|
||||||
|
|
||||||
# monkey-cache height for sizing on pp nav-hub
|
|
||||||
# arrow._height = path_br.height()
|
|
||||||
# arrow._width = path_br.width()
|
|
||||||
arrow._direction = direction
|
|
||||||
|
|
||||||
self.chart.getViewBox().scene().addItem(arrow)
|
self.chart.getViewBox().scene().addItem(arrow)
|
||||||
arrow.show()
|
arrow.show()
|
||||||
|
|
Loading…
Reference in New Issue