1
0
forked from Rowland/EG

1.整合菜单栏(2D图片、3D图片、地形)

This commit is contained in:
陈横 2025-08-29 16:04:25 +08:00
parent c39ebeb345
commit 95395bdb05
6 changed files with 403 additions and 288 deletions

View File

@ -13,6 +13,15 @@ class TerrainManager:
self.terrains = []
# core/terrain_manager.py
def _get_tree_widget(self):
"""安全获取树形控件"""
try:
if (hasattr(self.world, 'interface_manager') and
hasattr(self.world.interface_manager, 'treeWidget')):
return self.world.interface_manager.treeWidget
except AttributeError:
pass
return None
def createTerrainFromHeightMap(self, heightmap_path, scale=(1, 1, 1)):
"""从高度图创建地形"""

View File

@ -272,211 +272,6 @@ class GUIManager:
return None
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 createGUI2DImage(self, pos=(0, 0, 0), image_path=None, size=0.2):
"""创建2D GUI图片"""
from direct.gui.DirectGui import DirectFrame
from panda3d.core import TransparencyAttrib,Texture,CardMaker,CardMaker,Vec3
# 将3D坐标转换为2D屏幕坐标
gui_pos = (pos[0] * 0.1, 0, pos[2] * 0.1)
#使用CardMaker创建一个更可靠的图片框架
cm = CardMaker('gui-2d-image')
cm.setFrame(-size,size,-size,size)
image_node = self.world.aspect2d.attachNewNode(cm.generate())
image_node.setPos(gui_pos)
image_node.setBin('fixed',0)
image_node.setDepthWrite(False)
image_node.setDepthTest(False)
image_node.setColor(1,1,1,1)
# 设置透明度支持
image_node.setTransparency(TransparencyAttrib.MAlpha)
# 如果提供了图像路径,则加载纹理
if image_path:
try:
texture = self.world.loader.loadTexture(image_path)
if texture:
image_node.setTexture(texture,1)
texture.setWrapU(Texture.WM_clamp)
texture.setWrapV(Texture.WM_clamp)
texture.setMinfilter(Texture.FT_linear)
texture.setMagfilter(Texture.FT_linear)
image_node.setColor(1,1,1,1)
else:
print(f"⚠️ 无法加载2D图片纹理: {image_path}")
except Exception as e:
print(f"❌ 加载2D图片纹理失败: {e}")
# 为GUI元素添加标识
image_node.setTag("gui_type", "2d_image")
image_node.setTag("gui_id", f"2d_image_{len(self.gui_elements)}")
image_node.setTag("gui_text", f"2D图片_{len(self.gui_elements)}")
if image_path:
image_node.setTag("image_path", image_path)
image_node.setTag("is_gui_element", "1")
self.gui_elements.append(image_node)
# 安全地调用updateSceneTree
if hasattr(self.world, 'updateSceneTree'):
self.world.updateSceneTree()
print(f"✓ 创建2D GUI图片 (逻辑位置: {pos}, 屏幕位置: {gui_pos})")
return image_node
def constrain2DPosition(self,gui_element,new_x=None,new_z=None):
"""限制2dGUI元素位置在屏幕范围内"""
try:
from panda3d.core import Vec3
bounds = gui_element.getTightBounds()
element_width=0
element_height=0
if bounds:
min_point,max_point = bounds
element_width = (max_point.getX() - min_point.getX())/2
element_height = (max_point.getZ()-min_point.getZ())/2
#获取当前缩放
scale = gui_element.getScale()
if hasattr(scale,'getX'):
scale_x = scale.getX()
scale_z = scale.getZ() if hasattr(scale,'getZ') else scale_x
else:
scale_x = scale_z = scale if isinstance(scale,(int,float)) else 1.0
actual_width = element_width * scale_x
actual_height = element_height * scale_z
screen_width = 1.9
screen_height = 0.9
min_x = -screen_width + actual_width
max_x = screen_width - actual_width
min_z = -screen_height + actual_height
max_z = screen_height - actual_height
#获取当前位置
current_pos = gui_element.getPos()
x = new_x if new_x is not None else current_pos.getX()
z = new_z if new_z is not None else current_pos.getZ()
#应用边界限制
x = max(min_x,min(max_x,x))
z = max(min_z,min(max_z,z))
return Vec3(x,current_pos.getY(),z)
except Exception as e:
print(f"约束2D位置时出错: {e}")
# 出错时返回原始值
current_pos = gui_element.getPos()
x = new_x if new_x is not None else current_pos.getX()
z = new_z if new_z is not None else current_pos.getZ()
return Vec3(x, current_pos.getY(), z)
def createGUI3DText(self, pos=(0, 0, 0), text="3D文本", size=1):
"""创建3D空间文本"""
from panda3d.core import TextNode,Material,Vec4,ColorAttrib,TransparencyAttrib
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())
textNode.setTextColor(Vec4(1,1,1,1))
textNodePath = self.world.render.attachNewNode(textNode)
textNodePath.setPos(*pos)
textNodePath.setScale(size,size,size)
#textNodePath.setBillboardAxis() # 让文本总是面向相机
# 为3D文本创建默认材质
material = Material(f"text-material-{len(self.gui_elements)}")
material.setBaseColor(Vec4(1, 1, 1, 1)) # 白色
material.setDiffuse(Vec4(1, 1, 1, 1))
material.setAmbient(Vec4(0.5, 0.5, 0.5, 1))
material.setSpecular(Vec4(0.1, 0.1, 0.1, 1.0))
material.setShininess(10.0)
#material.setEmission(0,0,0,1)
textNodePath.setMaterial(material, 1)
textNodePath.setTransparency(TransparencyAttrib.MAlpha)
textNodePath.setAttrib(ColorAttrib.makeFlat(Vec4(1, 1, 1, 1)))
textNodePath.setLightOff()
# 为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")
textNodePath.setDepthWrite(True) # 确保深度写入
textNodePath.setDepthTest(True) # 启用深度测试
textNodePath.setBin("fixed", 0) # 设置渲染层级,避免被遮挡
# if hasattr(self, 'render_pipeline') and self.render_pipeline:
# try:
# self.render_pipeline.set_effect(
# textNodePath,
# "effects/default.yaml",
# {
# "normal_mapping": False,
# "render_gbuffer": False,
# "alpha_testing": True,
# "parallax_mapping": False,
# "render_shadow": False,
# "render_envmap": False
# },
# 50
# )
# except Exception as e:
# print(f"⚠️ PBR效果应用失败: {e}")
self.gui_elements.append(textNodePath)
# 安全地调用updateSceneTree
if hasattr(self.world, 'updateSceneTree'):
self.world.updateSceneTree()
print(f"✓ 创建3D文本: {text} (世界位置: {pos})")
return textNodePath
"""创建2D GUI文本输入框 - 支持多选创建和GUI父子关系优化版本"""
try:
from direct.gui.DirectGui import DirectEntry
@ -587,6 +382,185 @@ class GUIManager:
traceback.print_exc()
return None
def createGUI2DImage(self, pos=(0, 0, 0), image_path=None, size=0.2):
"""创建2D GUI图片"""
try:
from direct.gui.DirectGui import DirectButton
from PyQt5.QtCore import Qt
print(f"🔘 开始创建GUI按钮位置: {pos}, 图片路径: {image_path}, 尺寸: {size}")
# 获取树形控件
tree_widget = self._get_tree_widget()
if not tree_widget:
print("❌ 无法访问树形控件")
return None
# 使用CustomTreeWidget的方法获取目标父节点列表
target_parents = tree_widget.get_target_parents_for_gui_creation()
if not target_parents:
print("❌ 没有找到有效的父节点")
return None
created_2dimage = []
# 为每个有效的父节点创建GUI按钮
for parent_item, parent_node in target_parents:
try:
# 生成唯一名称
image_name = f"GUIImage_{len(self.gui_elements)}"
# 使用CustomTreeWidget的方法判断父节点类型并设置相应的挂载方式
if tree_widget.is_gui_element(parent_node):
# 父节点是GUI元素 - 作为子GUI挂载
gui_pos = tree_widget.calculate_relative_gui_position(pos, parent_node)
parent_gui_node = parent_node # 直接挂载到GUI元素
print(f"📎 挂载到GUI父节点: {parent_node.getName()}")
else:
# 父节点是普通3D节点 - 使用屏幕坐标
gui_pos = (pos[0] * 0.1, 0, pos[2] * 0.1)
parent_gui_node = None # 使用默认的aspect2d
print(f"📎 挂载到3D父节点: {parent_item.text(0)}")
# 使用CardMaker创建一个更可靠的图片框架
cm = CardMaker('gui-2d-image')
cm.setFrame(-size, size, -size, size)
image_node = self.world.aspect2d.attachNewNode(cm.generate())
image_node.setPos(gui_pos)
image_node.setBin('fixed', 0)
image_node.setDepthWrite(False)
image_node.setDepthTest(False)
image_node.setColor(1, 1, 1, 1)
# 设置透明度支持
image_node.setTransparency(TransparencyAttrib.MAlpha)
# 如果提供了图像路径,则加载纹理
if image_path:
try:
texture = self.world.loader.loadTexture(image_path)
if texture:
image_node.setTexture(texture, 1)
texture.setWrapU(Texture.WM_clamp)
texture.setWrapV(Texture.WM_clamp)
texture.setMinfilter(Texture.FT_linear)
texture.setMagfilter(Texture.FT_linear)
image_node.setColor(1, 1, 1, 1)
else:
print(f"⚠️ 无法加载2D图片纹理: {image_path}")
except Exception as e:
print(f"❌ 加载2D图片纹理失败: {e}")
# 设置节点标签
image_node.setTag("gui_type", "2d_image")
image_node.setTag("gui_id", f"2d_image_{len(self.gui_elements)}")
image_node.setTag("gui_text", f"2D图片_{len(self.gui_elements)}")
image_node.setTag("is_gui_element", "1")
image_node.setTag("is_scene_element", "1")
image_node.setTag("created_by_user", "1")
image_node.setTag("gui_parent_type", "gui" if parent_gui_node else "3d")
image_node.setName(image_name)
# 如果有GUI父节点建立引用关系
if parent_gui_node:
parent_id = parent_gui_node.getTag("gui_id") if hasattr(parent_gui_node, 'getTag') else ""
image_node.setTag("gui_parent_id", parent_id)
# 添加到GUI元素列表
self.gui_elements.append(image_node)
print(f"✅ 为 {parent_item.text(0)} 创建GUI按钮成功: {image_name}")
# 使用CustomTreeWidget的方法在Qt树形控件中添加对应节点
qt_item = tree_widget.add_node_to_tree_widget(image_node, parent_item, "GUI_IMAGE")
if qt_item:
created_2dimage.append((image_node, qt_item))
else:
created_2dimage.append((image_node, None))
print("⚠️ Qt树节点添加失败但GUI对象已创建")
except Exception as e:
print(f"❌ 为 {parent_item.text(0)} 创建GUI按钮失败: {str(e)}")
continue
# 处理创建结果
if not created_2dimage:
print("❌ 没有成功创建任何GUI按钮")
return None
# 选中最后创建的按钮并更新场景树
if created_2dimage:
last_button, last_qt_item = created_2dimage[-1]
if last_qt_item:
tree_widget.setCurrentItem(last_qt_item)
tree_widget.update_selection_and_properties(last_button, last_qt_item)
print(f"🎉 总共创建了 {len(created_2dimage)} 个GUI按钮")
# 返回值处理
if len(created_2dimage) == 1:
return created_2dimage[0][0]
else:
return [button for button, _ in created_2dimage]
except Exception as e:
print(f"❌ 创建GUI按钮过程失败: {str(e)}")
import traceback
traceback.print_exc()
return None
def constrain2DPosition(self,gui_element,new_x=None,new_z=None):
"""限制2dGUI元素位置在屏幕范围内"""
try:
from panda3d.core import Vec3
bounds = gui_element.getTightBounds()
element_width=0
element_height=0
if bounds:
min_point,max_point = bounds
element_width = (max_point.getX() - min_point.getX())/2
element_height = (max_point.getZ()-min_point.getZ())/2
#获取当前缩放
scale = gui_element.getScale()
if hasattr(scale,'getX'):
scale_x = scale.getX()
scale_z = scale.getZ() if hasattr(scale,'getZ') else scale_x
else:
scale_x = scale_z = scale if isinstance(scale,(int,float)) else 1.0
actual_width = element_width * scale_x
actual_height = element_height * scale_z
screen_width = 1.9
screen_height = 0.9
min_x = -screen_width + actual_width
max_x = screen_width - actual_width
min_z = -screen_height + actual_height
max_z = screen_height - actual_height
#获取当前位置
current_pos = gui_element.getPos()
x = new_x if new_x is not None else current_pos.getX()
z = new_z if new_z is not None else current_pos.getZ()
#应用边界限制
x = max(min_x,min(max_x,x))
z = max(min_z,min(max_z,z))
return Vec3(x,current_pos.getY(),z)
except Exception as e:
print(f"约束2D位置时出错: {e}")
# 出错时返回原始值
current_pos = gui_element.getPos()
x = new_x if new_x is not None else current_pos.getX()
z = new_z if new_z is not None else current_pos.getZ()
return Vec3(x, current_pos.getY(), z)
def createGUI3DText(self, pos=(0, 0, 0), text="3D文本", size=0.5):
"""创建3D空间文本 - 支持多选创建,优化版本"""
try:
@ -680,6 +654,141 @@ class GUIManager:
traceback.print_exc()
return None
def createGUI3DImage(self, pos=(0, 0, 0), image_path=None, size=1.0):
"""创建3D空间图片"""
try:
from panda3d.core import TextNode
from PyQt5.QtCore import Qt
print(f"📄 开始创建3D文本位置: {pos}, 3D图片位置: {image_path}, 尺寸: {size}")
# 获取树形控件
tree_widget = self._get_tree_widget()
if not tree_widget:
print("❌ 无法访问树形控件")
return None
# 获取目标父节点列表
target_parents = tree_widget.get_target_parents_for_creation()
if not target_parents:
print("❌ 没有找到有效的父节点")
return None
created_3dimage = []
# 为每个有效的父节点创建3D文本
for parent_item, parent_node in target_parents:
try:
# 生成唯一名称
image_name = f"GUI3DImage_{len(self.gui_elements)}"
# 参数类型检查和转换
if isinstance(size, (list, tuple)):
if len(size) >= 2:
x_size, y_size = float(size[0]), float(size[1])
else:
x_size = y_size = float(size[0]) if size else 1.0
else:
x_size = y_size = float(size)
# 创建卡片
cm = CardMaker('gui_3d_image')
cm.setFrame(-x_size / 2, x_size / 2, -y_size / 2, y_size / 2)
# 创建3D图像节点
image_node = self.world.render.attachNewNode(cm.generate())
image_node.setPos(*pos)
# 为3D图像创建独立的材质
material = Material(f"image-material-{len(self.gui_elements)}")
material.setBaseColor(LColor(1, 1, 1, 1))
material.setDiffuse(LColor(1, 1, 1, 1))
material.setAmbient(LColor(0.5, 0.5, 0.5, 1))
material.setSpecular(LColor(0.1, 0.1, 0.1, 1.0))
material.setShininess(10.0)
material.setEmission(LColor(0, 0, 0, 1)) # 无自发光
image_node.setMaterial(material, 1)
image_node.setTransparency(TransparencyAttrib.MAlpha)
# 如果提供了图像路径,则加载纹理
if image_path:
self.update3DImageTexture(image_node, image_path)
# 应用PBR效果如果可用
try:
if hasattr(self, 'render_pipeline') and self.render_pipeline:
self.render_pipeline.set_effect(
image_node,
"effects/default.yaml",
{
"normal_mapping": True,
"render_gbuffer": True,
"alpha_testing": False,
"parallax_mapping": False,
"render_shadow": False,
"render_envmap": True,
"disable_children_effects": True
},
50
)
print("✓ GUI 3D图像PBR效果已应用")
except Exception as e:
print(f"⚠️ GUI 3D图像PBR效果应用失败: {e}")
image_node.setName(image_name)
# 设置节点标签
image_node.setTag("gui_type", "3d_image")
image_node.setTag("gui_id", f"3d_image_{len(self.gui_elements)}")
if image_path:
image_node.setTag("gui_image_path", image_path)
image_node.setTag("is_gui_element", "1")
image_node.setTag("is_scene_element", "1")
image_node.setTag("created_by_user", "1")
# 添加到GUI元素列表
self.gui_elements.append(image_node)
print(f"✅ 为 {parent_item.text(0)} 创建3D文本成功: {image_name}")
# 在Qt树形控件中添加对应节点
qt_item = tree_widget.add_node_to_tree_widget(image_node, parent_item, "GUI_3DIMAGE")
if qt_item:
created_3dimage.append((image_node, qt_item))
else:
created_3dimage.append((image_node, None))
print("⚠️ Qt树节点添加失败但GUI对象已创建")
except Exception as e:
print(f"❌ 为 {parent_item.text(0)} 创建3D文本失败: {str(e)}")
continue
# 处理创建结果
if not created_3dimage:
print("❌ 没有成功创建任何3D文本")
return None
# 选中最后创建的文本并更新场景树
if created_3dimage:
last_image, last_qt_item = created_3dimage[-1]
if last_qt_item:
tree_widget.setCurrentItem(last_qt_item)
tree_widget.update_selection_and_properties(last_image, last_qt_item)
print(f"🎉 总共创建了 {len(created_3dimage)} 个3D文本")
# 返回值处理
if len(created_3dimage) == 1:
return created_3dimage[0][0]
else:
return [text_np for text_np, _ in created_3dimage]
except Exception as e:
print(f"❌ 创建3D文本过程失败: {str(e)}")
import traceback
traceback.print_exc()
return None
def createGUIVirtualScreen(self, pos=(0, 0, 0), size=(2, 1), text="虚拟屏幕"):
"""创建3D虚拟屏幕 - 支持多选创建,优化版本"""
try:

