Draw half infinite lines?

不羁岁月 提交于 2020-01-24 12:42:30

问题


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?


回答1:


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.




回答2:


thanks you for you very complete answer ! Your code works very well. I made some modifications on your class InfiniteLineWithBreak in order to set multiple threshold transitions. I modified the init and the paint methods only:

def __init__(self, listOfcouplesOfThresholdAndItsDate, pen=None):
    pg.GraphicsObject.__init__(self)

    self.listOfcouplesOfThresholdAndItsDate=listOfcouplesOfThresholdAndItsDate
    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 paint(self, p, *args):
    br = self.boundingRect()
    p.setPen(self.currentPen)
    if len(self.listOfcouplesOfThresholdAndItsDate)==0:
        pass
    elif len(self.listOfcouplesOfThresholdAndItsDate)==1:
        threshold = self.listOfcouplesOfThresholdAndItsDate[0][1]
        date = self.listOfcouplesOfThresholdAndItsDate[0][0]
        p.drawLine(pg.Point(date, threshold), pg.Point(br.right(), threshold))

    else:

        threshold = self.listOfcouplesOfThresholdAndItsDate[0][1]
        date = self.listOfcouplesOfThresholdAndItsDate[0][0]
        i=0
        for i in range(0, len(self.listOfcouplesOfThresholdAndItsDate)-2):
            threshold = self.listOfcouplesOfThresholdAndItsDate[i][1]
            date = self.listOfcouplesOfThresholdAndItsDate[i][0]
            nexteDate = self.listOfcouplesOfThresholdAndItsDate[i+1][0]
            nextThreshold = self.listOfcouplesOfThresholdAndItsDate[i+1][1]
            p.drawLine(pg.Point(date, threshold), pg.Point(nexteDate, threshold))
            p.drawLine(pg.Point(nexteDate, threshold), pg.Point(nexteDate, nextThreshold))


        threshold = self.listOfcouplesOfThresholdAndItsDate[-2][1]
        date = self.listOfcouplesOfThresholdAndItsDate[-2][0]
        nexteDate = self.listOfcouplesOfThresholdAndItsDate[-1][0]
        nextThreshold = self.listOfcouplesOfThresholdAndItsDate[-1][1]
        p.drawLine(pg.Point(date, threshold), pg.Point(nexteDate, threshold))
        p.drawLine(pg.Point(nexteDate, threshold), pg.Point(nexteDate, nextThreshold))

        p.drawLine(pg.Point(nexteDate, nextThreshold), pg.Point(br.right(), nextThreshold))

Moreover, I added a method to append a new threshold transition point to the listOfcouplesOfThersholdAndItsDate :

def addANewThreshold(self,date, threshold):
    self.listOfcouplesOfThresholdAndItsDate.append((date, threshold))

Here is an example of what it looks like : example of multiple thresholds

Does the code look okay to you ? Thank you,



来源:https://stackoverflow.com/questions/37702642/draw-half-infinite-lines

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!