pyqtgraph中带有箭头的自定义LinearRegionItem

Posted

技术标签:

【中文标题】pyqtgraph中带有箭头的自定义LinearRegionItem【英文标题】:Custom LinearRegionItem with Arrows in pyqtgraph 【发布时间】:2021-10-10 05:11:39 【问题描述】:

我正在Pyqtgraph 中制作数据可视化应用程序,并希望包含能够选择区域并进一步处理它的功能。我可以通过使用LinearRegionItem 来做到这一点。但我还想展示的是一个带有标签的箭头,表示选择的长度。以下是我使用ArrowItem 的尝试。这样做的问题是箭头长度不符合预期。

我假设箭头长度是错误的,因为从场景缩放到视图或相反。我理解映射的尝试没有意义,因为即使我更改了 QPoint 的参数 mapToScene 的输出也不会改变。

我寻求您的帮助以指导我正确的方向。

from PySide2 import QtCore, QtGui

import numpy as np
import pyqtgraph as pg

class LinearRegionItemWithArrow(pg.LinearRegionItem):

    def __init__(self, vb, *args, **kwds):
        pg.LinearRegionItem.__init__(self, *args, **kwds)
        self.vb = vb
        self.headLen = 20

        pos1, pos2 = self.getRegion()
        # pos1_vb = self.vb.mapToScene(QtCore.QPoint(pos2, 0)).x()
        # pos2_vb = self.vb.mapToScene(QtCore.QPoint(pos1, 0)).x()
        # print((pos1, pos2), (pos1_vb, pos2_vb))

        tailLen = pos2 - pos1 - self.headLen
        # tailLen_vb = pos2_vb - pos1_vb
        # print(tailLen/tailLen_vb)
        self.arrow1 = pg.ArrowItem(self, 
                                    angle=0, 
                                    headLen=self.headLen,
                                    tipAngle=45,
                                    pos= (pos1, 1), 
                                    tailLen = tailLen, #tailLen_vb if tailLen>2*self.headLen else None,
                                    tailWidth = 3, 
                                    pen=None, 
                                    brush=pg.mkBrush(255, 165, 0), 
                                    pxMode=True)
        
        self.sigRegionChanged.connect(self.updateArrows)

    def updateArrows(self):
        print(self.vb.mapFromScene(QtCore.QPoint(36, 0)))
        pos1, pos2 = self.getRegion()
        # pos1_vb = self.vb.mapToScene(QtCore.QPoint(pos2, 0)).x()
        # pos2_vb = self.vb.mapToScene(QtCore.QPoint(pos1, 0)).x()
        # print((pos1, pos2), (pos1_vb, pos2_vb))

        tailLen = pos2 - pos1 - self.headLen
        # tailLen_vb = pos2_vb - pos1_vb
        # print(tailLen/tailLen_vb)

        self.arrow1.setPos(pos1, 1)
        self.arrow1.setStyle(tailLen = tailLen)# if tailLen>2*self.headLen else None)

data = np.random.normal(size=1000)
win = pg.plot()
plot = win.plot(data, name='data', pen='r', title="Simplest possible plotting example")

lri = LinearRegionItemWithArrow(win.getViewBox(), values=[200, 500])
lri.setZValue(10)
win.addItem(lri)
print(win.mapFromScene(36 , 0))
lri.sigRegionChanged.connect(lambda: win.getViewBox().mapFromScene(QtCore.QPoint(36, 0))) #.mapSceneToView((1.0, 1.0))


## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
    import sys
    if sys.flags.interactive != 1 or not hasattr(QtCore, 'PYQT_VERSION'):
        pg.QtGui.QApplication.exec_()

【问题讨论】:

【参考方案1】:

正如我在评论中提到的,我应该使用 mapSceneToView 而不是 mapFromScenemapSceneToView 允许将点从绘图坐标系带到我正在寻找的窗口(ViewBox)坐标系。下面是清理后的代码。希望这对其他人有帮助。

from PySide2 import QtCore

import numpy as np
import pyqtgraph as pg


class LinearRegionItemWithArrow(pg.LinearRegionItem):

    def __init__(self, plotWindow, *args, **kwds):
        pg.LinearRegionItem.__init__(self, *args, **kwds)
        self.plotWindow = plotWindow
        self.headLen = 20

        self.arrow1 = pg.ArrowItem(self, 
                                    angle=0, 
                                    headLen=self.headLen,
                                    tipAngle=45,
                                    pos= (0, 0), 
                                    tailLen = 20, #tailLen_vb if tailLen>2*self.headLen else None,
                                    tailWidth = 3, 
                                    pen=None, 
                                    brush=pg.mkBrush(255, 165, 0), 
                                    pxMode=True)
        self.arrow2 = pg.ArrowItem(self, 
                                    angle=180, 
                                    headLen=self.headLen,
                                    tipAngle=45,
                                    pos= (0, 0), 
                                    tailLen = 20, #tailLen_vb if tailLen>2*self.headLen else None,
                                    tailWidth = 3, 
                                    pen=None, 
                                    brush=pg.mkBrush(255, 165, 0), 
                                    pxMode=True)
        self.updateArrows()
        self.sigRegionChanged.connect(self.updateArrows)
        self.plotWindow.sigRangeChanged.connect(self.updateArrows)  


        self.labels = [pg.TextItem(text='', anchor=(1, 0.5)), pg.TextItem(text='', anchor=(0.5, 0.5)), pg.TextItem(text='', anchor=(0, 0.5))]
        self.updateLabels()
        [self.plotWindow.addItem(l) for l in self.labels]
        self.sigRegionChanged.connect(self.updateLabels)
        self.plotWindow.sigRangeChanged.connect(self.updateLabels)  

    def updateLabels(self):
        pos1, pos2 = self.getRegion()
        for l, p in zip(self.labels, [pos1, 0.5*(pos1+pos2), pos2]):
            l.setPos(p, self.getYPosition())
            l.setText('%.2f'%p)
        self.labels[1].setText('%.2f'%(pos2 - pos1))

    def updateArrows(self):
        pos1, pos2 = self.getRegion()
        self.arrow1.setPos(pos1, self.getYPosition())
        self.arrow2.setPos(pos2, self.getYPosition())

    def getYPosition(self):
        return self.plotWindow.getViewBox().mapSceneToView(QtCore.QPoint(0, 0.1*self.plotWindow.range.height())).y()

data = np.random.normal(size=1000)
win = pg.plot()
plot = win.plot(data, name='data', pen='r', title="Simplest possible plotting example")

lri = LinearRegionItemWithArrow(win, values=[200, 500])
lri.setZValue(10)
win.addItem(lri)

## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
    import sys
    if sys.flags.interactive != 1 or not hasattr(QtCore, 'PYQT_VERSION'):
        pg.QtGui.QApplication.exec_()

【讨论】:

以上是关于pyqtgraph中带有箭头的自定义LinearRegionItem的主要内容,如果未能解决你的问题,请参考以下文章

Wordpress 中带有前缀的自定义字段的自定义简码

javascript中带有自定义触发器的自定义事件

sklearn中带有数据标签的自定义transformerMixin

张量流中带有循环的自定义损失

在 xib 中带有可选 UIImageView 的自定义 UITableViewCell

Keras中带有权重的自定义损失函数