EG/gui/gui_manager.py
2025-07-24 11:37:46 +08:00

953 lines
36 KiB
Python
Raw 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.

"""
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)}")