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

如何在PyQt5 GLScatterPlotItem中使用信号实现点击点获取坐标

3D点云点击获取坐标功能实现方案

错误原因

你混淆了pyqtgraph的两类散点控件:

  • 你查阅的是2D散点控件ScatterPlotItem的相关能力,该控件确实提供sigClicked点击信号
  • 你代码中实际使用的是OpenGL实现的3D散点控件GLScatterPlotItem,该控件本身没有内置sigClicked信号,因此抛出AttributeError

另外你原有代码还存在两处语法逻辑错误:

  • 信号绑定语句self.position.sigClicked.connect(self.clicked())中,槽函数加了括号会直接执行函数,传递的是函数返回值而非函数本身,绑定时需要去掉括号
  • 槽函数中直接用+拼接字符串和坐标对象会触发类型错误,需要先做类型转换或者用格式化字符串输出

实现方案

通过给3D视图绑定鼠标事件,自行实现射线拾取逻辑来获取点击的点坐标,完整实现如下:

1. 初始化代码修改

from pyqtgraph.opengl import GLScatterPlotItem, GLViewWidget
import numpy as np

# 原有3D视图初始化逻辑不变
self.graphtroisd = GLViewWidget()
# 你的点云数据,保留原有生成逻辑即可
self.pos = np.random.randn(100, 3) 
# 建议调大点的显示尺寸,降低拾取难度
self.position = GLScatterPlotItem(pos=self.pos, size=5)
self.graphtroisd.addItem(self.position)
# 给3D视图绑定鼠标点击事件
self.graphtroisd.mousePressEvent = self.on_3d_view_click

2. 拾取逻辑实现

def on_3d_view_click(self, event):
    # 先调用父类原有鼠标事件逻辑,不影响默认的视图旋转平移操作
    super(GLViewWidget, self.graphtroisd).mousePressEvent(event)
    # 获取点击位置坐标
    click_pos = event.pos()
    view_w = self.graphtroisd.width()
    view_h = self.graphtroisd.height()
    # 转换为标准化设备坐标(范围-1~1)
    x = 2 * click_pos.x() / view_w - 1
    y = 1 - 2 * click_pos.y() / view_h

    # 获取当前视图的投影矩阵、模型视图矩阵
    proj_mat = self.graphtroisd.projectionMatrix()
    view_mat = self.graphtroisd.viewMatrix()

    # 计算拾取射线的近、远平面点
    def unproject(point):
        point = np.linalg.inv(proj_mat) @ point
        point /= point[3]
        point = np.linalg.inv(view_mat) @ point
        return point[:3] / point[3]
    
    near_p = unproject(np.array([x, y, -1, 1.0]))
    far_p = unproject(np.array([x, y, 1, 1.0]))
    ray_dir = far_p - near_p
    ray_dir /= np.linalg.norm(ray_dir)

    # 计算所有点到射线的距离
    all_points = self.position.pos
    vec = all_points - near_p[np.newaxis, :]
    cross_res = np.cross(vec, ray_dir[np.newaxis, :])
    dist_list = np.linalg.norm(cross_res, axis=1)

    # 取距离最近的点,小于阈值视为选中
    min_idx = np.argmin(dist_list)
    # 阈值可根据点大小、点云密度自行调整
    if dist_list[min_idx] < 0.2:
        x, y, z = all_points[min_idx]
        print(f"点击点坐标:x={x:.3f}, y={y:.3f}, z={z:.3f}")

3. 注意事项

  • 拾取阈值可根据实际场景调整,阈值越大越容易选中点,同时误选概率也会升高
  • 若点云有额外的缩放、平移、旋转变换,需要将对应变换矩阵加入坐标计算逻辑,保证拾取精度

内容的提问来源于stack exchange,提问作者Pierre-yves Icdc Chaboche

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.13 01:30:03