Files
sorrow404null 554be4f92b feat(renderer): platform-gated dual renderer backend with isolated QtWebEngine profiles
Windows prefers WebView2, Linux uses QtWebEngine only; pythonnet deps are win32-marked. Each QtWebEngine instance gets its own profile under data/webengine, and Chromium flags are sanitized before QApplication (--no-sandbox accepted only for explicit root/container use).
2026-09-17 16:40:03 +08:00

120 lines
5.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# main.py
import io
import os
import sys
# ==================== 🌟 冻结/无控制台环境:stdout/stderr 安全化 ====================
# 🐛 打包成 exe 后踩到的真 bug(开发环境永远不会暴露):
# 打包版没有控制台,sys.stdout 可能是 None 或编码为 GBK 的流 →
# 任何 print 里的 emoji(🚀✅🛑…)都会抛 UnicodeEncodeError
# 而且是在 MainWindow() 之前就崩 → **主窗口根本不出现**。
# 统一改成 UTF-8 + errors=replace:打包版与开发版行为一致,
# 即使控制台是 GBK,也只会显示成 '?' 而不会崩。
for _name in ("stdout", "stderr"):
_s = getattr(sys, _name, None)
if _s is None:
try:
setattr(sys, _name,
open(os.devnull, "w", encoding="utf-8", errors="replace"))
except Exception:
pass
continue
try:
_s.reconfigure(encoding="utf-8", errors="replace")
except Exception:
try:
setattr(sys, _name, io.TextIOWrapper(
_s.buffer, encoding="utf-8", errors="replace", line_buffering=True))
except Exception:
pass
# ==================== 🌟 WebEngine 渲染配置(必须在 PyQt6 导入之前设置)====================
# 本机实测渲染模式:GPU 硬件加速(ANGLE → Direct3D11, AMD 核显),非 CPU 软渲染。
#
# 黑边/黑框背景(已实测):
# Windows 上窗口缩放的新暴露区域未出帧时,GPU 合成器露出黑色、软件合成器露出白色。
# 基准数据(重度页面 500 节点+滚动+强制重绘):
# GPU 合成 59.2 FPS(黑框)
# 软件合成 30.1 FPS(白框,视觉反差小;轻量聊天页实测跟手无差别)
# 因此默认采用“软件合成”(白框、好看);若遇重度页面掉帧,用 HAOCODE_COMPOSITING=gpu 回退。
#
# ⚠️ 2026-07 重大变更:默认改为全 CPU 软渲染(--disable-gpu)。
# 原因:本机 AMD 核显 GPU 上下文周期性丢失(context lost 实测复现),
# GPU 光栅化产物无法合成上屏 → 流式正文 DOM 已写入但屏幕不刷新。
# 软渲染在 Chromium 内全 CPU 完成,彻底绕开该故障模式;聊天页性能足够。
#
# 备选开关(二选一,或直接设置 QTWEBENGINE_CHROMIUM_FLAGS 完全覆盖):
# HAOCODE_RENDER=gpu → 旧默认:GPU 光栅化 + 软合成(需 GPU 驱动稳定)
# HAOCODE_COMPOSITING=gpu → 回退 GPU 合成(黑框、满帧)
if "QTWEBENGINE_CHROMIUM_FLAGS" not in os.environ:
_render_mode = os.environ.get("HAOCODE_RENDER", "").strip().lower()
_comp_mode = os.environ.get("HAOCODE_COMPOSITING", "soft").strip().lower()
if _render_mode in ("gpu", "hardware"):
# 旧默认:GPU 光栅化 + 软合成(仅当 GPU 驱动稳定时使用)
_flags = ["--enable-gpu-rasterization", "--enable-zero-copy", "--ignore-gpu-blocklist"]
if _comp_mode != "gpu": # 默认软合成:黑框→白框
_flags.append("--disable-gpu-compositing")
os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = " ".join(_flags)
else:
# 默认(含 software):全 CPU 软渲染,规避 GPU 上下文丢失导致的不刷新
os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = "--disable-gpu"
print(f"[渲染] QTWEBENGINE_CHROMIUM_FLAGS = {os.environ.get('QTWEBENGINE_CHROMIUM_FLAGS', '(未设置)')}", flush=True)
from PyQt6.QtWidgets import QApplication
from PyQt6.QtCore import Qt
# 确保 Python 能找到项目根目录下的模块
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
# P1-03Chromium sandbox 契约(在导入 PyQt6/QtWebEngine 前处理;renderer_backend 仅 stdlib
# - 默认保留 sandbox--no-sandbox 仅在显式配置 + root/容器场景下接受(高可见警告)
# - 普通桌面运行中的 --no-sandbox 被剥离并告警
from core import renderer_backend as _renderer_backend
os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = _renderer_backend.sanitize_chromium_flags(
os.environ["QTWEBENGINE_CHROMIUM_FLAGS"])
print(f"[渲染] 最终 QTWEBENGINE_CHROMIUM_FLAGS = {os.environ['QTWEBENGINE_CHROMIUM_FLAGS']!r}", flush=True)
# 导入主窗口类
from ui.views.main_window import MainWindow
def main():
# 1. 开启高 DPI 缩放支持 (让界面在 2K/4K 屏幕上不模糊)
# PyQt6 默认已经处理得很好了,但加上这句更稳妥
if hasattr(Qt, 'AA_EnableHighDpiScaling'):
QApplication.setAttribute(Qt.ApplicationAttribute.AA_EnableHighDpiScaling, True)
if hasattr(Qt, 'AA_UseHighDpiPixmaps'):
QApplication.setAttribute(Qt.ApplicationAttribute.AA_UseHighDpiPixmaps, True)
# 2. 初始化 QApplication 实例
app = QApplication(sys.argv)
# 极客细节:设置应用的全局字体 (可选)
# font = app.font()
# font.setFamily("Segoe UI") # Windows 推荐字体
# app.setFont(font)
# 防御:若系统解析出的默认字体 pointSize 无效(<=0),补一个合法值,
# 消除启动时 "QFont::setPointSize: Point size <= 0 (-1)" 的 Qt 警告(纯警告,非异常)
_app_font = app.font()
if _app_font.pointSize() <= 0:
_app_font.setPointSize(9)
app.setFont(_app_font)
print("🚀 GeekAgent-Studio 正在启动...")
print("--------------------------------------------------")
# 3. 实例化主窗口
window = MainWindow()
# 4. 显示窗口
window.show()
# 5. 进入主事件循环,并安全退出
sys.exit(app.exec())
if __name__ == "__main__":
main()