pyqtgraph - 将ArrowItem的原点移动到本地中心

时间:2018-03-11 11:04:51

标签: python qt pyqt pyqtgraph

我正在使用pyqtgraph绘制机器人的轨迹(机器人驾驶的路径)。现在我想在绘图中添加一个标记来指示机器人当前位置和标题。我认为ArrowItem是正确的选择,因为它是尺度不变的并且可以轻松旋转。然而,箭头的本地原点就像这样

但我希望它像这样在中心

我该怎么做?我也很欣赏这个问题的不同解决方案。

更新

应用eyllansec的代码后,我遇到了一些渲染问题。一个最小的例子(一个必须缩放或移动视图以禁用自动缩放):

import pyqtgraph as pg
import numpy as np
import time

class CenteredArrowItem(pg.ArrowItem):
    def paint(self, p, *args):
        p.translate(-self.boundingRect().center())
        pg.ArrowItem.paint(self, p, *args)


if __name__ == '__main__':
    app = pg.QtGui.QApplication([])  
    window = pg.GraphicsWindow(size=(1280, 720))
    window.setAntialiasing(True)
    tracker = window.addPlot(title='Tracker')

    while True:
        for i in range(300):         
            arrow = CenteredArrowItem(angle=i, headLen=40, tipAngle=45, baseAngle=30)   
            arrow.setPos(i / 300, i / 300)
            tracker.addItem(arrow)
            app.processEvents()
            time.sleep(0.02)
            tracker.removeItem(arrow)

您可能已经注意到我每次迭代都添加并删除箭头。这是因为arrow.setStyle(angle=i)无法正常工作,因为它不会更新箭头的旋转(可能是错误)。

2 个答案:

答案 0 :(得分:1)

一种可能的解决方案是覆盖ArrowItem的paint方法并移动QPainter:

import numpy as np
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg

class MyArrowItem(pg.ArrowItem):
    def paint(self, p, *args):
        p.translate(-self.boundingRect().center())
        pg.ArrowItem.paint(self, p, *args)

app = QtGui.QApplication([])

w = QtGui.QMainWindow()
p = pg.PlotWidget()
p.showGrid(x = True, y = True, alpha = 0.3)
w.show()
w.resize(640, 480)
w.setCentralWidget(p)
w.setWindowTitle('pyqtgraph example: Arrow')


a = pg.ArrowItem(angle=-160, tipAngle=60, headLen=40, tailLen=40, tailWidth=20, pen={'color': 'w', 'width': 3},  brush='r')
b = MyArrowItem(angle=-160, tipAngle=60, headLen=40, tailLen=40, tailWidth=20, pen={'color': 'w', 'width': 3})

a.setPos(10,0)
b.setPos(10,0)

p.addItem(a)
p.addItem(b)

## 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'):
        QtGui.QApplication.instance().exec_()

如下图所示,红色箭头是默认的ArrowItem,蓝色是偏移,两者都位于相对于图的相同位置。

enter image description here

<强>更新

问题是由默认情况下使用转换中心旋转用作坐标中心的项的方法引起的,也就是说(0,0),我们必须移动它:

import pyqtgraph as pg
import numpy as np
import time

from pyqtgraph.Qt import QtGui, QtCore
from pyqtgraph import functions as fn

class CenteredArrowItem(pg.ArrowItem):
    def setStyle(self, **opts):
        # http://www.pyqtgraph.org/documentation/_modules/pyqtgraph/graphicsItems/ArrowItem.html#ArrowItem.setStyle
        self.opts.update(opts)

        opt = dict([(k,self.opts[k]) for k in ['headLen', 'tipAngle', 'baseAngle', 'tailLen', 'tailWidth']])
        tr = QtGui.QTransform()
        path = fn.makeArrowPath(**opt)
        tr.rotate(self.opts['angle'])
        p = -path.boundingRect().center()
        tr.translate(p.x(), p.y())
        self.path = tr.map(path)
        self.setPath(self.path)

        self.setPen(fn.mkPen(self.opts['pen']))
        self.setBrush(fn.mkBrush(self.opts['brush']))

        if self.opts['pxMode']:
            self.setFlags(self.flags() | self.ItemIgnoresTransformations)
        else:
            self.setFlags(self.flags() & ~self.ItemIgnoresTransformations)

if __name__ == '__main__':
    app = pg.QtGui.QApplication([])  
    window = pg.GraphicsWindow(size=(1280, 720))
    window.setAntialiasing(True)
    tracker = window.addPlot(title='Tracker')

    while True:
        for i in range(300):         
            arrow = CenteredArrowItem(angle=i, headLen=40, tipAngle=45, baseAngle=30)   
            arrow.setPos(i / 300, i / 300)
            tracker.addItem(arrow)
            app.processEvents()
            time.sleep(0.02)
            tracker.removeItem(arrow)

答案 1 :(得分:0)

在深入了解pyqtgraph的源代码之后,我得到了一个适合我需要的特殊功能。我在创建箭头路径时应用转换,而不是在渲染时应用转换。幸运的是,这也解决了动作错误(无论出于何种原因)。

示例:

import pyqtgraph as pg
import numpy as np
import time
import pyqtgraph.functions

class CenteredArrowItem(pg.ArrowItem):
    def setData(self, x, y, angle):
        self.opts['angle'] = angle
        opt = dict([(k, self.opts[k]) for k in ['headLen', 'tipAngle', 'baseAngle', 'tailLen', 'tailWidth']])
        path = pg.functions.makeArrowPath(**opt)
        b = path.boundingRect()
        tr = pg.QtGui.QTransform()              
        tr.rotate(angle)
        tr.translate(-b.x() - b.width() / 2, -b.y() - b.height() / 2)  
        self.path = tr.map(path)
        self.setPath(self.path)
        self.setPos(x, y)


if __name__ == '__main__':
    app = pg.QtGui.QApplication([])  
    window = pg.GraphicsWindow(size=(1280, 720))
    window.setAntialiasing(True)
    tracker = window.addPlot(title='Tracker')

    arrow = CenteredArrowItem(headLen=40, tipAngle=45, baseAngle=30)   
    tracker.addItem(arrow)
    tracker.addItem(pg.InfiniteLine(pos=(0,0), angle=45))
    center = pg.ScatterPlotItem([], [], brush='r')
    tracker.addItem(center)

    while True:
        for i in range(300):
            arrow.setData(i, i, i)     
            center.setData([i], [i])    
            app.processEvents()
            time.sleep(0.02)