""" GUI元素管理模块 负责2D/3D GUI元素的管理: - GUI元素的创建(按钮、标签、输入框等) - GUI编辑模式 - GUI属性编辑 - GUI元素的复制和删除 """ from direct.gui.DirectGui import * from panda3d.core import * from PyQt5.QtWidgets import (QDialog, QVBoxLayout, QFormLayout, QLineEdit, QDoubleSpinBox, QPushButton, QDialogButtonBox, QColorDialog, QLabel, QWidget, QGroupBox, QHBoxLayout) from PyQt5.QtGui import QColor from PyQt5.QtCore import Qt class GUIManager: """GUI元素管理系统类""" def __init__(self, world): """初始化GUI管理系统 Args: world: 核心世界对象引用 """ self.world = world # GUI元素列表 self.gui_elements = [] #光源列表 self.light_elements = [] # GUI编辑模式状态 self.guiEditMode = False self.guiEditPanel = None self.guiPreviewWindow = None self.currentGUITool = None print("✓ GUI管理系统初始化完成") # ==================== GUI元素创建方法 ==================== def createGUIButton(self, pos=(0, 0, 0), text="按钮", size=0.1): """创建2D GUI按钮""" from direct.gui.DirectGui import DirectButton # 将3D坐标转换为2D屏幕坐标 gui_pos = (pos[0] * 0.1, 0, pos[2] * 0.1) button = DirectButton( text=text, pos=gui_pos, scale=size, command=self.onGUIButtonClick, extraArgs=[f"button_{len(self.gui_elements)}"], frameColor=(0.2, 0.6, 0.8, 1), text_font=self.world.getChineseFont() if self.world.getChineseFont() else None, rolloverSound=None, clickSound=None ) # 为GUI元素添加标识 button.setTag("gui_type", "button") button.setTag("gui_id", f"button_{len(self.gui_elements)}") button.setTag("gui_text", text) button.setTag("is_gui_element", "1") self.gui_elements.append(button) # 安全地调用updateSceneTree if hasattr(self.world, 'updateSceneTree'): self.world.updateSceneTree() print(f"✓ 创建GUI按钮: {text} (逻辑位置: {pos}, 屏幕位置: {gui_pos})") return button def createGUILabel(self, pos=(0, 0, 0), text="标签", size=0.08): """创建2D GUI标签""" from direct.gui.DirectGui import DirectLabel # 将3D坐标转换为2D屏幕坐标 gui_pos = (pos[0] * 0.1, 0, pos[2] * 0.1) label = DirectLabel( text=text, pos=gui_pos, scale=size, frameColor=(0, 0, 0, 0), # 透明背景 text_fg=(1, 1, 1, 1), text_font=self.world.getChineseFont() if self.world.getChineseFont() else None ) # 为GUI元素添加标识 label.setTag("gui_type", "label") label.setTag("gui_id", f"label_{len(self.gui_elements)}") label.setTag("gui_text", text) label.setTag("is_gui_element", "1") self.gui_elements.append(label) # 安全地调用updateSceneTree if hasattr(self.world, 'updateSceneTree'): self.world.updateSceneTree() print(f"✓ 创建GUI标签: {text} (逻辑位置: {pos}, 屏幕位置: {gui_pos})") return label def createGUIEntry(self, pos=(0, 0, 0), placeholder="输入文本...", size=0.08): """创建2D GUI文本输入框""" from direct.gui.DirectGui import DirectEntry # 将3D坐标转换为2D屏幕坐标 gui_pos = (pos[0] * 0.1, 0, pos[2] * 0.1) entry = DirectEntry( text="", pos=gui_pos, scale=size, command=self.onGUIEntrySubmit, extraArgs=[f"entry_{len(self.gui_elements)}"], initialText=placeholder, numLines=1, width=12, focus=0 ) # 为GUI元素添加标识 entry.setTag("gui_type", "entry") entry.setTag("gui_id", f"entry_{len(self.gui_elements)}") entry.setTag("gui_placeholder", placeholder) entry.setTag("is_gui_element", "1") self.gui_elements.append(entry) # 安全地调用updateSceneTree if hasattr(self.world, 'updateSceneTree'): self.world.updateSceneTree() print(f"✓ 创建GUI输入框: {placeholder} (逻辑位置: {pos}, 屏幕位置: {gui_pos})") return entry def createGUI3DText(self, pos=(0, 0, 0), text="3D文本", size=0.5): """创建3D空间文本""" from panda3d.core import TextNode textNode = TextNode(f'3d-text-{len(self.gui_elements)}') textNode.setText(text) textNode.setAlign(TextNode.ACenter) if self.world.getChineseFont(): textNode.setFont(self.world.getChineseFont()) textNodePath = self.world.render.attachNewNode(textNode) textNodePath.setPos(*pos) textNodePath.setScale(size) textNodePath.setColor(1, 1, 0, 1) textNodePath.setBillboardAxis() # 让文本总是面向相机 # 为GUI元素添加标识 textNodePath.setTag("gui_type", "3d_text") textNodePath.setTag("gui_id", f"3d_text_{len(self.gui_elements)}") textNodePath.setTag("gui_text", text) textNodePath.setTag("is_gui_element", "1") self.gui_elements.append(textNodePath) # 安全地调用updateSceneTree if hasattr(self.world, 'updateSceneTree'): self.world.updateSceneTree() print(f"✓ 创建3D文本: {text} (世界位置: {pos})") return textNodePath def createGUIVirtualScreen(self, pos=(0, 0, 0), size=(2, 1), text="虚拟屏幕"): """创建3D虚拟屏幕""" from panda3d.core import CardMaker, TransparencyAttrib, TextNode # 创建虚拟屏幕 cm = CardMaker(f"virtual-screen-{len(self.gui_elements)}") cm.setFrame(-size[0]/2, size[0]/2, -size[1]/2, size[1]/2) virtualScreen = self.world.render.attachNewNode(cm.generate()) virtualScreen.setPos(*pos) virtualScreen.setColor(0.2, 0.2, 0.2, 0.8) virtualScreen.setTransparency(TransparencyAttrib.MAlpha) # 在虚拟屏幕上添加文本 screenText = TextNode(f'screen-text-{len(self.gui_elements)}') screenText.setText(text) screenText.setAlign(TextNode.ACenter) if self.world.getChineseFont(): screenText.setFont(self.world.getChineseFont()) screenTextNP = virtualScreen.attachNewNode(screenText) screenTextNP.setPos(0, 0.01, 0) screenTextNP.setScale(0.3) screenTextNP.setColor(0, 1, 0, 1) # 为GUI元素添加标识 virtualScreen.setTag("gui_type", "virtual_screen") virtualScreen.setTag("gui_id", f"virtual_screen_{len(self.gui_elements)}") virtualScreen.setTag("gui_text", text) virtualScreen.setTag("is_gui_element", "1") self.gui_elements.append(virtualScreen) # 安全地调用updateSceneTree if hasattr(self.world, 'updateSceneTree'): self.world.updateSceneTree() print(f"✓ 创建虚拟屏幕: {text} (世界位置: {pos})") return virtualScreen def createGUISlider(self, pos=(0, 0, 0), text="滑块", scale=0.3): """创建2D GUI滑块""" from direct.gui.DirectGui import DirectSlider gui_pos = (pos[0] * 0.1, 0, pos[2] * 0.1) slider = DirectSlider( pos=gui_pos, scale=scale, range=(0, 100), value=50, frameColor=(0.6, 0.6, 0.6, 1), thumbColor=(0.2, 0.8, 0.2, 1) ) slider.setTag("gui_type", "slider") slider.setTag("gui_id", f"slider_{len(self.gui_elements)}") slider.setTag("gui_text", text) slider.setTag("is_gui_element", "1") self.gui_elements.append(slider) # 安全地调用updateSceneTree if hasattr(self.world, 'updateSceneTree'): self.world.updateSceneTree() print(f"✓ 创建GUI滑块: {text} (逻辑位置: {pos}, 屏幕位置: {gui_pos})") return slider # ==================== GUI元素管理方法 ==================== def deleteGUIElement(self, gui_element): """删除GUI元素""" try: if gui_element in self.gui_elements: # 移除GUI元素 if hasattr(gui_element, 'removeNode'): gui_element.removeNode() elif hasattr(gui_element, 'destroy'): gui_element.destroy() # 从列表中移除 self.gui_elements.remove(gui_element) # 更新场景树 # 安全地调用updateSceneTree if hasattr(self.world, 'updateSceneTree'): self.world.updateSceneTree() print(f"删除GUI元素: {gui_element}") return True except Exception as e: print(f"删除GUI元素失败: {str(e)}") return False def editGUIElement(self, gui_element, property_name, value): """编辑GUI元素属性""" try: from panda3d.core import TextNode gui_type = gui_element.getTag("gui_type") if hasattr(gui_element, 'getTag') else "unknown" print(f"开始编辑GUI元素: 类型={gui_type}, 属性={property_name}, 值={value}") if property_name == "text": if gui_type in ["button", "label"]: gui_element['text'] = value print(f"成功更新2D GUI文本: {value}") elif gui_type == "entry": gui_element.set(value) print(f"成功更新输入框文本: {value}") elif gui_type == "3d_text": # 对于3D文本,直接修改自身的TextNode if isinstance(gui_element.node(), TextNode): gui_element.node().setText(value) print(f"成功更新3D文本: {value}") else: print(f"警告: {gui_type}节点类型为{type(gui_element.node())},不是TextNode类型") elif gui_type == "virtual_screen": # 对于虚拟屏幕,需要找到TextNode子节点 print(f"虚拟屏幕有 {gui_element.getNumChildren()} 个子节点") text_found = False for i, child in enumerate(gui_element.getChildren()): print(f"子节点 {i}: {child.getName()}, 类型: {type(child.node())}") if isinstance(child.node(), TextNode): child.node().setText(value) text_found = True print(f"成功更新虚拟屏幕文本: {value}") break if not text_found: print(f"警告: 在{gui_type}中未找到TextNode子节点") gui_element.setTag("gui_text", value) elif property_name == "position": if isinstance(value, (list, tuple)) and len(value) >= 3: gui_element.setPos(*value[:3]) elif property_name == "scale": if isinstance(value, (int, float)): gui_element.setScale(value) elif isinstance(value, (list, tuple)) and len(value) >= 3: gui_element.setScale(*value[:3]) elif property_name == "color": if isinstance(value, (list, tuple)) and len(value) >= 3: if gui_type in ["button", "label"]: gui_element['frameColor'] = value else: gui_element.setColor(*value) print(f"编辑GUI元素 {gui_type}: {property_name} = {value}") return True except Exception as e: print(f"编辑GUI元素失败: {str(e)}") return False def duplicateGUIElement(self, gui_element): """复制GUI元素""" try: gui_type = gui_element.getTag("gui_type") gui_text = gui_element.getTag("gui_text") # 获取当前位置并偏移 pos = gui_element.getPos() new_pos = (pos.getX() + 0.2, pos.getY(), pos.getZ() + 0.2) # 根据类型创建新的GUI元素 if gui_type == "button": self.createGUIButton(new_pos, gui_text + "_副本") elif gui_type == "label": self.createGUILabel(new_pos, gui_text + "_副本") elif gui_type == "entry": self.createGUIEntry(new_pos, gui_text + "_副本") elif gui_type == "3d_text": self.createGUI3DText(new_pos, gui_text + "_副本") elif gui_type == "virtual_screen": self.createGUIVirtualScreen(new_pos, text=gui_text + "_副本") print(f"复制GUI元素: {gui_type} - {gui_text}") except Exception as e: print(f"复制GUI元素失败: {str(e)}") def editGUIElementDialog(self, gui_element): """显示GUI元素编辑对话框""" dialog = QDialog() dialog.setWindowTitle("编辑GUI元素") dialog.setMinimumWidth(300) layout = QVBoxLayout(dialog) form = QFormLayout() gui_type = gui_element.getTag("gui_type") gui_text = gui_element.getTag("gui_text") # 文本编辑 if gui_type in ["button", "label", "entry", "3d_text", "virtual_screen"]: textEdit = QLineEdit(gui_text or "") form.addRow("文本:", textEdit) # 位置编辑 if hasattr(gui_element, 'getPos'): pos = gui_element.getPos() xEdit = QDoubleSpinBox() xEdit.setRange(-1000, 1000) xEdit.setValue(pos.getX()) form.addRow("位置 X:", xEdit) yEdit = QDoubleSpinBox() yEdit.setRange(-1000, 1000) yEdit.setValue(pos.getY()) form.addRow("位置 Y:", yEdit) zEdit = QDoubleSpinBox() zEdit.setRange(-1000, 1000) zEdit.setValue(pos.getZ()) form.addRow("位置 Z:", zEdit) # 缩放编辑 if hasattr(gui_element, 'getScale'): scale = gui_element.getScale() scaleEdit = QDoubleSpinBox() scaleEdit.setRange(0.01, 10) scaleEdit.setSingleStep(0.1) scaleEdit.setValue(scale.getX()) form.addRow("缩放:", scaleEdit) layout.addLayout(form) # 按钮 buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) buttonBox.accepted.connect(dialog.accept) buttonBox.rejected.connect(dialog.reject) layout.addWidget(buttonBox) # 执行对话框 if dialog.exec_() == QDialog.Accepted: try: # 应用更改 if gui_type in ["button", "label", "entry", "3d_text", "virtual_screen"]: self.editGUIElement(gui_element, "text", textEdit.text()) if hasattr(gui_element, 'getPos'): self.editGUIElement(gui_element, "position", [xEdit.value(), yEdit.value(), zEdit.value()]) if hasattr(gui_element, 'getScale'): self.editGUIElement(gui_element, "scale", scaleEdit.value()) # 更新属性面板 if self.world.treeWidget: currentItem = self.world.treeWidget.currentItem() if currentItem: self.world.updatePropertyPanel(currentItem) print("GUI元素编辑完成") except Exception as e: print(f"应用GUI编辑失败: {str(e)}") # ==================== GUI事件处理方法 ==================== def onGUIButtonClick(self, button_id): """GUI按钮点击事件处理""" print(f"GUI按钮被点击: {button_id}") def onGUIEntrySubmit(self, text, entry_id): """GUI输入框提交事件处理""" print(f"GUI输入框提交: {entry_id} = {text}") # ==================== GUI编辑模式方法 ==================== def toggleGUIEditMode(self): """切换GUI编辑模式""" self.guiEditMode = not self.guiEditMode if self.guiEditMode: self.enterGUIEditMode() else: self.exitGUIEditMode() def enterGUIEditMode(self): """进入GUI编辑模式""" print("\n=== 进入GUI编辑模式 ===") # 打开GUI预览窗口 self.openGUIPreviewWindow() # 创建GUI编辑面板 self.createGUIEditPanel() # 改变当前工具为GUI选择工具 self.world.currentTool = "GUI编辑" print("GUI编辑模式已激活") print("- 使用右侧工具栏创建GUI元素") print("- 在独立预览窗口中查看效果") print("- 左键点击现有GUI元素选择和编辑") def exitGUIEditMode(self): """退出GUI编辑模式""" print("\n=== 退出GUI编辑模式 ===") # 关闭GUI预览窗口 self.closeGUIPreviewWindow() # 移除GUI编辑面板 if self.guiEditPanel: self.guiEditPanel.destroy() self.guiEditPanel = None # 恢复普通工具 self.world.currentTool = "选择" print("GUI编辑模式已关闭") def createGUIEditPanel(self): """创建GUI编辑面板""" from direct.gui.DirectGui import DirectFrame, DirectButton, DirectLabel # 创建主面板 self.guiEditPanel = DirectFrame( pos=(0.85, 0, 0), frameSize=(-0.15, 0.15, -0.9, 0.9), frameColor=(0.1, 0.1, 0.1, 0.8), text="GUI编辑器", text_pos=(0, 0.85), text_scale=0.05, text_fg=(1, 1, 1, 1), text_font=self.world.getChineseFont() if self.world.getChineseFont() else None ) # 创建工具按钮 y_pos = 0.7 spacing = 0.12 # 2D GUI工具 label_2d = DirectLabel( parent=self.guiEditPanel, text="2D GUI", pos=(0, 0, y_pos), scale=0.04, text_fg=(1, 1, 0, 1), frameColor=(0, 0, 0, 0), text_font=self.world.getChineseFont() if self.world.getChineseFont() else None ) y_pos -= 0.08 # 按钮工具 btn_button = DirectButton( parent=self.guiEditPanel, text="按钮", pos=(0, 0, y_pos), scale=0.04, command=self.setGUICreateTool, extraArgs=["button"], frameColor=(0.2, 0.6, 0.8, 1), text_font=self.world.getChineseFont() if self.world.getChineseFont() else None ) y_pos -= spacing # 标签工具 btn_label = DirectButton( parent=self.guiEditPanel, text="标签", pos=(0, 0, y_pos), scale=0.04, command=self.setGUICreateTool, extraArgs=["label"], frameColor=(0.6, 0.8, 0.2, 1), text_font=self.world.getChineseFont() if self.world.getChineseFont() else None ) y_pos -= spacing # 输入框工具 btn_entry = DirectButton( parent=self.guiEditPanel, text="输入框", pos=(0, 0, y_pos), scale=0.04, command=self.setGUICreateTool, extraArgs=["entry"], frameColor=(0.8, 0.6, 0.2, 1), text_font=self.world.getChineseFont() if self.world.getChineseFont() else None ) y_pos -= spacing # 3D GUI工具 label_3d = DirectLabel( parent=self.guiEditPanel, text="3D GUI", pos=(0, 0, y_pos), scale=0.04, text_fg=(1, 1, 0, 1), frameColor=(0, 0, 0, 0), text_font=self.world.getChineseFont() if self.world.getChineseFont() else None ) y_pos -= 0.08 # 3D文本工具 btn_3dtext = DirectButton( parent=self.guiEditPanel, text="3D文本", pos=(0, 0, y_pos), scale=0.04, command=self.setGUICreateTool, extraArgs=["3d_text"], frameColor=(0.8, 0.2, 0.6, 1), text_font=self.world.getChineseFont() if self.world.getChineseFont() else None ) y_pos -= spacing # 虚拟屏幕工具 btn_screen = DirectButton( parent=self.guiEditPanel, text="虚拟屏幕", pos=(0, 0, y_pos), scale=0.04, command=self.setGUICreateTool, extraArgs=["virtual_screen"], frameColor=(0.6, 0.2, 0.8, 1), text_font=self.world.getChineseFont() if self.world.getChineseFont() else None ) y_pos -= spacing # 分隔线 y_pos -= 0.1 # 操作按钮 label_ops = DirectLabel( parent=self.guiEditPanel, text="操作", pos=(0, 0, y_pos), scale=0.04, text_fg=(1, 1, 0, 1), frameColor=(0, 0, 0, 0), text_font=self.world.getChineseFont() if self.world.getChineseFont() else None ) y_pos -= 0.08 # 删除工具 btn_delete = DirectButton( parent=self.guiEditPanel, text="删除", pos=(0, 0, y_pos), scale=0.04, command=self.deleteSelectedGUI, frameColor=(0.8, 0.2, 0.2, 1), text_font=self.world.getChineseFont() if self.world.getChineseFont() else None ) y_pos -= spacing # 复制工具 btn_copy = DirectButton( parent=self.guiEditPanel, text="复制", pos=(0, 0, y_pos), scale=0.04, command=self.copySelectedGUI, frameColor=(0.2, 0.8, 0.2, 1), text_font=self.world.getChineseFont() if self.world.getChineseFont() else None ) y_pos -= spacing # 退出GUI编辑模式 btn_exit = DirectButton( parent=self.guiEditPanel, text="退出", pos=(0, 0, -0.8), scale=0.04, command=self.toggleGUIEditMode, frameColor=(0.5, 0.5, 0.5, 1), text_font=self.world.getChineseFont() if self.world.getChineseFont() else None ) # 存储当前的GUI创建工具 self.currentGUITool = None def openGUIPreviewWindow(self): """打开独立的GUI预览窗口""" try: from gui_preview_window import GUIPreviewWindow self.guiPreviewWindow = GUIPreviewWindow() self.guiPreviewWindow.set_main_world(self.world) print("✓ GUI预览窗口已打开") print("这个独立窗口会实时显示您创建的GUI元素") except ImportError: print("错误: 无法导入GUI预览窗口模块") except Exception as e: print(f"打开GUI预览窗口失败: {str(e)}") def closeGUIPreviewWindow(self): """关闭GUI预览窗口""" if self.guiPreviewWindow: self.guiPreviewWindow.destroy() self.guiPreviewWindow = None print("GUI预览窗口已关闭") # ==================== GUI工具和选择方法 ==================== def setGUICreateTool(self, tool_type): """设置GUI创建工具""" self.currentGUITool = tool_type print(f"选择GUI创建工具: {tool_type}") def deleteSelectedGUI(self): """删除选中的GUI元素""" if self.world.selection.selectedNode and hasattr(self.world.selection.selectedNode, 'getTag'): gui_type = self.world.selection.selectedNode.getTag("gui_type") if gui_type: success = self.deleteGUIElement(self.world.selection.selectedNode) if success: self.world.selection.updateSelection(None) print("GUI元素已删除") else: print("删除GUI元素失败") else: print("选中的不是GUI元素") else: print("没有选中的GUI元素") def copySelectedGUI(self): """复制选中的GUI元素""" if self.world.selection.selectedNode and hasattr(self.world.selection.selectedNode, 'getTag'): gui_type = self.world.selection.selectedNode.getTag("gui_type") if gui_type: self.duplicateGUIElement(self.world.selection.selectedNode) print("GUI元素已复制") else: print("选中的不是GUI元素") else: print("没有选中的GUI元素") def handleGUIEditClick(self, hitPos): """处理GUI编辑模式下的点击""" if not self.guiEditMode: return False if self.currentGUITool: # 创建新的GUI元素 self.createGUIAtPosition(hitPos, self.currentGUITool) return True return False def createGUIAtPosition(self, world_pos, gui_type): """在指定位置创建GUI元素""" print(f"在位置 {world_pos} 创建 {gui_type}") # 根据GUI类型选择合适的坐标转换 if gui_type in ["button", "label", "entry"]: # 2D GUI - 将世界坐标转换为屏幕逻辑坐标 screen_x = world_pos.getX() * 2 # 缩放因子 screen_z = world_pos.getZ() * 2 pos = (screen_x, 0, screen_z) else: # 3D GUI - 直接使用世界坐标 pos = (world_pos.getX(), world_pos.getY(), world_pos.getZ()) # 创建不同类型的GUI元素 if gui_type == "button": element = self.createGUIButton(pos, f"按钮_{len(self.gui_elements)}") elif gui_type == "label": element = self.createGUILabel(pos, f"标签_{len(self.gui_elements)}") elif gui_type == "entry": element = self.createGUIEntry(pos, f"输入框_{len(self.gui_elements)}") elif gui_type == "3d_text": element = self.createGUI3DText(pos, f"3D文本_{len(self.gui_elements)}") elif gui_type == "virtual_screen": element = self.createGUIVirtualScreen(pos, text=f"屏幕_{len(self.gui_elements)}") else: print(f"未知的GUI类型: {gui_type}") return # 自动选中新创建的元素 self.world.selection.updateSelection(element) self.selectGUIInTree(element) print(f"创建并选中了新的{gui_type}元素") def findClickedGUI(self, hitNode): """查找被点击的GUI元素""" # 检查点击的节点是否是GUI元素 current = hitNode while current != self.world.render: if hasattr(current, 'getTag') and current.getTag("gui_type"): return current current = current.getParent() return None def selectGUIInTree(self, gui_element): """在树形控件中选中GUI元素""" if not self.world.treeWidget or not gui_element: return def findGUIItem(item): """递归查找GUI元素对应的树形项""" if item.data(0, Qt.UserRole) == gui_element: return item for i in range(item.childCount()): child = item.child(i) result = findGUIItem(child) if result: return result return None # 从根开始查找 root = self.world.treeWidget.invisibleRootItem() for i in range(root.childCount()): sceneItem = root.child(i) if sceneItem.text(0) == "场景": for j in range(sceneItem.childCount()): childItem = sceneItem.child(j) if childItem.text(0) == "GUI元素": foundItem = findGUIItem(childItem) if foundItem: self.world.treeWidget.setCurrentItem(foundItem) self.world.updatePropertyPanel(foundItem) return def updateGUISelection(self, gui_element): """更新GUI元素选择状态""" self.world.selection.updateSelection(gui_element) if gui_element and hasattr(gui_element, 'getTag'): gui_type = gui_element.getTag("gui_type") gui_text = gui_element.getTag("gui_text") print(f"选中GUI元素: {gui_type} - {gui_text}") # 在树形控件中选中 self.selectGUIInTree(gui_element) # ==================== GUI属性面板方法 ==================== def updateGUIPropertyPanel(self, gui_element, layout): """更新GUI元素属性面板""" gui_type = gui_element.getTag("gui_type") gui_text = gui_element.getTag("gui_text") # GUI类型显示 typeLabel = QLabel("GUI类型:") typeValue = QLabel(gui_type) typeValue.setStyleSheet("color: #00AAFF; font-weight: bold;") layout.addRow(typeLabel, typeValue) # 文本属性(如果适用) if gui_type in ["button", "label", "entry", "3d_text", "virtual_screen"]: textLabel = QLabel("文本:") textEdit = QLineEdit(gui_text or "") # 创建一个更新函数来处理文本变化 def updateText(text): success = self.editGUIElement(gui_element, "text", text) if success: # 更新场景树显示的名称 # 安全地调用updateSceneTree if hasattr(self.world, 'updateSceneTree'): self.world.updateSceneTree() textEdit.textChanged.connect(updateText) layout.addRow(textLabel, textEdit) # 位置属性 if hasattr(gui_element, 'getPos'): pos = gui_element.getPos() # 根据GUI类型决定位置编辑方式 if gui_type in ["button", "label", "entry"]: # 2D GUI组件使用屏幕坐标 logical_x = pos.getX() / 0.1 # 反向转换为逻辑坐标 logical_z = pos.getZ() / 0.1 xPos = QDoubleSpinBox() xPos.setRange(-50, 50) xPos.setValue(logical_x) xPos.valueChanged.connect(lambda v: self.editGUI2DPosition(gui_element, "x", v)) layout.addRow("屏幕位置 X:", xPos) zPos = QDoubleSpinBox() zPos.setRange(-50, 50) zPos.setValue(logical_z) zPos.valueChanged.connect(lambda v: self.editGUI2DPosition(gui_element, "z", v)) layout.addRow("屏幕位置 Z:", zPos) # 显示实际屏幕坐标(只读) actualXLabel = QLabel(f"{pos.getX():.3f}") actualXLabel.setStyleSheet("color: gray; font-size: 10px;") layout.addRow("实际屏幕 X:", actualXLabel) actualZLabel = QLabel(f"{pos.getZ():.3f}") actualZLabel.setStyleSheet("color: gray; font-size: 10px;") layout.addRow("实际屏幕 Z:", actualZLabel) else: # 3D GUI组件使用世界坐标 xPos = QDoubleSpinBox() xPos.setRange(-1000, 1000) xPos.setValue(pos.getX()) xPos.valueChanged.connect(lambda v: self.editGUIElement(gui_element, "position", [v, pos.getY(), pos.getZ()])) layout.addRow("位置 X:", xPos) yPos = QDoubleSpinBox() yPos.setRange(-1000, 1000) yPos.setValue(pos.getY()) yPos.valueChanged.connect(lambda v: self.editGUIElement(gui_element, "position", [pos.getX(), v, pos.getZ()])) layout.addRow("位置 Y:", yPos) zPos = QDoubleSpinBox() zPos.setRange(-1000, 1000) zPos.setValue(pos.getZ()) zPos.valueChanged.connect(lambda v: self.editGUIElement(gui_element, "position", [pos.getX(), pos.getY(), v])) layout.addRow("位置 Z:", zPos) # 缩放属性 if hasattr(gui_element, 'getScale'): scale = gui_element.getScale() scaleSpinBox = QDoubleSpinBox() scaleSpinBox.setRange(0.01, 10) scaleSpinBox.setSingleStep(0.1) scaleSpinBox.setValue(scale.getX()) scaleSpinBox.valueChanged.connect(lambda v: self.editGUIElement(gui_element, "scale", v)) layout.addRow("缩放:", scaleSpinBox) # 颜色属性(针对2D GUI) if gui_type in ["button", "label"]: colorButton = QPushButton("选择颜色") colorButton.clicked.connect(lambda: self.selectGUIColor(gui_element)) layout.addRow("背景颜色:", colorButton) # 3D特有属性 if gui_type in ["3d_text", "virtual_screen"]: # 旋转属性 if hasattr(gui_element, 'getHpr'): hpr = gui_element.getHpr() hRot = QDoubleSpinBox() hRot.setRange(-180, 180) hRot.setValue(hpr.getX()) hRot.valueChanged.connect(lambda v: gui_element.setH(v)) layout.addRow("旋转 H:", hRot) pRot = QDoubleSpinBox() pRot.setRange(-180, 180) pRot.setValue(hpr.getY()) pRot.valueChanged.connect(lambda v: gui_element.setP(v)) layout.addRow("旋转 P:", pRot) rRot = QDoubleSpinBox() rRot.setRange(-180, 180) rRot.setValue(hpr.getZ()) rRot.valueChanged.connect(lambda v: gui_element.setR(v)) layout.addRow("旋转 R:", rRot) def selectGUIColor(self, gui_element): """选择GUI元素颜色""" color = QColorDialog.getColor(QColor(128, 128, 128), None, "选择颜色") if color.isValid(): r, g, b = color.red() / 255.0, color.green() / 255.0, color.blue() / 255.0 self.editGUIElement(gui_element, "color", [r, g, b, 1.0]) def editGUI2DPosition(self, gui_element, axis, value): """编辑2D GUI元素位置""" try: current_pos = gui_element.getPos() if axis == "x": # 将逻辑坐标转换为屏幕坐标 new_screen_x = value * 0.1 gui_element.setPos(new_screen_x, current_pos.getY(), current_pos.getZ()) elif axis == "z": # 将逻辑坐标转换为屏幕坐标 new_screen_z = value * 0.1 gui_element.setPos(current_pos.getX(), current_pos.getY(), new_screen_z) print(f"更新2D GUI位置: {axis}轴 = {value} (屏幕坐标: {gui_element.getPos()})") except Exception as e: print(f"编辑2D GUI位置失败: {str(e)}")