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 中......如何?
带有 Qt Designer 的 pyqtgraph:如何将 PlotItems 添加到 GraphicsLayoutWidget