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

PyQtGraph十字ROI手柄文本镜像问题修复求助

解决CrossHairROI文本镜像与旋转倒置问题

核心问题分析

文本镜像源于ROI的场景变换矩阵包含Y轴方向的翻转(pyqtgraph坐标系Y轴向下),而旋转时文本会跟随ROI的旋转角度同步转动,导致出现倒置情况。

具体修复方案

1. 修复镜像问题

放弃直接复用ROI的场景变换绘制文本,改为基于手柄的全局坐标计算文本位置,手动抵消坐标系Y轴翻转的影响。

2. 确保旋转时文本可读

ROI旋转时,让文本的旋转角度与ROI的旋转角度反向抵消,始终保持水平正立状态。

修改后的完整代码

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

class CrossHairROI(pg.ROI):
    def __init__(self, pos, size, **kwargs):
        super().__init__(pos, size, **kwargs)
        self.addScaleHandle([1, 0.5], [0, 0.5])
        self.addScaleHandle([0, 0.5], [1, 0.5])
        self.addScaleHandle([0.5, 1], [0.5, 0])
        self.addScaleHandle([0.5, 0], [0.5, 1])
        self.addRotateHandle([1, 1], [0.5, 0.5])
    
    def paint(self, p, opt, widget):
        super().paint(p, opt, widget)
        p.save()  # 隔离文本绘制状态,避免影响ROI其他绘图
        
        # 获取ROI核心参数
        pos = self.pos()
        size = self.size()
        angle = self.angle()  # ROI旋转角度(弧度)
        radius_x = size[0] / 2
        radius_y = size[1] / 2
        
        # 四个手柄的局部坐标
        handle_local_pos = [
            (pos.x() + radius_x, pos.y()),  # 右
            (pos.x() - radius_x, pos.y()),  # 左
            (pos.x(), pos.y() + radius_y),  # 下
            (pos.x(), pos.y() - radius_y)   # 上
        ]
        
        # 文本偏移(避免与手柄重叠)
        text_offsets = [
            QtCore.QPointF(10, 0),   # 右文本右移
            QtCore.QPointF(-50, 0),  # 左文本左移
            QtCore.QPointF(0, 20),   # 下文本下移
            QtCore.QPointF(0, -20)   # 上文本上移
        ]
        
        # 文本内容
        text_contents = [
            f"Width: {2*radius_x:.1f}",
            f"Width: {2*radius_x:.1f}",
            f"Height: {2*radius_y:.1f}",
            f"Height: {2*radius_y:.1f}"
        ]
        
        # 逐个绘制文本
        for local_pos, offset, text in zip(handle_local_pos, text_offsets, text_contents):
            # 转换为全局场景坐标
            scene_pos = self.mapToScene(QtCore.QPointF(local_pos))
            text_pos = scene_pos + offset
            
            # 重置变换,清除ROI的镜像/旋转影响
            p.resetTransform()
            # 反向旋转,抵消ROI的旋转角度,保持文本水平
            p.rotate(-pg.radians(angle))
            # 绘制文本
            p.drawText(text_pos, text)
        
        p.restore()  # 恢复绘图状态

# 测试代码
if __name__ == '__main__':
    app = QtGui.QApplication([])
    win = pg.GraphicsLayoutWidget()
    win.show()
    view = win.addViewBox()
    view.setAspectLocked(True)
    img = pg.ImageItem(pg.gaussianFilter(np.random.normal(size=(100,100)), sigma=2))
    view.addItem(img)
    
    roi = CrossHairROI(pos=(50,50), size=(40,60))
    view.addItem(roi)
    
    app.exec_()

关键修改点说明

  • 用self.mapToScene()将手柄局部坐标转为全局坐标,避开ROI自身变换矩阵导致的镜像。
  • 绘制文本前调用p.resetTransform()清除之前的变换,确保文本绘制不受ROI变换干扰。
  • 通过p.rotate(-pg.radians(angle))反向抵消ROI的旋转角度,让文本始终保持水平可读。
  • 用painter.save()和restore()隔离文本绘制状态,避免影响ROI其他绘图逻辑。

内容的提问来源于stack exchange,提问作者温泽海

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 18:31:01