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

Posted

技术标签:

【中文标题】pyqtgraph - 将 ArrowItem 的原点移动到本地中心【英文标题】:pyqtgraph - move origin of ArrowItem to local center 【发布时间】:2018-08-19 12:45:26 【问题描述】:

我正在使用 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) 不工作,因为它不更新箭头的旋转(可能是一个错误)。

【问题讨论】:

它提供了生成第一个光标的代码,因此更容易将其转换为第二个 @eyllansec 我不确定你的意思,但箭头的创建是直截了当的a=pyqtgraph.ArrowItem(...)。我只希望其局部坐标系的原点位于其中心而不是其尖端。 你指的问题是在转弯结束时发生的跳跃? 很抱歉,我忘了提到您必须缩小或移动视图,以便禁用自动缩放 好的,我理解了问题,编辑您的问题并添加该信息。 【参考方案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,蓝色为偏移量,两者相对于绘图位于同一位置。

更新:

问题是由默认使用变换中心旋转作为坐标中心的item的方法引起的,即(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)

【讨论】:

有点用,但是当箭头在固定的视图框中移动和旋转时出现渲染问题(请参阅here) @Timo 请编辑您的问题并添加必要的代码,以便您可以重现您的错误并提出更好的解决方案。在 SO 我们称之为minimal reproducible example @Timo 试试我的新解决方案。 看起来我们最终得到了相同的解决方案 :) 谢谢 @Timo 我才意识到你之前回答过。【参考方案2】:

在挖掘 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)

【讨论】:

以上是关于pyqtgraph - 将 ArrowItem 的原点移动到本地中心的主要内容,如果未能解决你的问题,请参考以下文章

将 pyqtgraph 嵌入到 enaml 中......如何?

如何将附加的简单表格放置在 PyQtGraph 中

带有 Qt Designer 的 pyqtgraph:如何将 PlotItems 添加到 GraphicsLayoutWidget

将 Pyqtgraph 嵌入到 PySide2

pyqtgraph绘图如何使用pyqtgraph

如何将 3d GLViewWidget 插入包含 2d PyQtGraph 图的窗口中