from PyQt5 import QtWidgets, QtGui from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * from direct.task.TaskManagerGlobal import taskMgr from QMeta3D.QMeta3D_Buttons_Translation import QMeta3D_Button_translation from QMeta3D.QMeta3D_Keys_Translation import QMeta3D_Key_translation from QMeta3D.QMeta3D_Modifiers_Translation import QMeta3D_Modifier_translation __all__ = ["QMeta3DWidget"] class QMeta3DSynchronizer(QTimer): def __init__(self, qMeta3DWidget, FPS=60): QTimer.__init__(self) self.qMeta3DWidget = qMeta3DWidget dt = 1000 / FPS self.setInterval(int(dt)) self.timeout.connect(self.tick) def tick(self): try: # 在渲染前清理可能损坏的TransformState对象 from panda3d.core import TransformState TransformState.clear_cache() taskMgr.step() self.qMeta3DWidget.update() except AssertionError as e: # 专门处理 TransformState has_mat() 断言错误 if "has_mat" in str(e): print(f"警告: 检测到TransformState断言错误,已静默处理: {e}") # 尝试恢复渲染状态 try: # 强制清理缓存并重试 from panda3d.core import TransformState, RenderState TransformState.clear_cache() RenderState.clear_cache() taskMgr.step() self.qMeta3DWidget.update() except: pass else: # 重新抛出其他断言错误 raise except Exception as e: # 静默处理其他所有异常 print(f"警告: 检测到异常,已静默处理: {e}") def get_panda_key_modifiers(evt): panda_mods = [] qt_mods = evt.modifiers() for qt_mod, panda_mod in QMeta3D_Modifier_translation.items(): if (qt_mods & qt_mod) == qt_mod: panda_mods.append(panda_mod) return panda_mods def get_panda_key_modifiers_prefix(evt): # join all modifiers (except NoModifier, which is None) with '-' prefix = "-".join([mod for mod in get_panda_key_modifiers(evt) if mod is not None]) # if the prefix is not empty, append a '-' if prefix: prefix += '-' return prefix class QMeta3DWidget(QWidget): def __init__(self, Meta3DWorld, parent=None, FPS=60, debug=False): QWidget.__init__(self, parent) self.rp_sync_requested = False # set fixed geometry self.Meta3DWorld = Meta3DWorld self.Meta3DWorld.set_parent(self) # Setup a timer in Qt that runs taskMgr.step() to simulate Panda's own main loop # pandaTimer = QTimer(self) # pandaTimer.timeout.connect() # pandaTimer.start(0) self.setFocusPolicy(Qt.StrongFocus) # Setup another timer that redraws this widget in a specific FPS # redrawTimer = QTimer(self) # redrawTimer.timeout.connect(self.update) # redrawTimer.start(1000/FPS) self.paintSurface = QPainter() self.rotate = QTransform() self.rotate.rotate(180) self.out_image = QImage() size = self.Meta3DWorld.cam.node().get_lens().get_film_size() self.initial_film_size = QSizeF(size.x, size.y) self.initial_size = self.size() self.synchronizer = QMeta3DSynchronizer(self, FPS) self.synchronizer.start() self.debug = debug def mousePressEvent(self, evt): button = evt.button() try: b = "{}{}".format(get_panda_key_modifiers_prefix(evt), QMeta3D_Button_translation[button]) if self.debug: print(b) messenger.send(b,[{"x":evt.x(),"y":evt.y()}]) except: print("Unimplemented button. Please send an issue on github to fix this problem") def mouseMoveEvent(self, evt:QtGui.QMouseEvent): button = evt.button() try: b = "mouse-move" if self.debug: print(b) messenger.send(b,[{"x":evt.x(),"y":evt.y()}]) except: print("Unimplemented button. Please send an issue on github to fix this problem") def mouseReleaseEvent(self, evt): button = evt.button() try: b = "{}{}-up".format(get_panda_key_modifiers_prefix(evt), QMeta3D_Button_translation[button]) if self.debug: print(b) messenger.send(b,[{"x":evt.x(),"y":evt.y()}]) except: print("Unimplemented button. Please send an issue on github to fix this problem") def wheelEvent(self, evt): delta = evt.angleDelta().y() try: if self.debug: print(f"wheel {delta}") messenger.send('wheel',[{"delta":delta}]) except: print("Unimplemented button. Please send an issue on github to fix this problem") def keyPressEvent(self, evt): key = evt.key() try: k = "{}{}".format(get_panda_key_modifiers_prefix(evt), QMeta3D_Key_translation[key]) if self.debug: print(k) messenger.send(k) except: print("Unimplemented key. Please send an issue on github to fix this problem") def keyReleaseEvent(self, evt): key = evt.key() try: k = "{}{}-up".format(get_panda_key_modifiers_prefix(evt), QMeta3D_Key_translation[key]) if self.debug: print(k) messenger.send(k) except: print("Unimplemented key. Please send an issue on github to fix this problem") def resizeEvent(self, evt): width = evt.size().width() height = evt.size().height() #print(f"width:{width}") #print(f"height:{height}") from Meta3DWorld import resize_buffer lens = self.Meta3DWorld.cam.node().get_lens() def minimumSizeHint(self): return QSize(400, 300) def paintEvent(self, event): # 正常窗口渲染模式下,Panda3D 会直接渲染到窗口 # 不需要从纹理复制数据 # 只需要确保渲染循环继续运行 # 触发一次渲染更新 if hasattr(self.Meta3DWorld, 'win') and self.Meta3DWorld.win: # 确保窗口大小匹配 width = self.width() height = self.height() # 如果窗口大小不匹配,更新窗口属性 if (self.Meta3DWorld.win.get_x_size() != width or self.Meta3DWorld.win.get_y_size() != height): from panda3d.core import WindowProperties props = WindowProperties() props.setSize(width, height) self.Meta3DWorld.win.request_properties(props) # 继续下一次更新 self.update() def sync_Meta3d_window_size(self, width, height): try: from panda3d.core import WindowProperties # 更新窗口属性 props = WindowProperties() props.setSize(width, height) # 对于正常窗口渲染,直接更新窗口属性 if self.Meta3DWorld.win: self.Meta3DWorld.win.request_properties(props) # 如果使用 RenderPipeline,更新相关设置 if hasattr(self.Meta3DWorld, 'render_pipeline'): try: from RenderPipelineFile.rpcore.globals import Globals from panda3d.core import LVecBase2i # 更新全局分辨率 Globals.native_resolution = LVecBase2i(width, height) # 重新计算渲染分辨率 self.Meta3DWorld.render_pipeline._compute_render_resolution() # 通知各个管理器处理尺寸变化 self.Meta3DWorld.render_pipeline.light_mgr.compute_tile_size() self.Meta3DWorld.render_pipeline.stage_mgr.handle_window_resize() if hasattr(self.Meta3DWorld.render_pipeline, 'debugger'): self.Meta3DWorld.render_pipeline.debugger.handle_window_resize() # 触发插件的窗口尺寸变化钩子 self.Meta3DWorld.render_pipeline.plugin_mgr.trigger_hook("window_resized") except Exception as rp_e: print(f"RenderPipeline 尺寸更新失败: {rp_e}") except Exception as e: print(f"同步 Meta3D 窗口尺寸失败: {str(e)}")