Add support for curve "cursors" using a filled dot
Add a new graphic `LineDot` which is a `pg.CurvePoint` that draws a simple filled dot over a curve at the specified index. Add support for adding these cursor-dots to the crosshair/mouse through a new `CrossHair.add_curve_cursor()`. Discretized the vertical line updates on the crosshair such that it's only drawn in the middle of the current bar in the main chart.bar_select
parent
88583d999a
commit
1f6b5da17e
|
@ -16,11 +16,50 @@ from ._axes import YAxisLabel, XAxisLabel
|
||||||
# TODO:
|
# TODO:
|
||||||
# - checkout pyqtgraph.PlotCurveItem.setCompositionMode
|
# - checkout pyqtgraph.PlotCurveItem.setCompositionMode
|
||||||
|
|
||||||
_mouse_rate_limit = 60
|
_mouse_rate_limit = 60 # calc current screen refresh rate?
|
||||||
_debounce_delay = 1/2e3
|
_debounce_delay = 1/2e3
|
||||||
_ch_label_opac = 1
|
_ch_label_opac = 1
|
||||||
|
|
||||||
|
|
||||||
|
class LineDot(pg.CurvePoint):
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
curve: pg.PlotCurveItem,
|
||||||
|
index: int,
|
||||||
|
pos=None,
|
||||||
|
size: int = 2, # in pxs
|
||||||
|
color: str = 'default_light',
|
||||||
|
) -> None:
|
||||||
|
pg.CurvePoint.__init__(
|
||||||
|
self,
|
||||||
|
curve,
|
||||||
|
index=index,
|
||||||
|
pos=pos,
|
||||||
|
rotate=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO: get pen from curve if not defined?
|
||||||
|
cdefault = hcolor(color)
|
||||||
|
pen = pg.mkPen(cdefault)
|
||||||
|
brush = pg.mkBrush(cdefault)
|
||||||
|
|
||||||
|
# presuming this is fast since it's built in?
|
||||||
|
dot = self.dot = QtGui.QGraphicsEllipseItem(
|
||||||
|
QtCore.QRectF(-size/2, -size/2, size, size)
|
||||||
|
)
|
||||||
|
# dot.translate(-size*0.5, -size*0.5)
|
||||||
|
dot.setPen(pen)
|
||||||
|
dot.setBrush(brush)
|
||||||
|
dot.setParentItem(self)
|
||||||
|
|
||||||
|
# keep a static size
|
||||||
|
self.setFlag(self.ItemIgnoresTransformations)
|
||||||
|
|
||||||
|
def paint(self, p, opt, widget):
|
||||||
|
p.drawPicture(0, 0, self._pic)
|
||||||
|
|
||||||
|
|
||||||
class CrossHair(pg.GraphicsObject):
|
class CrossHair(pg.GraphicsObject):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -44,6 +83,7 @@ class CrossHair(pg.GraphicsObject):
|
||||||
self.plots = []
|
self.plots = []
|
||||||
self.active_plot = None
|
self.active_plot = None
|
||||||
self.digits = digits
|
self.digits = digits
|
||||||
|
self._lastx = None
|
||||||
|
|
||||||
def add_plot(
|
def add_plot(
|
||||||
self,
|
self,
|
||||||
|
@ -100,17 +140,32 @@ class CrossHair(pg.GraphicsObject):
|
||||||
color=self.pen,
|
color=self.pen,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def add_curve_cursor(
|
||||||
|
self,
|
||||||
|
plot: 'ChartPlotWidget', # noqa
|
||||||
|
curve: 'PlotCurveItem', # noqa
|
||||||
|
) -> LineDot:
|
||||||
|
# if this plot contains curves add line dot "cursors" to denote
|
||||||
|
# the current sample under the mouse
|
||||||
|
cursor = LineDot(curve, index=len(plot._array))
|
||||||
|
plot.addItem(cursor)
|
||||||
|
self.graphics[plot].setdefault('cursors', []).append(cursor)
|
||||||
|
return cursor
|
||||||
|
|
||||||
def mouseAction(self, action, plot): # noqa
|
def mouseAction(self, action, plot): # noqa
|
||||||
if action == 'Enter':
|
if action == 'Enter':
|
||||||
|
self.active_plot = plot
|
||||||
|
|
||||||
# show horiz line and y-label
|
# show horiz line and y-label
|
||||||
self.graphics[plot]['hl'].show()
|
self.graphics[plot]['hl'].show()
|
||||||
self.graphics[plot]['yl'].show()
|
self.graphics[plot]['yl'].show()
|
||||||
self.active_plot = plot
|
|
||||||
else: # Leave
|
else: # Leave
|
||||||
|
self.active_plot = None
|
||||||
|
|
||||||
# hide horiz line and y-label
|
# hide horiz line and y-label
|
||||||
self.graphics[plot]['hl'].hide()
|
self.graphics[plot]['hl'].hide()
|
||||||
self.graphics[plot]['yl'].hide()
|
self.graphics[plot]['yl'].hide()
|
||||||
self.active_plot = None
|
|
||||||
|
|
||||||
def mouseMoved(
|
def mouseMoved(
|
||||||
self,
|
self,
|
||||||
|
@ -130,20 +185,27 @@ class CrossHair(pg.GraphicsObject):
|
||||||
return
|
return
|
||||||
|
|
||||||
x, y = mouse_point.x(), mouse_point.y()
|
x, y = mouse_point.x(), mouse_point.y()
|
||||||
|
|
||||||
plot = self.active_plot
|
plot = self.active_plot
|
||||||
|
|
||||||
|
# update y-range items
|
||||||
self.graphics[plot]['hl'].setY(y)
|
self.graphics[plot]['hl'].setY(y)
|
||||||
|
|
||||||
self.graphics[self.active_plot]['yl'].update_label(
|
self.graphics[self.active_plot]['yl'].update_label(
|
||||||
abs_pos=pos, data=y
|
abs_pos=pos, data=y
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Update x if cursor changed after discretization calc
|
||||||
|
# (this saves draw cycles on small mouse moves)
|
||||||
|
lastx = self._lastx
|
||||||
|
newx = int(x)
|
||||||
|
|
||||||
|
if newx != lastx:
|
||||||
for plot, opts in self.graphics.items():
|
for plot, opts in self.graphics.items():
|
||||||
# move the vertical line to the current x
|
# move the vertical line to the current "center of bar"
|
||||||
opts['vl'].setX(x)
|
opts['vl'].setX(newx + BarItems.w)
|
||||||
|
|
||||||
# update the chart's "contents" label
|
# update the chart's "contents" label
|
||||||
plot._update_contents_label(int(x))
|
plot._update_contents_label(newx + 1)
|
||||||
|
|
||||||
# update the label on the bottom of the crosshair
|
# update the label on the bottom of the crosshair
|
||||||
self.xaxis_label.update_label(
|
self.xaxis_label.update_label(
|
||||||
|
@ -151,6 +213,10 @@ class CrossHair(pg.GraphicsObject):
|
||||||
data=x
|
data=x
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# update all subscribed curve dots
|
||||||
|
for cursor in opts.get('cursors', ()):
|
||||||
|
cursor.setIndex(newx + 1)
|
||||||
|
|
||||||
def boundingRect(self):
|
def boundingRect(self):
|
||||||
try:
|
try:
|
||||||
return self.active_plot.boundingRect()
|
return self.active_plot.boundingRect()
|
||||||
|
|
Loading…
Reference in New Issue