View File

@ -282,9 +282,6 @@ class InterfaceManager:
terrain_item.setData(0,Qt.UserRole+1,"terrain") # 标记为地形节点
addNodeToTree(terrain_node,terrain_item,force=True)
# 展开所有节点
#self.treeWidget.expandAll()
self._restore_expanded()

View File

@ -41,6 +41,11 @@ class MainWindow(QMainWindow):
self.updateTimer.timeout.connect(self.updateScriptPanel)
self.updateTimer.start(500) # 每500毫秒更新一次
self.toolbarDragging = False
self.dragStartPos = QPoint(0, 0)
self.toolbarStartPos = QPoint(0, 0)
def setupCenterWidget(self):
"""设置窗口基本属性"""
self.setWindowTitle("引擎编辑器")
@ -178,21 +183,27 @@ class MainWindow(QMainWindow):
def toolbarMouseMoveEvent(self, event):
"""工具栏鼠标移动事件"""
if self.toolbarDragging and event.buttons() == Qt.LeftButton:
# 计算新位置
delta = event.globalPos() - self.dragStartPos
new_pos = self.toolbarStartPos + delta
try:
if self.toolbarDragging and event.buttons() == Qt.LeftButton:
# 计算新位置
delta = event.globalPos() - self.dragStartPos
new_pos = self.toolbarStartPos + delta
# 边界检测
panda_rect = self.pandaWidget.geometry()
toolbar_size = self.embeddedToolbar.size()
# 边界检测
panda_rect = self.pandaWidget.geometry()
toolbar_size = self.embeddedToolbar.size()
# 限制在Panda3D区域内
new_pos.setX(max(0, min(new_pos.x(), panda_rect.width() - toolbar_size.width())))
new_pos.setY(max(0, min(new_pos.y(), panda_rect.height() - toolbar_size.height())))
# 限制在Panda3D区域内
new_pos.setX(max(0, min(new_pos.x(), panda_rect.width() - toolbar_size.width())))
new_pos.setY(max(0, min(new_pos.y(), panda_rect.height() - toolbar_size.height())))
self.embeddedToolbar.move(new_pos)
event.accept()
except Exception as e:
print(f"工具栏鼠标移动事件出错")
import traceback
traceback.print_exc()
self.embeddedToolbar.move(new_pos)
event.accept()
def toolbarMouseReleaseEvent(self, event):
"""工具栏鼠标释放事件"""
@ -303,26 +314,12 @@ class MainWindow(QMainWindow):
self.createMenu = menubar.addMenu('创建')
self.setupCreateMenuActions() # 统一创建菜单动作
self.createGUIaddMenu = self.createMenu.addMenu('GUI')
self.createButtonAction = self.createGUIaddMenu.addAction('创建按钮')
self.createLabelAction = self.createGUIaddMenu.addAction('创建标签')
self.createEntryAction = self.createGUIaddMenu.addAction('创建输入框')
self.createGUIaddMenu.addSeparator()
self.createVirtualScreenAction = self.createGUIaddMenu.addAction('创建虚拟屏幕')
self.createCesiumViewAction = self.createGUIaddMenu.addAction('创建Cesium地图')
self.toggleCesiumViewAction = self.createGUIaddMenu.addAction('开关地图')
self.refreshCesiumViewAction = self.createGUIaddMenu.addAction('刷新地图')
self.createLightaddMenu = self.createMenu.addMenu('光源')
self.createSpotLightAction = self.createLightaddMenu.addAction('聚光灯')
self.createPointLightAction = self.createLightaddMenu.addAction('点光源')
#添加地形菜单
self.createTerrainMenu = self.createMenu.addMenu('地形')
self.createFlatTerrainAction = self.createTerrainMenu.addAction('创建平面地形')
self.createHeightmapTerrainAction = self.createTerrainMenu.addAction('从高度图创建地形')
self.createTerrainMenu.addSeparator()
self.terrainEditModeAction = self.createTerrainMenu.addAction('地形编辑模式')
# self.terrainEditModeAction = self.createTerrainMenu.addAction('地形编辑模式')
# GUI菜单
# GUI菜单 - 复用创建菜单的动作
@ -383,12 +380,14 @@ class MainWindow(QMainWindow):
# 3D GUI子菜单
self.create3dGUIaddMenu = self.createMenu.addMenu('3D GUI')
self.create3DTextAction = self.create3dGUIaddMenu.addAction('3D文本')
self.create3DImageAction = self.create3dGUIaddMenu.addAction('3D图片')
# GUI子菜单
self.createGUIaddMenu = self.createMenu.addMenu('GUI')
self.createButtonAction = self.createGUIaddMenu.addAction('创建按钮')
self.createLabelAction = self.createGUIaddMenu.addAction('创建标签')
self.createEntryAction = self.createGUIaddMenu.addAction('创建输入框')
self.createImageAction = self.createGUIaddMenu.addAction('创建图片')
self.createGUIaddMenu.addSeparator()
self.createVideoScreen = self.createGUIaddMenu.addAction('创建视频屏幕')
self.createSphericalVideo = self.createGUIaddMenu.addAction('创建球形视频')
@ -408,12 +407,14 @@ class MainWindow(QMainWindow):
# 连接到world对象的创建方法
# self.createEnptyaddAction.triggered.connect(self.world.createEmptyObject)
self.create3DTextAction.triggered.connect(lambda: self.world.createGUI3DText())
self.create3DImageAction.triggered.connect(lambda: self.world.createGUI3DImage())
self.createButtonAction.triggered.connect(lambda: self.world.createGUIButton())
self.createLabelAction.triggered.connect(lambda: self.world.createGUILabel())
self.createEntryAction.triggered.connect(lambda: self.world.createGUIEntry())
self.createImageAction.triggered.connect(lambda: self.world.createGUI2DImage())
# self.createVideoScreen.triggered.connect(self.world.createVideoScreen)
# self.createSphericalVideo.triggered.connect(self.world.createSphericalVideo)
# self.createVirtualScreenAction.triggered.connect(self.world.createVirtualScreen)
self.createVirtualScreenAction.triggered.connect(lambda: self.world.createGUIVirtualScreen())
self.createSpotLightAction.triggered.connect(lambda :self.world.createSpotLight())
self.createPointLightAction.triggered.connect(lambda :self.world.createPointLight())
@ -430,9 +431,11 @@ class MainWindow(QMainWindow):
return {
'createEmpty': self.createEnptyaddAction,
'create3DText': self.create3DTextAction,
'create3DImage': self.create3DImageAction,
'createButton': self.createButtonAction,
'createLabel': self.createLabelAction,
'createEntry': self.createEntryAction,
'createImage': self.createImageAction,
'createVideoScreen': self.createVideoScreen,
'createSphericalVideo': self.createSphericalVideo,
'createVirtualScreen': self.createVirtualScreenAction,
@ -785,13 +788,13 @@ class MainWindow(QMainWindow):
self.guiEditModeAction.triggered.connect(lambda: self.world.toggleGUIEditMode())
# 连接创建事件 - 使用菜单动作而不是不存在的工具栏按钮
self.createSpotLightAction.triggered.connect(lambda: self.world.createSpotLight())
self.createPointLightAction.triggered.connect(lambda: self.world.createPointLight())
self.createButtonAction.triggered.connect(lambda: self.world.createGUIButton())
self.createLabelAction.triggered.connect(lambda: self.world.createGUILabel())
self.createEntryAction.triggered.connect(lambda: self.world.createGUIEntry())
self.create3DTextAction.triggered.connect(lambda: self.world.createGUI3DText())
self.createVirtualScreenAction.triggered.connect(lambda: self.world.createGUIVirtualScreen())
# self.createSpotLightAction.triggered.connect(lambda: self.world.createSpotLight())
# self.createPointLightAction.triggered.connect(lambda: self.world.createPointLight())
# self.createButtonAction.triggered.connect(lambda: self.world.createGUIButton())
# self.createLabelAction.triggered.connect(lambda: self.world.createGUILabel())
# self.createEntryAction.triggered.connect(lambda: self.world.createGUIEntry())
# self.create3DTextAction.triggered.connect(lambda: self.world.createGUI3DText())
# self.createVirtualScreenAction.triggered.connect(lambda: self.world.createGUIVirtualScreen())
self.createCesiumViewAction.triggered.connect(self.onCreateCesiumView)
self.toggleCesiumViewAction.triggered.connect(self.onToggleCesiumView)
self.refreshCesiumViewAction.triggered.connect(self.onRefreshCesiumView)
@ -799,7 +802,7 @@ class MainWindow(QMainWindow):
# 连接地形创建事件
self.createFlatTerrainAction.triggered.connect(self.onCreateFlatTerrain)
self.createHeightmapTerrainAction.triggered.connect(self.onCreateHeightmapTerrain)
self.terrainEditModeAction.triggered.connect(self.onTerrainEditMode)
# self.terrainEditModeAction.triggered.connect(self.onTerrainEditMode)
# 连接树节点点击信号
self.treeWidget.itemSelectionChanged.connect(

View File

@ -1553,6 +1553,7 @@ class PropertyPanelManager:
)
if file_path:
# 应用新纹理到 3D Image
file_path = util.normalize_model_path(file_path)
success = self.world.gui_manager.update3DImageTexture(gui_element, file_path)
if success:
# 保存路径到 Tag
@ -1560,7 +1561,7 @@ class PropertyPanelManager:
# 更新显示
texture_label.setText(file_path)
# 可选:刷新场景树或其他 UI
self.world.scene_manager.updateSceneTree()
# self.world.scene_manager.updateSceneTree()
select_texture_button.clicked.connect(onSelectTexture)
@ -1595,6 +1596,7 @@ class PropertyPanelManager:
)
if file_path:
# 应用新纹理到 2D Image
file_path = util.normalize_model_path(file_path)
success = self.update2DImageTexture(gui_element, file_path)
if success:
# 保存路径到 Tag
@ -1603,7 +1605,7 @@ class PropertyPanelManager:
# 更新显示
#texture_label.setText(file_path)
# 可选:刷新场景树或其他 UI
self.world.scene_manager.updateSceneTree()
# self.world.scene_manager.updateSceneTree()
select_texture_button.clicked.connect(onSelect2DTexture)

