Search code examples
pythonpyqtpyqtgraph

Draw half infinite lines?


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).

example

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?


Solution

  • 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:

    enter image description here

    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.