I use pyqtgraph for data acquisition and I have to represent some thresholds on the graphics view. For example to represent a high voltage limit, etc.
I used the class InfiniteLine
from pyqtgraph, but now, I have to take into account some possible changes on the threshold value during the acquisition. It would look as a step between two infinite line (please find an example attached).
For this, I would have to draw a half infinite line. Do you know a simple way to do it?
I thought about using some plotCurveItem limited by the viewBox minimum and maximum :
thresholdValue = 60V # just an example
range = self.viewBox.viewRange()
xRange = range[0] # we only want ViewBox horizontal limits
minView = xRange[0]
maxView = xRange[1]
myPlotCurveItem = pyqtgraph.PlotCurveItem([minView, maxView],[thresholdValue, thresholdValue])
In case of changing on threshold value :
newThresholdValue = 70V
the x data for the plotCurveItem would become :
[minView, changingTime] #with changinTime : the moment we change the threshold
and we would add a new plotCurveItem :
myNewPlotCurveItem = pyqtgraph.plotCurveItem([changingTime, maxView],[newThresholdValue, newThresholdValue])
Does this solution looks good or do you see any problem with that?
Your approach looks good and is mostly what pyqtgraph.InfiniteLine
is doing. I examined the source of InfiniteLine and extracted those parts which are absolutely necessary and added the change point and two level information, then drawing three lines (left border to change point at left level, change point to right border at right level, connection of both).
Here is the full code:
from pyqtgraph.Qt import QtGui
import numpy as np
import pyqtgraph as pg
class InfiniteLineWithBreak(pg.GraphicsObject):
def __init__(self, changeX, levelsY, pen=None):
pg.GraphicsObject.__init__(self)
self.changeX = changeX
self.levelsY = levelsY
self.maxRange = [None, None]
self.moving = False
self.movable = False
self.mouseHovering = False
pen = (200, 200, 100)
self.setPen(pen)
self.setHoverPen(color=(255,0,0), width=self.pen.width())
self.currentPen = self.pen
def setBounds(self, bounds):
self.maxRange = bounds
self.setValue(self.value())
def setPen(self, *args, **kwargs):
self.pen = pg.fn.mkPen(*args, **kwargs)
if not self.mouseHovering:
self.currentPen = self.pen
self.update()
def setHoverPen(self, *args, **kwargs):
self.hoverPen = pg.fn.mkPen(*args, **kwargs)
if self.mouseHovering:
self.currentPen = self.hoverPen
self.update()
def boundingRect(self):
br = self.viewRect()
return br.normalized()
def paint(self, p, *args):
br = self.boundingRect()
p.setPen(self.currentPen)
# three lines (left border to change point, change point vertical, change point to right)
p.drawLine(pg.Point(br.left(), self.levelsY[0]), pg.Point(self.changeX, self.levelsY[0]))
p.drawLine(pg.Point(self.changeX, self.levelsY[0]), pg.Point(self.changeX, self.levelsY[1]))
p.drawLine(pg.Point(self.changeX, self.levelsY[1]), pg.Point(br.right(), self.levelsY[1]))
def dataBounds(self, axis, frac=1.0, orthoRange=None):
if axis == 0:
return None ## x axis should never be auto-scaled
else:
return (0,0)
def setMouseHover(self, hover):
pass
app = QtGui.QApplication([])
w = pg.GraphicsWindow()
w.resize(1000, 600)
v = w.addPlot(y=np.random.normal(size=100))
v.addItem(InfiniteLineWithBreak(changeX=50, levelsY=(-1, 1)))
app.exec_()
It looks like:
What one could add is reaction to hovering and changing the values with the mouse (change point as well as levels) or even rotate by 90 degree. InfiniteLine
is a good example of how to do that.