View File

@ -1302,11 +1302,40 @@ class CustomTreeWidget(QTreeWidget):
parent = wrapinstance(0, QWidget)
super().__init__(parent)
self.world = world
self.initData()
self.setupUI() # 初始化界面
self.setupContextMenu() # 初始化右键菜单
self.setupDragDrop() # 设置拖拽功能
def initData(self):
"""初始化变量"""
# 定义2D GUI元素类型
self.gui_2d_types = {
"GUI_BUTTON", # DirectButton
"GUI_LABEL", # DirectLabel
"GUI_ENTRY", # DirectEntry
"GUI_IMAGE",
"GUI_NODE" # 其他2D GUI容器
}
# 定义3D GUI元素类型
self.gui_3d_types = {
"GUI_3DTEXT", # 3D TextNode
"GUI_3DIMAGE",
"GUI_VIRTUAL_SCREEN" # Virtual Screen
}
# 定义3D场景节点类型可以接受3D GUI元素和其他3D场景元素
self.scene_3d_types = {
"SCENE_ROOT",
"SCENE_NODE",
"LIGHT_NODE",
"CAMERA_NODE",
"IMPORTED_MODEL_NODE",
"MODEL_NODE"
}
def setupUI(self):
"""初始化UI设置"""
self.setHeaderHidden(True)
@ -1356,12 +1385,14 @@ class CustomTreeWidget(QTreeWidget):
# 3D GUI菜单
create3dGUIMenu = createMenu.addMenu('3D GUI')
create3dGUIMenu.addAction(create_actions['create3DText'])
create3dGUIMenu.addAction(create_actions['create3DImage'])
# GUI菜单
createGUIMenu = createMenu.addMenu('GUI')
createGUIMenu.addAction(create_actions['createButton'])
createGUIMenu.addAction(create_actions['createLabel'])
createGUIMenu.addAction(create_actions['createEntry'])
createGUIMenu.addAction(create_actions['createImage'])
createGUIMenu.addSeparator()
createGUIMenu.addAction(create_actions['createVideoScreen'])
createGUIMenu.addAction(create_actions['createSphericalVideo'])
@ -1455,7 +1486,7 @@ class CustomTreeWidget(QTreeWidget):
# 检查是否是2D GUI元素
dragged_type = dragged_item.data(0, Qt.UserRole + 1)
is_2d_gui = dragged_type in {"GUI_BUTTON", "GUI_LABEL", "GUI_ENTRY", "GUI_NODE"}
is_2d_gui = dragged_type in self.gui_2d_types
# 重新父化到新的父节点
if new_parent_node:
@ -1595,28 +1626,13 @@ class CustomTreeWidget(QTreeWidget):
target_type = target_item.data(0, Qt.UserRole + 1)
# 定义2D GUI元素类型
gui_2d_types = {
"GUI_BUTTON", # DirectButton
"GUI_LABEL", # DirectLabel
"GUI_ENTRY", # DirectEntry
"GUI_NODE" # 其他2D GUI容器
}
gui_2d_types = self.gui_2d_types
# 定义3D GUI元素类型
gui_3d_types = {
"GUI_3DTEXT", # 3D TextNode
"GUI_VIRTUAL_SCREEN" # Virtual Screen
}
gui_3d_types = self.gui_3d_types
# 定义3D场景节点类型可以接受3D GUI元素和其他3D场景元素
scene_3d_types = {
"SCENE_ROOT",
"SCENE_NODE",
"LIGHT_NODE",
"CAMERA_NODE",
"IMPORTED_MODEL_NODE",
"MODEL_NODE"
}
scene_3d_types = self.scene_3d_types
# 检查拖拽元素的类型
is_dragged_2d_gui = dragged_type in gui_2d_types
@ -1704,24 +1720,6 @@ class CustomTreeWidget(QTreeWidget):
# 出错时采用保守策略,禁止拖拽
return False
def _getNodeTypeDescription(self, node_type):
"""获取节点类型的描述文本(用于错误提示)"""
type_descriptions = {
"GUI_BUTTON": "2D按钮",
"GUI_LABEL": "2D标签",
"GUI_ENTRY": "2D输入框",
"GUI_NODE": "2D GUI容器",
"GUI_3DTEXT": "3D文本",
"GUI_VIRTUAL_SCREEN": "虚拟屏幕",
"SCENE_ROOT": "场景根节点",
"SCENE_NODE": "场景节点",
"LIGHT_NODE": "灯光节点",
"CAMERA_NODE": "相机节点",
"IMPORTED_MODEL_NODE": "导入模型",
"MODEL_NODE": "模型节点"
}
return type_descriptions.get(node_type, f"未知类型({node_type})")
def _getRootNode(self, item):
"""获取树中节点的根节点项"""
if not item:
@ -2018,10 +2016,7 @@ class CustomTreeWidget(QTreeWidget):
node_type = item.data(0, Qt.UserRole + 1)
# 场景根节点和普通场景节点可以作为父节点
if node_type in ["SCENE_ROOT",
"SCENE_NODE", "LIGHT_NODE", "CAMERA_NODE",
"IMPORTED_MODEL_NODE",
"GUI_3DTEXT"]:
if node_type in self.gui_3d_types and self.scene_3d_types:
return True
# # 模型节点也可以作为父节点
@ -2095,7 +2090,7 @@ class CustomTreeWidget(QTreeWidget):
node_type = item.data(0, Qt.UserRole + 1)
# GUI元素可以作为其他GUI元素的父节点
if node_type in ["GUI_BUTTON", "GUI_LABEL", "GUI_ENTRY", "GUI_NODE"]:
if node_type in self.gui_2d_types:
return True
# 场景根节点和普通场景节点也可以作为父节点