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

PyQtGraph:如何计算ViewBox内角度以实现曲线文本对齐

解决PyQtGraph中文本与曲线对齐的角度计算问题

想要让文本项根据曲线前两个数据点的角度对齐,实现类似算盘的样式,但直接通过数据坐标计算的角度无法匹配ViewBox的显示刻度,导致文本未正确对齐。

核心原因

直接用数据点的y/x比值计算的是数据空间的斜率,但PyQtGraph的TextItem.angle参数使用的是屏幕空间的角度(以度为单位,顺时针为正)。两者因ViewBox的缩放比例不同,无法直接对应。

解决方案

将数据点转换为屏幕像素坐标,基于像素差值计算实际显示角度,步骤如下:

  1. 获取PlotWidget的ViewBox实例
  2. 将目标数据点转换为屏幕像素坐标
  3. 根据像素坐标的差值计算屏幕角度(注意屏幕y轴向下的特性)

修改后的完整代码

import pyqtgraph as pg
import numpy as np
from PyQt5 import QtCore
from PyQt5.QtGui import QFont

# plot widget
pw = pg.plot(title="How to align the text item with the curves ?", pen="b")


def abacus(
        pw,         # a plot widget item
        x, ys,      # same x for all ys curves
        labels,     # text to display on each curve
        indx_txt=0, # index used to compute text angle and position
        width=2, color=(140, 140, 140, 140), fontsize=12
    ):
    
    # to store and return created items
    lines = []
    viewbox = pw.getViewBox()  # 获取ViewBox实例
    
    for y, lab in zip(ys, labels):
        line = pw.plot(
            x, y,
            pen=pg.mkPen(color=color, width=width, style=QtCore.Qt.DashLine),
            width=width, 
            name=lab, 
        )
        
        # 将数据点转换为屏幕像素坐标
        p1 = viewbox.mapToDevice(QtCore.QPointF(x[indx_txt], y[indx_txt]))
        p2 = viewbox.mapToDevice(QtCore.QPointF(x[indx_txt+1], y[indx_txt+1]))
        
        # 计算屏幕坐标的差值,注意屏幕y轴向下,dy取反
        dx = p2.x() - p1.x()
        dy = p2.y() - p1.y()
        
        # 计算屏幕角度(转换为度,TextItem使用顺时针角度)
        angle = np.degrees(np.arctan2(-dy, dx))
        
        txt = pg.TextItem(
            text=lab,
            color=color, 
            anchor=(0, 0.9),
            border=None, 
            angle=angle,     # 使用转换后的屏幕角度
        )
        
        font = QFont()
        font.setPixelSize(fontsize)
        txt.setFont(font)
        pw.addItem(txt)
        txt.setPos(x[indx_txt], y[indx_txt])
        lines.append((line, txt))
    return lines
        
        
# create dummy data and labels
x = np.arange(5, 8)
ys = 2*(3*(x+3)+np.outer(x, x))
labs = [f"This is the {i} curve" for i in range(3)]
    
lines = abacus(pw, x, ys, labs)

pg.exec()

关键细节说明

  • viewbox.mapToDevice():将数据坐标系的点转换为屏幕像素坐标,消除ViewBox缩放的影响
  • np.arctan2(-dy, dx):屏幕y轴方向与常规数学坐标系相反,因此对dy取反,确保角度计算符合屏幕显示逻辑
  • np.degrees():将弧度转换为度,匹配TextItem.angle的参数要求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 15:05:25