You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将pyqtgraph的ArrowItem原点移至本地中心?求替代方案

解决方案:调整pyqtgraph ArrowItem的原点到中心

你选ArrowItem来做机器人朝向标记确实很合适,它的尺度不变性和易旋转特性完美匹配需求。默认情况下ArrowItem的原点在尖端,要把它移到几何中心,这里给你几种可行的解决方案:

方案一:自定义ArrowItem子类重定位原点

这是最灵活且一劳永逸的方式,通过继承ArrowItem,重写绘制逻辑来把原点偏移到箭头中心。参考你提到的eyllansec思路,我整理了完善后的可运行代码:

import pyqtgraph as pg
from PyQt5.QtGui import QPainterPath, QPen, QBrush
from PyQt5.QtCore import Qt, QPointF

class CenteredArrowItem(pg.ArrowItem):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        # 预计算箭头的边界范围,用于计算中心偏移量
        self.path = self._generate_arrow_path()
        self.bounds = self.path.boundingRect()
        # 计算从尖端到中心的偏移值(反向平移)
        self.center_offset = QPointF(-self.bounds.center().x(), -self.bounds.center().y())

    def _generate_arrow_path(self):
        # 复制原ArrowItem的路径生成逻辑,确保和默认箭头样式一致
        path = QPainterPath()
        opts = self.opts
        head_len = opts['headLen']
        head_width = opts['headWidth']
        tail_len = opts['tailLen']
        tail_width = opts['tailWidth']
        
        path.moveTo(0, 0)
        path.lineTo(head_len, head_width/2)
        if tail_len > 0:
            path.lineTo(head_len + tail_len, tail_width/2)
            path.lineTo(head_len + tail_len, -tail_width/2)
        path.lineTo(head_len, -head_width/2)
        path.lineTo(0, 0)
        return path

    def paint(self, painter, opt, widget):
        # 保存画笔状态,避免影响其他绘制
        painter.save()
        # 平移到箭头中心位置
        painter.translate(self.center_offset)
        # 调用父类的绘制方法
        super().paint(painter, opt, widget)
        # 恢复画笔状态
        painter.restore()

使用时直接实例化这个自定义类,设置位置和旋转时,原点就是箭头的中心了:

# 示例代码
app = pg.mkQApp()
win = pg.GraphicsLayoutWidget()
view = win.addViewBox()
view.setAspectLocked(True)

# 创建中心原点的箭头
arrow = CenteredArrowItem(headLen=20, headWidth=15, tailLen=30, tailWidth=5)
arrow.setPos(100, 100)  # 此位置对应箭头中心
arrow.setRotation(45)   # 旋转围绕中心进行
view.addItem(arrow)

win.show()
app.exec_()

方案二:通过变换矩阵手动偏移

如果不想子类化,也可以在创建默认ArrowItem后,手动计算偏移量,通过变换矩阵调整原点:

import pyqtgraph as pg

# 创建默认箭头
arrow = pg.ArrowItem(headLen=20, headWidth=15, tailLen=30, tailWidth=5)
# 获取箭头的边界矩形
bounds = arrow.boundingRect()
# 计算从尖端到中心的偏移值
offset_x = -(bounds.width() / 2 + bounds.left())
offset_y = -(bounds.height() / 2 + bounds.top())
# 设置变换矩阵,将原点移到中心
arrow.setTransform(pg.QtGui.QTransform().translate(offset_x, offset_y))

# 后续设置位置和旋转都基于中心
arrow.setPos(100, 100)
arrow.setRotation(45)

这个方法的缺点是:如果箭头的尺寸参数(如headLen、tailLen)修改后,需要重新计算偏移量,灵活性不如子类化方案。

方案三:自定义组合图形替代ArrowItem

如果觉得ArrowItem的原点问题太麻烦,也可以用pg.GraphicsObject组合基础图形(矩形+三角形)做自定义标记,这样能完全掌控原点位置:

import pyqtgraph as pg
from PyQt5.QtGui import QPainterPath

class RobotMarker(pg.GraphicsObject):
    def __init__(self, body_length=30, body_width=15, head_length=20):
        super().__init__()
        self.body_length = body_length
        self.body_width = body_width
        self.head_length = head_length
        self.path = self._generate_marker_path()

    def _generate_marker_path(self):
        path = QPainterPath()
        # 绘制车身(矩形,中心在原点)
        path.addRect(-self.body_length/2, -self.body_width/2, self.body_length, self.body_width)
        # 绘制头部(三角形,指向x轴正方向)
        path.moveTo(self.body_length/2, 0)
        path.lineTo(self.body_length/2 + self.head_length, self.head_length/2)
        path.lineTo(self.body_length/2 + self.head_length, -self.head_length/2)
        path.lineTo(self.body_length/2, 0)
        return path

    def boundingRect(self):
        return self.path.boundingRect()

    def paint(self, painter, opt, widget):
        painter.setPen(pg.mkPen('darkred'))
        painter.setBrush(pg.mkBrush('lightcoral'))
        painter.drawPath(self.path)

这个自定义标记的原点天然在中心,旋转、移动都很顺手,还能根据需求自由修改机器人的外观形状,适合需要定制化标记的场景。


关于你提到应用eyllansec的代码后遇到的问题,可以补充具体的报错信息、异常表现(比如箭头位置偏移错误、旋转异常、显示缺失等),这样能更精准地帮你排查问题~

内容的提问来源于stack exchange,提问作者Timo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 09:01:25