migrate pyqt5 to pyside6
This commit is contained in:
@@ -13,9 +13,10 @@ from concurrent.futures import Executor
|
||||
|
||||
import numpy as np
|
||||
from numpy.typing import NDArray
|
||||
from PyQt5.QtCore import QEvent, pyqtBoundSignal
|
||||
from PyQt5.QtGui import QMouseEvent, QOpenGLContext, QWheelEvent
|
||||
from PyQt5.QtWidgets import QOpenGLWidget, QWidget
|
||||
from PySide6.QtCore import QEvent, SignalInstance
|
||||
from PySide6.QtGui import QMouseEvent, QOpenGLContext, QWheelEvent
|
||||
from PySide6.QtOpenGLWidgets import QOpenGLWidget
|
||||
from PySide6.QtWidgets import QWidget
|
||||
from orisys_app_shared.acquisition.models import SdkPacket
|
||||
from orisys.finger.runtime import FingerRuntime
|
||||
|
||||
@@ -343,9 +344,9 @@ class GLRenderer(Protocol):
|
||||
|
||||
|
||||
class Sensor3DWidget(QOpenGLWidget):
|
||||
capabilitiesChanged: pyqtBoundSignal # Payload: GLCapabilities.
|
||||
diagnosticRaised: pyqtBoundSignal # Payload: Diagnostic.
|
||||
renderStatsChanged: pyqtBoundSignal # Payload: RenderStats; rate-limited.
|
||||
capabilitiesChanged: SignalInstance # Payload: GLCapabilities.
|
||||
diagnosticRaised: SignalInstance # Payload: Diagnostic.
|
||||
renderStatsChanged: SignalInstance # Payload: RenderStats; rate-limited.
|
||||
def __init__(self, renderer: GLRenderer, camera: OrbitCamera,
|
||||
parent: QWidget | None = None) -> None: ...
|
||||
def set_scene(self, assets: SceneAssets) -> None:
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## 1. 决策与实施范围
|
||||
|
||||
**推荐现有 PyQt5 的 QOpenGLWidget + PyOpenGL,使用现代着色器和 GPU 缓冲区;Open3D 保留为离线几何处理和开发验证工具。** 不为三维显示更换整个 Studio UI 技术栈。
|
||||
**推荐使用现有 Qt 技术栈的 QOpenGLWidget + PyOpenGL(Qt 绑定为 PySide6,LGPLv3),使用现代着色器和 GPU 缓冲区;Open3D 保留为离线几何处理和开发验证工具。** 不为三维显示更换整个 Studio UI 技术栈。
|
||||
|
||||
目标为 Jetson Orin Nano、QCS6490、Ubuntu。目前没有两块实际部署板的 Ubuntu/BSP、Qt 构建方式、窗口系统和驱动版本;这不阻碍接口设计,但禁止承诺“装好 pip 包即可兼容 ARM”。
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|---|---|---|
|
||||
| QOpenGLWidget + PyOpenGL | 嵌入现有 Qt、动态网格、固定色标、分层渲染 | 首选;自己维护相机、shader、资源生命周期,工作量可控 |
|
||||
| Open3D | 点云、配准、网格处理、离线算法验证 | 不作为 Studio 运行时必需依赖;其 GUI 与 ARM 图形栈有额外限制 |
|
||||
| Qt 自身 OpenGL 封装 | Qt 上下文与资源管理;可作为 GL 调用适配实现 | 若 PyOpenGL 与目标平台 EGL 函数加载不匹配,在 GLDispatch 内替换绑定;需验证实际 PyQt5 暴露的接口 |
|
||||
| Qt 自身 OpenGL 封装 | Qt 上下文与资源管理;可作为 GL 调用适配实现 | 若 PyOpenGL 与目标平台 EGL 函数加载不匹配,在 GLDispatch 内替换绑定;需验证实际 PySide6 暴露的接口。Qt6 把 `QOpenGLWidget` 移到了 `QtOpenGLWidgets`,打包时该模块与 `Qt6OpenGLWidgets.dll` 都要收进 bundle |
|
||||
| 更换完整场景引擎/Qt 大版本 | 重型场景、复杂材质、动画 | 第一版需求不足以抵消迁移成本,暂不做 |
|
||||
|
||||
Open3D 官方将 ARM64 支持标为实验性;其支持矩阵区分 Linux 桌面 OpenGL 与 OpenGL ES,后者不支持其 Visualizer/GUI。新渲染模块的文档另要求 OpenGL 4.1。不能把 Jetson 上的运行结果等同于 QCS6490,也不能把 Python 包可安装当作 GUI 可用。[Open3D ARM 支持](https://www.open3d.org/docs/release/arm.html)、[渲染要求](https://www.open3d.org/docs/release/tutorial/visualization/cpu_rendering.html)
|
||||
@@ -366,7 +366,8 @@ Panel 控件:三维开关、预设视角、复位、外壳显隐、显示形
|
||||
|
||||
### 11.1 依赖
|
||||
|
||||
- 继续使用项目验证过的 Python、PyQt5、NumPy;新增 PyOpenGL。先建立兼容版本范围,PoC 通过后分别锁定 Windows/Jetson/QCS6490 的版本与 wheel 哈希,不在文档虚构已验证版本。
|
||||
- 继续使用项目验证过的 Python、**PySide6**、NumPy;新增 PyOpenGL。先建立兼容版本范围,PoC 通过后分别锁定 Windows/Jetson/QCS6490 的版本与 wheel 哈希,不在文档虚构已验证版本。
|
||||
Qt 绑定选 PySide6(LGPLv3)而不是 PyQt5(GPLv3-or-commercial):两个 app 都只允许 PySide6,由 `apps/studio/tests/test_binding_purity.py` 与 `apps/autocal/tests/test_binding_purity.py` 静态强制;构建环境里出现第二个 binding 会让 `pyqtgraph` 绑错(`setup.ps1` 会卸载 PyQt5/PyQt6/PySide2,`main.py` 在 import pyqtgraph 之前设 `PYQTGRAPH_QT_LIB=PySide6`)。
|
||||
- PyOpenGL_accelerate 是可选优化,不作为首轮 ARM 安装前提;官方也将它列为可选项。[PyOpenGL 安装](https://pyopengl.sourceforge.net/documentation/installation.html)
|
||||
- 加密发布模式另引入成熟密码库;Blender、GLB 转换工具只在资产构建环境,客户运行时不依赖 Blender。
|
||||
- PyOpenGL 不自带 GPU 驱动。不要为了满足依赖让 pip/系统包覆盖板卡 BSP 指定的 EGL/GLES 库。
|
||||
|
||||
@@ -186,9 +186,9 @@ graph TB
|
||||
| 规则 | 是否成立 | 证据 |
|
||||
|---|---|---|
|
||||
| 模块级无循环依赖 | ✅ | 扫描 **94 个模块**(studio 37 + shared 57)的 import:**环 0 个** |
|
||||
| 共享层不依赖 Qt | ✅(有一条惰性例外) | `import` 时拉进 PyQt5 的 21 个模块**全部**在 `orisys_studio.*`;`shared/theme/qss.py` 用 PyQt5 **画图标时**才 import(函数内 + try 保护) |
|
||||
| 共享层不依赖 Qt | ✅(有一条惰性例外) | `import` 时拉进 PySide6 的模块**全部**在 `orisys_studio.*`;`shared/theme/qss.py` 用 PySide6 **画图标时**才 import(函数内 + try 保护)。两个 app 的 `tests/test_binding_purity.py` 现在静态强制"只允许 PySide6"(在此之前 `qss.py` 的 import 因为写的是 PyQt5 而静默失败,AutoCal 的箭头图标一直没有画出来) |
|
||||
| OpenGL 从不在 import 时拉入 | ✅ | 唯一使用者 `scene3d/gl_backend.py` 是函数内惰性 import;`widget.py` 同理 |
|
||||
| 三维调度与相机无 Qt,可脱离 GUI 测试 | ✅ | 新解释器里 `import orisys_studio.scene3d.coordinator` / `.camera` → 不含 PyQt5/OpenGL/SDK |
|
||||
| 三维调度与相机无 Qt,可脱离 GUI 测试 | ✅ | 新解释器里 `import orisys_studio.scene3d.coordinator` / `.camera` → 不含 PySide6/OpenGL/SDK |
|
||||
| 采集侧可在无 GUI 环境导入 | ✅ | `acquisition.arrows` / `.sinks`、`record.playback`、`scene3d.contracts`、`scene3d.composer` 全部"干净" |
|
||||
| 共享层与 SDK 解耦 | ⚠️ **不成立** | `orisys_app_shared.force.calibration_io` 在 import 时 `import orisys`;于是 `import orisys_app_shared.force.history` 也会把 SDK 拉进来 |
|
||||
| 三维是单向流动 | ✅ | `window → mesh3d_panel → view → {coordinator, composer, widget, gl_backend, camera}`;`coordinator` 不知道 Qt/GL/IO |
|
||||
@@ -300,7 +300,7 @@ graph TB
|
||||
|---|---|---|---|---|
|
||||
| 1 | ~~**拆 `app_window`**~~ **已完成**(会话 → `session_controller`、回放 → `replay_controller`、布局 → `view_layout`、显示偏好 → `display_prefs`、标定规则 → `calibration_service`;窗口 3044 → 2309 行) | 改动热点从 1 个 3000 行文件变成 5 个 200–650 行文件 + 一个装配器;每个控制器可脱离窗口单测(新增 41 项) | ~~中~~(已付:纯搬运 + 一层转发,未改行为) | `test_ui_smoke`/`test_scene3d_view`/`ui_check` 全绿;`git diff` 只应有移动与转发 |
|
||||
| 1b | **删掉过渡转发**:窗口里那约 40 个 `self._x` 转发是给 tests/`ui_check` 用的桥,下一次改动落在哪个控制器,就顺手把该控制器的转发与测试调用点改成 `self.<controller>.*` | 窗口真正退到"只有装配";转发留着会慢慢变成新的隐式耦合 | 小-中(每次改动顺带;一次性清理会大面积改测试) | 全套 + `ui_check` |
|
||||
| 2 | **给共享层加"依赖规则测试"**:AST 扫描断言 `shared` 不得 eager import PyQt5/OpenGL(现状成立),以及 `studio.scene3d` 不得 import `acquisition`;把 `force → orisys` 的现状登记为"已知例外"或改为惰性 | 把"现在对"变成"以后不会错";顺手暴露第 4 条风险 | 小(一个测试文件,用现成扫描脚本改) | 新测试 + 现有套件 |
|
||||
| 2 | **给共享层加"依赖规则测试"**:AST 扫描断言 `shared` 不得 eager import PySide6/OpenGL(现状成立),以及 `studio.scene3d` 不得 import `acquisition`;把 `force → orisys` 的现状登记为"已知例外"或改为惰性 —— *部分已完成*:`apps/studio/tests/test_binding_purity.py` 与 `apps/autocal/tests/test_binding_purity.py` 已经强制"只允许 PySide6"(含无作用域枚举的检查,且用真 binding 内省而不是手写枚举名单);`studio.scene3d` 与 `force → orisys` 两条仍未加 | 把"现在对"变成"以后不会错";顺手暴露第 4 条风险 | 小(一个测试文件,用现成扫描脚本改) | 新测试 + 现有套件 |
|
||||
| 3 | **消歧命名**:`scene3d/panel.py` → `scene3d/controls.py`;`ui/mesh3d_panel.py` → `ui/scene3d_host.py`;`scene3d/replay` 与 `record/playback` 在 docstring 里互相指路 | 新人少走 3 次错门 | 小(改名 + import 更新),但会触及 ui_check 的引用 | 全套 + `ui_check` |
|
||||
| 4 | **把 `force → SDK` 改为惰性**(`calibration_io` 里改成函数内 import,或把 SDK 相关函数拆出去) | 力曲线的纯计算(history/filters/scale)可在无 SDK 环境测试 | 中(要区分哪些函数真的需要 SDK) | 现有力测试 + 无 SDK 的 import 检查 |
|
||||
| 5 | **`ui_check` 拆模块**(按场景拆 `tools/ui_checks/*.py`,保留单一入口与退出码) | 最脆的资产变成可分工、可 diff 的几块 | 中(3401 行搬运,语义不变) | 检查项计数不变(587 项 0 失败) |
|
||||
|
||||
Reference in New Issue
Block a user