diff --git a/ui/main_window.py b/ui/main_window.py index 7964bdd9..717045be 100644 --- a/ui/main_window.py +++ b/ui/main_window.py @@ -1,5 +1,4 @@ - -""" +""" 主窗口设置模块 负责主窗口的界面构建和事件绑定: @@ -7,22 +6,259 @@ - 停靠窗口设置 - 事件连接和信号处理 """ -import os +WEB_BROWSER_PROCESS_CODE = r""" import sys +import os +from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout +from PyQt5.QtCore import Qt, QUrl, QTimer +from PyQt5.QtGui import QPalette, QColor -from PyQt5.QtGui import QKeySequence, QIcon, QPalette, QColor +# 禁用硬件加速 +os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = "--disable-gpu --disable-software-rasterizer --disable-gpu-compositing --in-process-gpu" +os.environ["QT_WEBENGINE_DISABLE_GPU"] = "1" +os.environ["QT_OPENGL"] = "software" -# QtWebEngine 导入(环境变量已在 main.py 中配置) try: from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings WEB_ENGINE_AVAILABLE = True - print("✓ QtWebEngineWidgets 已导入") except ImportError: WEB_ENGINE_AVAILABLE = False - QWebEngineView = None + print("⚠️ QtWebEngine 不可用") +class EmbeddedWebBrowser(QWidget): + + def __init__(self, parent=None, url="https://www.baidu.com", parent_window_id=0): + super().__init__(parent) + self.setWindowTitle("Web 浏览器") + self.parent_window_id = parent_window_id + self.url = url + + # 设置窗口标志 - 如果要嵌入,使用子窗口样式 + if parent_window_id > 0: + # 使用 FramelessWindowHint 去除边框,便于嵌入 + self.setWindowFlags(Qt.FramelessWindowHint) + else: + self.setWindowFlags(Qt.Window) + + self.resize(800, 600) + + # 设置背景色 + palette = self.palette() + palette.setColor(QPalette.Window, QColor(25, 25, 27)) + self.setPalette(palette) + self.setAutoFillBackground(True) + + # 创建布局 + layout = QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + + if WEB_ENGINE_AVAILABLE: + # 创建 WebEngine 视图 + self.web_view = QWebEngineView() + + # 设置样式 + self.web_view.setStyleSheet("background-color: #19191b;") + + # 禁用硬件加速 + settings = self.web_view.settings() + settings.setAttribute(QWebEngineSettings.Accelerated2dCanvasEnabled, False) + settings.setAttribute(QWebEngineSettings.WebGLEnabled, False) + settings.setAttribute(QWebEngineSettings.PluginsEnabled, False) + settings.setAttribute(QWebEngineSettings.JavascriptEnabled, True) + settings.setAttribute(QWebEngineSettings.AutoLoadImages, True) + settings.setAttribute(QWebEngineSettings.LocalContentCanAccessRemoteUrls, True) + + # 连接加载信号 + self.web_view.loadStarted.connect(self.on_load_started) + self.web_view.loadProgress.connect(self.on_load_progress) + self.web_view.loadFinished.connect(self.on_load_finished) + + layout.addWidget(self.web_view) + print("[WebEngine] WebEngine 视图已创建") + + # 延迟加载网页,确保窗口完全初始化 + QTimer.singleShot(500, lambda: self.load_url(url)) + else: + from PyQt5.QtWidgets import QLabel + label = QLabel("QtWebEngine 不可用\n请安装: pip install PyQtWebEngine") + label.setAlignment(Qt.AlignCenter) + label.setStyleSheet("color: white; font-size: 14px;") + layout.addWidget(label) + + def on_load_started(self): + print("[WebEngine] 开始加载网页...") + + def on_load_progress(self, progress): + if progress % 20 == 0: # 每20%打印一次 + print(f"[WebEngine] 加载进度: {progress}%") + + def on_load_finished(self, success): + if success: + print("[WebEngine] 网页加载成功") + else: + print("[WebEngine] 网页加载失败") + + def load_url(self, url): + if WEB_ENGINE_AVAILABLE and hasattr(self, 'web_view'): + print(f"[WebEngine] 准备加载 URL: {url}") + qurl = QUrl(url) + print(f"[WebEngine] QUrl 有效性: {qurl.isValid()}") + print(f"[WebEngine] QUrl 内容: {qurl.toString()}") + self.web_view.load(qurl) + print(f"[WebEngine] load() 方法已调用") + else: + print(f"[WebEngine] 无法加载 URL - WEB_ENGINE_AVAILABLE={WEB_ENGINE_AVAILABLE}, has_web_view={hasattr(self, 'web_view')}") + + def get_native_window_id(self): + + return int(self.winId()) + + def embed_into_parent(self): + if self.parent_window_id > 0: + try: + print(f"[WebEngine] 开始嵌入窗口到父窗口 ID: {self.parent_window_id}") + + # Windows 平台使用 win32 API 嵌入窗口 + if sys.platform == 'win32': + import ctypes + from ctypes import wintypes + + # 获取窗口句柄 + hwnd = int(self.winId()) + parent_hwnd = self.parent_window_id + + print(f"[WebEngine] 子窗口句柄: {hwnd}") + print(f"[WebEngine] 父窗口句柄: {parent_hwnd}") + + user32 = ctypes.windll.user32 + + # 先获取父窗口大小 + rect = wintypes.RECT() + user32.GetClientRect(parent_hwnd, ctypes.byref(rect)) + width = rect.right - rect.left + height = rect.bottom - rect.top + print(f"[WebEngine] 父窗口大小: {width}x{height}") + + # 设置父窗口 + result = user32.SetParent(hwnd, parent_hwnd) + print(f"[WebEngine] SetParent 结果: {result}") + + # 设置窗口样式为子窗口 + GWL_STYLE = -16 + WS_CHILD = 0x40000000 + WS_VISIBLE = 0x10000000 + WS_CLIPCHILDREN = 0x02000000 + WS_CLIPSIBLINGS = 0x04000000 + + old_style = user32.GetWindowLongW(hwnd, GWL_STYLE) + print(f"[WebEngine] 旧窗口样式: {hex(old_style)}") + + # 设置为子窗口样式,移除边框和标题栏 + new_style = (WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS) + user32.SetWindowLongW(hwnd, GWL_STYLE, new_style) + + print(f"[WebEngine] 新窗口样式: {hex(new_style)}") + + # 调整窗口位置和大小,填充整个父窗口 + SWP_FRAMECHANGED = 0x0020 + SWP_SHOWWINDOW = 0x0040 + SWP_NOZORDER = 0x0004 + + result = user32.SetWindowPos( + hwnd, + 0, # HWND_TOP + 0, 0, # x, y + width, height, # width, height + SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOZORDER + ) + print(f"[WebEngine] SetWindowPos 结果: {result}") + + # 强制刷新窗口 + user32.UpdateWindow(hwnd) + user32.InvalidateRect(hwnd, None, True) + + print(f"[WebEngine] 已成功嵌入到父窗口 (Windows)") + return True + else: + # 其他平台使用 Qt 的方式 + from PyQt5.QtGui import QWindow + parent_window = QWindow.fromWinId(self.parent_window_id) + if parent_window: + self.windowHandle().setParent(parent_window) + print(f"[WebEngine] 已嵌入到父窗口 (Qt): {self.parent_window_id}") + return True + except Exception as e: + print(f"[WebEngine] 嵌入窗口失败: {e}") + import traceback + traceback.print_exc() + return False + + +def main(): + + # 设置标准输出编码为 UTF-8,避免 Windows GBK 编码问题 + if sys.platform == 'win32': + import io + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace') + + print("=" * 60) + print("[WebEngine] 启动独立 WebEngine 进程") + print("=" * 60) + + # 创建应用 + app = QApplication(sys.argv) + + # 从命令行参数获取 URL 和窗口 ID + url = sys.argv[1] if len(sys.argv) > 1 else "https://www.baidu.com" + parent_window_id = int(sys.argv[2]) if len(sys.argv) > 2 else 0 + + print(f"[WebEngine] 参数:") + print(f" - URL: {url}") + print(f" - 父窗口 ID: {parent_window_id}") + + # 创建浏览器窗口 + browser = EmbeddedWebBrowser(url=url, parent_window_id=parent_window_id) + + # 显示窗口 + browser.show() + + # 输出窗口 ID 供父进程使用 + window_id = browser.get_native_window_id() + print(f"WINDOW_ID:{window_id}") + sys.stdout.flush() + + # 等待网页开始加载后再嵌入窗口 + if parent_window_id > 0: + # 延迟2秒,确保网页已经开始加载和渲染 + QTimer.singleShot(10, browser.embed_into_parent) + + print("[WebEngine] 进程已就绪") + print("=" * 60) + + sys.exit(app.exec_()) + + +if __name__ == "__main__": + main() +""" + +import os +import sys + +# ==================== 禁用 QtWebEngine 硬件加速 ==================== +# 必须在导入 QtWebEngine 之前设置环境变量 +os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = "--disable-gpu --disable-software-rasterizer --disable-gpu-compositing --in-process-gpu" +os.environ["QT_WEBENGINE_DISABLE_GPU"] = "1" +os.environ["QT_OPENGL"] = "software" +print("✓ QtWebEngine 硬件加速已禁用,使用软件渲染模式") +# ==================== 结束配置 ==================== + +from PyQt5.QtGui import QKeySequence, QIcon, QPalette, QColor +# from PyQt5.QtWebEngineWidgets import QWebEngineView from PyQt5.QtWidgets import (QApplication, QMainWindow, QMenuBar, QMenu, QAction, QDockWidget, QTreeWidget, QListWidget, QWidget, QVBoxLayout, QTreeWidgetItem, QLabel, QLineEdit, QFormLayout, QDoubleSpinBox, QScrollArea, @@ -39,7 +275,7 @@ from ui.widgets import (CustomMeta3DWidget, CustomFileView, CustomTreeWidget, UniversalMessageDialog) from ui.icon_manager import get_icon_manager, get_icon -import yaml +# import yaml from PyQt5.QtWidgets import QDialog, QVBoxLayout, QListWidget, QPushButton, QDialogButtonBox, QCheckBox, QLabel, QHBoxLayout from PyQt5.QtCore import Qt @@ -1516,13 +1752,13 @@ class MainWindow(QMainWindow): self.createSamplePanelAction.triggered.connect(self.world.info_panel_manager.onCreateSampleInfoPanel) self.create3DSamplePanelAction.triggered.connect(self.onCreate3DSampleInfoPanel) + #self.webBrowserAction.triggered.connect(self.openWebBrowser_win) if sys.platform == 'win32': self.webBrowserAction.triggered.connect(self.openWebBrowser_win) else: self.webBrowserAction.triggered.connect(self.openWebBrowser) - def getCreateMenuActions(self): """获取所有创建菜单动作的字典 - 供右键菜单使用""" return { @@ -2307,90 +2543,90 @@ class MainWindow(QMainWindow): self.bottomDock.setWidget(bottomDockContainer) self.addDockWidget(Qt.BottomDockWidgetArea, self.bottomDock) - # # 创建底部停靠控制台 - # self.consoleDock = QDockWidget("控制台", self) - # self.consoleDock.setStyleSheet(""" - # QDockWidget { - # background-color: #19191b; - # color: #ffffff; - # border: none; - # } - # QDockWidget::title { - # background-color: #19191b; - # padding: 8px 10px; - # border-bottom: none; - # text-align: left; - # font-family: 'Microsoft YaHei', 'Inter', sans-serif; - # font-size: 14px; - # font-weight: 500; - # } - # QDockWidget::close-button { - # background-color: rgba(89, 100, 113, 0.3); - # border: 1px solid rgba(184, 211, 241, 0.2); - # icon-size: 10px; - # border-radius: 2px; - # width: 16px; - # height: 16px; - # right: 6px; - # top: 6px; - # } - # QDockWidget::float-button { - # background-color: rgba(89, 100, 113, 0.3); - # border: 1px solid rgba(184, 211, 241, 0.2); - # icon-size: 10px; - # border-radius: 2px; - # width: 16px; - # height: 16px; - # right: 26px; - # top: 6px; - # } - # QDockWidget::close-button:hover, QDockWidget::float-button:hover { - # background-color: rgba(89, 100, 113, 0.5); - # border: 1px solid rgba(184, 211, 241, 0.3); - # } - # QDockWidget::close-button:pressed, QDockWidget::float-button:pressed { - # background-color: #3067c0; - # border: 1px solid rgba(48, 103, 192, 0.6); - # } - # """) - # self.consoleView = CustomConsoleDockWidget(self.world) - # # 为控制台添加样式 - # self.consoleView.setStyleSheet(""" - # QTextEdit { - # background-color: #19191b; - # color: #ebebeb; - # border: none; - # font-family: 'Consolas', 'Monaco', 'Microsoft YaHei', monospace; - # font-size: 10px; - # font-weight: 300; - # } - # """) - # - # # 创建包装容器,添加标题下方的分隔线 - # consoleDockContainer = QWidget() - # consoleDockContainer.setStyleSheet("QWidget { background-color: #19191b; }") - # consoleDockLayout = QVBoxLayout(consoleDockContainer) - # consoleDockLayout.setContentsMargins(0, 0, 0, 0) - # consoleDockLayout.setSpacing(0) - # - # # 添加带左右间距的分隔线 - # consoleSeparator = QFrame() - # consoleSeparator.setFrameShape(QFrame.HLine) - # consoleSeparator.setFrameShadow(QFrame.Plain) - # consoleSeparator.setStyleSheet(""" - # QFrame { - # background-color: rgba(77, 116, 189, 0.4); - # max-height: 1px; - # margin-left: 8px; - # margin-right: 8px; - # border: none; - # } - # """) - # consoleDockLayout.addWidget(consoleSeparator) - # consoleDockLayout.addWidget(self.consoleView) - # - # self.consoleDock.setWidget(consoleDockContainer) - # self.addDockWidget(Qt.BottomDockWidgetArea, self.consoleDock) + # 创建底部停靠控制台 + self.consoleDock = QDockWidget("控制台", self) + self.consoleDock.setStyleSheet(""" + QDockWidget { + background-color: #19191b; + color: #ffffff; + border: none; + } + QDockWidget::title { + background-color: #19191b; + padding: 8px 10px; + border-bottom: none; + text-align: left; + font-family: 'Microsoft YaHei', 'Inter', sans-serif; + font-size: 14px; + font-weight: 500; + } + QDockWidget::close-button { + background-color: rgba(89, 100, 113, 0.3); + border: 1px solid rgba(184, 211, 241, 0.2); + icon-size: 10px; + border-radius: 2px; + width: 16px; + height: 16px; + right: 6px; + top: 6px; + } + QDockWidget::float-button { + background-color: rgba(89, 100, 113, 0.3); + border: 1px solid rgba(184, 211, 241, 0.2); + icon-size: 10px; + border-radius: 2px; + width: 16px; + height: 16px; + right: 26px; + top: 6px; + } + QDockWidget::close-button:hover, QDockWidget::float-button:hover { + background-color: rgba(89, 100, 113, 0.5); + border: 1px solid rgba(184, 211, 241, 0.3); + } + QDockWidget::close-button:pressed, QDockWidget::float-button:pressed { + background-color: #3067c0; + border: 1px solid rgba(48, 103, 192, 0.6); + } + """) + self.consoleView = CustomConsoleDockWidget(self.world) + # 为控制台添加样式 + self.consoleView.setStyleSheet(""" + QTextEdit { + background-color: #19191b; + color: #ebebeb; + border: none; + font-family: 'Consolas', 'Monaco', 'Microsoft YaHei', monospace; + font-size: 10px; + font-weight: 300; + } + """) + + # 创建包装容器,添加标题下方的分隔线 + consoleDockContainer = QWidget() + consoleDockContainer.setStyleSheet("QWidget { background-color: #19191b; }") + consoleDockLayout = QVBoxLayout(consoleDockContainer) + consoleDockLayout.setContentsMargins(0, 0, 0, 0) + consoleDockLayout.setSpacing(0) + + # 添加带左右间距的分隔线 + consoleSeparator = QFrame() + consoleSeparator.setFrameShape(QFrame.HLine) + consoleSeparator.setFrameShadow(QFrame.Plain) + consoleSeparator.setStyleSheet(""" + QFrame { + background-color: rgba(77, 116, 189, 0.4); + max-height: 1px; + margin-left: 8px; + margin-right: 8px; + border: none; + } + """) + consoleDockLayout.addWidget(consoleSeparator) + consoleDockLayout.addWidget(self.consoleView) + + self.consoleDock.setWidget(consoleDockContainer) + self.addDockWidget(Qt.BottomDockWidgetArea, self.consoleDock) # 配置Dock区域的角落归属 # 使右侧和下侧的Dock面板可以占据右下角区域 @@ -2414,7 +2650,7 @@ class MainWindow(QMainWindow): # 确保其他面板也正常显示 self.bottomDock.raise_() - #self.consoleDock.raise_() + self.consoleDock.raise_() self.leftDock.raise_() # ========================================================================= # ↓↓↓ 为停靠窗口的标签栏(QTabBar)设置统一样式(根据Figma设计)↓↓↓ @@ -3929,70 +4165,181 @@ class MainWindow(QMainWindow): self.world.setCurrentTool(None) print("工具栏: 取消选择工具") - # def openWebBrowser_win(self): - # """ - # 使用 QMainWindow 窗口显示网页 - # 创建独立的 Web 浏览器窗口 - # """ - # from PyQt5.QtWidgets import QMainWindow, QVBoxLayout, QWidget - # from PyQt5.QtWebEngineWidgets import QWebEngineView - # from PyQt5.QtCore import QUrl - # - # # 创建独立的浏览器窗口 - # window = QMainWindow(self) - # window.setWindowTitle("Web浏览器") - # window.setGeometry(100, 100, 1024, 768) - # - # # 创建中央部件和布局 - # central_widget = QWidget() - # window.setCentralWidget(central_widget) - # layout = QVBoxLayout(central_widget) - # - # # 创建 WebEngine 视图 - # web_view = QWebEngineView() - # web_view.load(QUrl("https://www.baidu.com")) - # - # # 添加到布局 - # layout.addWidget(web_view) - # - # # 显示窗口 - # window.show() - def openWebBrowser_win(self): - """ - 在独立进程中运行Web浏览器(无终端窗口) - 避免与Panda3D的OpenGL上下文冲突 - """ - import subprocess - import sys - import os - - # 获取当前脚本目录 - current_dir = os.path.dirname(os.path.abspath(__file__)) - browser_script = os.path.join(current_dir, '..', 'standalone_web_browser.py') - - # 在独立进程中启动Web浏览器,不创建终端窗口 + """打开Web浏览器面板(使用独立进程避免与Panda3D冲突)""" try: - if sys.platform == "win32": - subprocess.Popen([sys.executable, browser_script], - creationflags=subprocess.CREATE_NO_WINDOW) - else: - subprocess.Popen([sys.executable, browser_script]) - print("✓ 独立Web浏览器已在后台启动") - except Exception as e: - print(f"✗ 启动独立Web浏览器失败: {e}") - # 如果启动失败,则使用系统默认浏览器作为备选方案 - import webbrowser - webbrowser.open("https://www.baidu.com") + from PyQt5.QtWidgets import QDockWidget, QWidget, QVBoxLayout + from PyQt5.QtCore import QProcess, Qt + import subprocess + import sys - QWebEngineSettings = None - print("⚠ QtWebEngineWidgets 未找到,Web 浏览器功能将不可用") + main_window = self.world.main_window + + # 尝试获取主窗口引用 + if main_window is None: + print("🔍 尝试获取主窗口引用...") + if hasattr(self.world, 'interface_manager'): + print(f" - interface_manager 存在: {self.world.interface_manager}") + if hasattr(self.world.interface_manager, 'main_window'): + main_window = self.world.interface_manager.main_window + print(f" - interface_manager.main_window: {main_window}") + + if main_window is None and hasattr(self.world, 'main_window'): + main_window = self.world.main_window + print(f" - world.main_window: {main_window}") + + if main_window is None and self.world.treeWidget: + try: + main_window = self.world.treeWidget.window() + print(f" - 从 treeWidget 获取窗口: {main_window}") + except: + pass + + if main_window is None: + print("✗ 无法获取主窗口引用") + UniversalMessageDialog.show_warning( + self, "错误", "无法获取主窗口引用", False, "确定" + ) + return None + else: + print(f"✅ 使用传入的主窗口引用: {main_window}") + + # 检查主窗口是否有效 + if not hasattr(main_window, 'addDockWidget'): + print(f"✗ 主窗口引用无效,缺少 addDockWidget 方法") + return None + + # 检查是否已经存在浏览器视图 + for element in self.world.gui_elements: + if hasattr(element, 'objectName') and element.objectName() == "WebBrowserView": + print("⚠ 浏览器视图已经存在") + element.show() + element.raise_() + return element + + print(f"🔧 创建浏览器停靠窗口(独立进程模式),父窗口: {main_window}") + + # 创建停靠窗口 + browser_dock = QDockWidget("信息面板", main_window) + browser_dock.setObjectName("WebBrowserView") + + # 创建容器 Widget + container_widget = QWidget() + container_layout = QVBoxLayout(container_widget) + container_layout.setContentsMargins(0, 0, 0, 0) + + # 创建一个占位 Widget,用于嵌入外部进程窗口 + embed_widget = QWidget() + embed_widget.setMinimumSize(400, 300) + container_layout.addWidget(embed_widget) + + browser_dock.setWidget(container_widget) + + # 添加到主窗口 + print("📍 将浏览器视图添加到主窗口") + main_window.addDockWidget(Qt.RightDockWidgetArea, browser_dock) + + # 获取嵌入窗口的 ID + embed_window_id = int(embed_widget.winId()) + print(f"🔑 嵌入窗口 ID: {embed_window_id}") + + # 启动独立进程 + print("🚀 启动独立 WebEngine 进程...") + #web_process_script = os.path.join(os.path.dirname(__file__), "web_browser_process.py") + import tempfile + + temp_dir = tempfile.gettempdir() + web_process_script = os.path.join(temp_dir, "web_browser_process_temp.py") + + with open(web_process_script, "w", encoding="utf-8") as f: + f.write(WEB_BROWSER_PROCESS_CODE) + + # ----------------------------------------------------- + # 创建 QProcess(注意:必须在 start 之前) + # ----------------------------------------------------- + # ----------------------------------------------------- + # 创建 QProcess(注意:必须在 start 之前) + # ----------------------------------------------------- + process = QProcess(browser_dock) + browser_dock.web_process = process # 保存进程引用 + + # ----------------------------------------------------- + # 输出信号 + # ----------------------------------------------------- + def on_process_output(): + output = bytes(process.readAllStandardOutput()).decode('utf-8', errors='ignore') + if output.strip(): + print(f"[WebProcess] {output.strip()}") + + def on_process_error(): + error = bytes(process.readAllStandardError()).decode('utf-8', errors='ignore') + if error.strip(): + print(f"[WebProcess Error] {error.strip()}") + + def on_process_finished(exit_code, exit_status): + print(f"⚠️ WebEngine 进程退出: {exit_code} {exit_status}") + + process.readyReadStandardOutput.connect(on_process_output) + process.readyReadStandardError.connect(on_process_error) + process.finished.connect(on_process_finished) + + # ----------------------------------------------------- + # 写入临时脚本 + # ----------------------------------------------------- + import tempfile + temp_dir = tempfile.gettempdir() + web_process_script = os.path.join(temp_dir, "web_browser_process_temp.py") + + with open(web_process_script, "w", encoding="utf-8") as f: + f.write(WEB_BROWSER_PROCESS_CODE) + + # ----------------------------------------------------- + # 启动子进程(这里顺序不能错) + # ----------------------------------------------------- + python_exe = sys.executable + url = "https://baidu.com" # 更可控的测试站点 + + print(f"🚀 启动: {python_exe} {web_process_script} {url} {embed_window_id}") + process.start(python_exe, [web_process_script, url, str(embed_window_id)]) + + if not process.waitForStarted(3000): + print("✗ 启动 WebEngine 失败") + return None + + print("✓ WebEngine 进程已启动") + + # 添加到GUI元素列表以便管理 + self.world.gui_elements.append(browser_dock) + + # 添加清理函数 + def cleanup_process(): + if hasattr(browser_dock, 'web_process') and browser_dock.web_process: + print("🧹 清理 WebEngine 进程...") + browser_dock.web_process.terminate() + if not browser_dock.web_process.waitForFinished(2000): + browser_dock.web_process.kill() + + browser_dock.destroyed.connect(cleanup_process) + + print("✓ 网页浏览器视图已创建并集成到项目中(独立进程模式)") + return browser_dock + + except Exception as e: + print(f"✗ 创建浏览器视图失败: {str(e)}") + import traceback + traceback.print_exc() + UniversalMessageDialog.show_error( + self, "错误", + f"创建浏览器视图失败:\n{str(e)}", + False, "确定" + ) + return None def openWebBrowser(self): if not WEB_ENGINE_AVAILABLE: return None try: - #from PyQt5.QtWebEngineWidgets import QWebEngineView + # from PyQt5.QtWebEngineWidgets import QWebEngineView from PyQt5.QtWidgets import QDockWidget from PyQt5.QtCore import QUrl import os @@ -6348,7 +6695,17 @@ def setup_main_window(world,path = None): QCoreApplication.setAttribute(Qt.AA_ShareOpenGLContexts) if sys.platform == 'win32': QCoreApplication.setAttribute(Qt.AA_UseDesktopOpenGL) + + # 添加 Chromium 参数禁用硬件加速,避免与 Panda3D 的 OpenGL 冲突 + sys.argv.extend([ + '--disable-gpu', + '--disable-software-rasterizer', + '--disable-gpu-compositing', + '--disable-accelerated-2d-canvas', + '--num-raster-threads=1' + ]) app = QApplication(sys.argv) + print("✓ QApplication 已创建,QtWebEngine 硬件加速已禁用") main_window = MainWindow(world) main_window.show()