pyqtgraph/pyqtgraph/graphicsItems/CurvePoint.py
Ogi Moore b0a3849960 Use math module methods for singular values
Using numpy methods that are intended for vectorized operations is
substantially slower than using the math module, so when feasible the
math module methods should be used.
2021-04-23 11:53:00 -07:00

125 lines
4.6 KiB
Python

from math import atan2, pi
from ..Qt import QtGui, QtCore
from . import ArrowItem
from ..functions import clip_scalar
import numpy as np
from ..Point import Point
import weakref
from .GraphicsObject import GraphicsObject
__all__ = ['CurvePoint', 'CurveArrow']
class CurvePoint(GraphicsObject):
"""A GraphicsItem that sets its location to a point on a PlotCurveItem.
Also rotates to be tangent to the curve.
The position along the curve is a Qt property, and thus can be easily animated.
Note: This class does not display anything; see CurveArrow for an applied example
"""
def __init__(self, curve, index=0, pos=None, rotate=True):
"""Position can be set either as an index referring to the sample number or
the position 0.0 - 1.0
If *rotate* is True, then the item rotates to match the tangent of the curve.
"""
GraphicsObject.__init__(self)
#QObjectWorkaround.__init__(self)
self._rotate = rotate
self.curve = weakref.ref(curve)
self.setParentItem(curve)
self.setProperty('position', 0.0)
self.setProperty('index', 0)
if hasattr(self, 'ItemHasNoContents'):
self.setFlags(self.flags() | self.ItemHasNoContents)
if pos is not None:
self.setPos(pos)
else:
self.setIndex(index)
def setPos(self, pos):
self.setProperty('position', float(pos))## cannot use numpy types here, MUST be python float.
def setIndex(self, index):
self.setProperty('index', int(index)) ## cannot use numpy types here, MUST be python int.
def event(self, ev):
if not isinstance(ev, QtCore.QDynamicPropertyChangeEvent) or self.curve() is None:
return False
if ev.propertyName() == 'index':
index = self.property('index')
if 'QVariant' in repr(index):
index = index.toInt()[0]
elif ev.propertyName() == 'position':
index = None
else:
return False
(x, y) = self.curve().getData()
if index is None:
#print ev.propertyName(), self.property('position').toDouble()[0], self.property('position').typeName()
pos = self.property('position')
if 'QVariant' in repr(pos): ## need to support 2 APIs :(
pos = pos.toDouble()[0]
index = (len(x)-1) * np.clip(pos, 0.0, 1.0)
if index != int(index): ## interpolate floating-point values
i1 = int(index)
i2 = clip_scalar(i1+1, 0, len(x)-1)
s2 = index-i1
s1 = 1.0-s2
newPos = (x[i1]*s1+x[i2]*s2, y[i1]*s1+y[i2]*s2)
else:
index = int(index)
i1 = clip_scalar(index-1, 0, len(x)-1)
i2 = clip_scalar(index+1, 0, len(x)-1)
newPos = (x[index], y[index])
p1 = self.parentItem().mapToScene(QtCore.QPointF(x[i1], y[i1]))
p2 = self.parentItem().mapToScene(QtCore.QPointF(x[i2], y[i2]))
ang = atan2(p2.y()-p1.y(), p2.x()-p1.x()) ## returns radians
self.resetTransform()
if self._rotate:
self.setRotation(180 + ang * (180. / pi))
QtGui.QGraphicsItem.setPos(self, *newPos)
return True
def boundingRect(self):
return QtCore.QRectF()
def paint(self, *args):
pass
def makeAnimation(self, prop='position', start=0.0, end=1.0, duration=10000, loop=1):
# In Python 3, a bytes object needs to be used as a property name in
# QPropertyAnimation. PyQt stopped automatically encoding a str when a
# QByteArray was expected in v5.5 (see qbytearray.sip).
if not isinstance(prop, bytes):
prop = prop.encode('latin-1')
anim = QtCore.QPropertyAnimation(self, prop)
anim.setDuration(duration)
anim.setStartValue(start)
anim.setEndValue(end)
anim.setLoopCount(loop)
return anim
class CurveArrow(CurvePoint):
"""Provides an arrow that points to any specific sample on a PlotCurveItem.
Provides properties that can be animated."""
def __init__(self, curve, index=0, pos=None, **opts):
CurvePoint.__init__(self, curve, index=index, pos=pos)
if opts.get('pxMode', True):
opts['pxMode'] = False
self.setFlags(self.flags() | self.ItemIgnoresTransformations)
opts['angle'] = 0
self.arrow = ArrowItem.ArrowItem(**opts)
self.arrow.setParentItem(self)
def setStyle(self, **opts):
return self.arrow.setStyle(**opts)