953 lines
36 KiB
Python
953 lines
36 KiB
Python
"""
|
||
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)}") |