Files
Haocode/ui/views/custom_web_page.py
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

77 lines
3.3 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.
from PyQt6.QtWebEngineCore import QWebEnginePage, QWebEngineScript, QWebEngineProfile
from PyQt6.QtCore import QUrl
from PyQt6.QtGui import QDesktopServices
class CustomWebPage(QWebEnginePage):
"""
自定义网页类,集中约束内置 QtWebEngine 的不合规浏览器行为:
1. 拦截所有外部链接跳转,改用系统默认浏览器打开;
2. 禁用 Ctrl/Meta + 滚轮 及 Ctrl + +/-/0 快捷键的页面缩放
(浏览器式缩放对桌面聊天工具无意义且易误触)。
"""
def __init__(self, profile_or_parent=None, parent=None):
# P1-03:兼容两种调用 —— 新式 CustomWebPage(profile, parent) 与旧式 CustomWebPage(parent)。
# profile 缺省 None → QtWebEngine 默认 profilemain_window 传入本实例独立 profile。
if isinstance(profile_or_parent, QWebEngineProfile):
profile, par = profile_or_parent, parent
else:
profile, par = None, profile_or_parent
if parent is not None:
raise TypeError("CustomWebPage(profile, parent) 或 CustomWebPage(parent)")
if profile is not None:
super().__init__(profile, par)
else:
super().__init__(par)
self._inject_zoom_lock()
def _inject_zoom_lock(self):
"""
🌟 缩放锁定:通过网页脚本在文档创建阶段(DocumentCreation)注入监听,
使用隔离世界(ApplicationWorld)注入,不污染页面自身逻辑。
原理:Chromium 的 Ctrl+滚轮缩放会先经过页面 wheel 事件的
preventDefault() 判定 —— 页面阻止即取消缩放,这是地图类应用
禁用缩放的官方做法;Ctrl+=/-/0 快捷键同理用 keydown 拦截。
"""
script = QWebEngineScript()
script.setName("haocode_zoom_lock")
script.setInjectionPoint(QWebEngineScript.InjectionPoint.DocumentCreation)
script.setWorldId(QWebEngineScript.ScriptWorldId.ApplicationWorld)
script.setRunsOnSubFrames(True)
script.setSourceCode(r"""
(function () {
if (window.__haocodeZoomLocked) return; // 防重复注入
window.__haocodeZoomLocked = true;
// 1) Ctrl/Meta + 滚轮 → 阻止缩放
document.addEventListener('wheel', function (e) {
if (e.ctrlKey || e.metaKey) {
e.preventDefault();
e.stopPropagation();
}
}, { passive: false }); // 必须非被动,preventDefault 才有效
// 2) Ctrl + = / + / - / 0 → 阻止缩放快捷键
document.addEventListener('keydown', function (e) {
if ((e.ctrlKey || e.metaKey) && ['+', '-', '=', '0'].indexOf(e.key) >= 0) {
e.preventDefault();
}
}, false);
})();
""")
self.scripts().insert(script)
def acceptNavigationRequest(self, url: QUrl, nav_type, is_main_frame):
# 如果是本地文件(我们自己的 index.html),允许加载
if url.scheme() == "file":
return True
# 如果是外部链接(http/https),用系统浏览器打开
if url.scheme() in ["http", "https"]:
print(f"[System]: 在系统浏览器中打开 -> {url.toString()}")
QDesktopServices.openUrl(url)
return False # 阻止在应用内跳转
# 其他情况(如 javascript:void(0)),允许
return True