forked from Rowland/EG
脚本打包加载
This commit is contained in:
parent
9c4116319b
commit
c252a8cba7
@ -351,7 +351,11 @@ class RenderPipeline(RPObject):
|
||||
continue
|
||||
|
||||
material = state.get_attrib(MaterialAttrib).get_material()
|
||||
shading_model = material.emission.x
|
||||
if material.emission is not None:
|
||||
shading_model = material.emission.x
|
||||
else:
|
||||
shading_model = 0.0
|
||||
|
||||
|
||||
# SHADING_MODEL_TRANSPARENT
|
||||
if shading_model == 3:
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
from xml.sax.handler import property_encoding
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
from direct.gui.DirectGui import DirectFrame, DirectLabel
|
||||
from direct.showbase.ShowBaseGlobal import aspect2d
|
||||
from panda3d.core import TextNode, Vec4, NodePath
|
||||
@ -189,7 +190,7 @@ class InfoPanelManager(DirectObject):
|
||||
|
||||
# 如果有背景图片,保存背景图片路径
|
||||
if bg_image:
|
||||
panel_node.setTag("bg_image_path", bg_image)
|
||||
panel_node.setTag("image_path", bg_image)
|
||||
|
||||
|
||||
if not visible:
|
||||
@ -197,6 +198,8 @@ class InfoPanelManager(DirectObject):
|
||||
|
||||
# 将面板添加到场景树
|
||||
#self._addPanelToSceneTree(panel_node, panel_id)
|
||||
if hasattr(self.world, 'gui_elements'):
|
||||
self.world.gui_elements.append(panel_node)
|
||||
|
||||
return panel_node
|
||||
|
||||
@ -820,6 +823,8 @@ class InfoPanelManager(DirectObject):
|
||||
|
||||
# 将面板添加到场景树
|
||||
#self._addPanelToSceneTree(panel_node, panel_id)
|
||||
if hasattr(self.world, 'gui_elements'):
|
||||
self.world.gui_elements.append(panel_node)
|
||||
|
||||
return panel_node
|
||||
|
||||
@ -1266,6 +1271,101 @@ class InfoPanelManager(DirectObject):
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
def onCreateSampleInfoPanel(self):
|
||||
"""创建示例天气信息面板(模拟数据)"""
|
||||
try:
|
||||
# 获取中文字体
|
||||
from panda3d.core import TextNode
|
||||
font = self.world.getChineseFont() if self.world.getChineseFont() else None
|
||||
|
||||
# 使用唯一的面板ID
|
||||
import time
|
||||
unique_id = f"weather_info_{int(time.time())}"
|
||||
|
||||
# 创建示例面板
|
||||
weather_panel = self.createInfoPanel(
|
||||
panel_id=unique_id, # 使用唯一ID
|
||||
position=(1.32, 0.68),
|
||||
size=(1, 0.6),
|
||||
bg_color=(0.15, 0.25, 0.35, 0), # 蓝色背景
|
||||
border_color=(0.3, 0.5, 0.7, 0), # 蓝色边框
|
||||
title_color=(0.7, 0.9, 1.0, 1.0), # 浅蓝色标题
|
||||
content_color=(0.95, 0.95, 0.95, 1.0),
|
||||
font=font,
|
||||
bg_image="/home/tiger/图片/内部信息框2@2x.png"
|
||||
)
|
||||
|
||||
# 更新面板标题
|
||||
self.updatePanelContent(unique_id, title="北京天气")
|
||||
|
||||
self._addPanelToSceneTree(weather_panel, unique_id)
|
||||
# 立即显示加载中信息
|
||||
self.updatePanelContent(unique_id, content="正在获取天气数据...")
|
||||
|
||||
self.registerDataSource(unique_id, self.getRealWeatherData, update_interval=5.0)
|
||||
|
||||
print("✓ 示例天气信息面板已创建")
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ 创建示例天气信息面板失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
QMessageBox.critical(self, "错误", f"创建示例天气信息面板时出错: {str(e)}")
|
||||
|
||||
def getRealWeatherData(self):
|
||||
"""获取真实天气数据"""
|
||||
try:
|
||||
import requests
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
# 请求天气数据
|
||||
url = "https://wttr.in/Beijing?format=j1"
|
||||
response = requests.get(url, timeout=10)
|
||||
response.raise_for_status()
|
||||
|
||||
# 解析JSON数据
|
||||
weather_data = response.json()
|
||||
|
||||
# 提取当前天气信息
|
||||
current_condition = weather_data['current_condition'][0]
|
||||
weather_desc = current_condition['weatherDesc'][0]['value']
|
||||
temp_c = current_condition['temp_C']
|
||||
feels_like = current_condition['FeelsLikeC']
|
||||
humidity = current_condition['humidity']
|
||||
pressure = current_condition['pressure']
|
||||
visibility = current_condition['visibility']
|
||||
wind_speed = current_condition['windspeedKmph']
|
||||
wind_dir = current_condition['winddir16Point']
|
||||
|
||||
# 提取空气质量(如果可用)
|
||||
air_quality = "N/A"
|
||||
if 'air_quality' in weather_data and weather_data['air_quality']:
|
||||
if 'us-epa-index' in current_condition:
|
||||
air_quality_index = current_condition['air_quality_index']
|
||||
air_quality = f"指数: {air_quality_index}"
|
||||
|
||||
# 获取更新时间
|
||||
update_time = datetime.now().strftime("%Y-%m-%d %H:%M")
|
||||
|
||||
# 格式化显示内容
|
||||
content = f"天气状况: {weather_desc}\n温度: {temp_c}°C (体感 {feels_like}°C)\n湿度: {humidity}%\n气压: {pressure} hPa\n能见度: {visibility} km\n风速: {wind_speed} km/h ({wind_dir})\n空气质量: {air_quality}\n更新时间: {update_time}"
|
||||
|
||||
return content
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
return "错误: 获取天气数据超时"
|
||||
except requests.exceptions.ConnectionError:
|
||||
return "错误: 网络连接失败"
|
||||
except requests.exceptions.HTTPError as e:
|
||||
return f"HTTP错误: {e}"
|
||||
except json.JSONDecodeError:
|
||||
return "错误: 无法解析天气数据"
|
||||
except KeyError as e:
|
||||
return f"错误: 天气数据格式不正确 (缺少字段: {e})"
|
||||
except Exception as e:
|
||||
return f"获取天气数据失败: {str(e)}"
|
||||
|
||||
|
||||
# 示例数据源函数
|
||||
def getRealtimeData():
|
||||
|
||||
@ -544,8 +544,8 @@ class GUIManager:
|
||||
|
||||
if isinstance(size, (list, tuple)) and len(size) >= 2:
|
||||
# 分别处理宽度和高度的缩放
|
||||
width_scale = size[0] * 0.05
|
||||
height_scale = size[2] * 0.05
|
||||
width_scale = size[0] * 0.25
|
||||
height_scale = size[2] * 0.25
|
||||
else:
|
||||
# 如果只提供了一个缩放值,则使用相同值
|
||||
width_scale = size * 0.1 if isinstance(size, (int, float)) else 0.2
|
||||
|
||||
2
main.py
2
main.py
@ -100,7 +100,7 @@ class MyWorld(CoreWorld):
|
||||
self.vr_manager = VRManager(self)
|
||||
self.vr_input_handler = VRInputHandler(self, self.vr_manager)
|
||||
self.alvr_streamer = ALVRStreamer(self, self.vr_manager)
|
||||
|
||||
|
||||
# 启动脚本系统
|
||||
self.script_manager.start_system()
|
||||
|
||||
|
||||
@ -399,6 +399,12 @@ class ProjectManager:
|
||||
|
||||
self._saveGUIElementsToJSON(build_dir, project_path)
|
||||
|
||||
self._copyScriptsToBuild(build_dir, project_path)
|
||||
|
||||
self._copyScriptSystemToBuild(build_dir)
|
||||
|
||||
self._copyInfoPanelSystemToBuild(build_dir)
|
||||
|
||||
source_render_pipeline = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),"RenderPipelineFile")
|
||||
dest_render_pipeline = os.path.join(build_dir,"RenderPipelineFile")
|
||||
|
||||
@ -424,6 +430,89 @@ class ProjectManager:
|
||||
#创建requirements.txt文件
|
||||
self._createRequirementsFile(build_dir)
|
||||
|
||||
def _copyScriptsToBuild(self, build_dir, project_path):
|
||||
"""复制脚本文件到构建目录的scripts文件夹"""
|
||||
try:
|
||||
# 创建目标scripts目录
|
||||
scripts_dest = os.path.join(build_dir, "scripts")
|
||||
|
||||
# 正确的源scripts目录路径
|
||||
scripts_src = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "scripts")
|
||||
|
||||
# 如果上面的路径不存在,尝试项目路径下的scripts目录
|
||||
if not os.path.exists(scripts_src):
|
||||
scripts_src = os.path.join(project_path, "scripts")
|
||||
|
||||
if os.path.exists(scripts_src):
|
||||
# 直接复制整个scripts目录
|
||||
if os.path.exists(scripts_dest):
|
||||
shutil.rmtree(scripts_dest)
|
||||
|
||||
shutil.copytree(
|
||||
scripts_src,
|
||||
scripts_dest,
|
||||
ignore=shutil.ignore_patterns('__pycache__', '*.pyc', '.git', '.vscode', '*.log')
|
||||
)
|
||||
print("✓ Scripts目录已复制到build/scripts")
|
||||
else:
|
||||
# 创建空的scripts目录
|
||||
if not os.path.exists(scripts_dest):
|
||||
os.makedirs(scripts_dest)
|
||||
print("⚠️ 项目中没有scripts目录")
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ 复制脚本文件时出错: {str(e)}")
|
||||
|
||||
def _copyScriptSystemToBuild(self, build_dir):
|
||||
"""复制core/script_system.py文件到构建目录"""
|
||||
try:
|
||||
# 源文件路径
|
||||
source_script_system = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "core",
|
||||
"script_system.py")
|
||||
|
||||
# 目标目录
|
||||
core_dest = os.path.join(build_dir, "core")
|
||||
|
||||
# 如果源文件存在
|
||||
if os.path.exists(source_script_system):
|
||||
# 确保目标目录存在
|
||||
if not os.path.exists(core_dest):
|
||||
os.makedirs(core_dest)
|
||||
|
||||
# 复制文件
|
||||
shutil.copy2(source_script_system, os.path.join(core_dest, "script_system.py"))
|
||||
print("✓ core/script_system.py文件已复制到build目录")
|
||||
else:
|
||||
print("⚠️ core/script_system.py文件未找到")
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ 复制core/script_system.py文件时出错: {str(e)}")
|
||||
|
||||
def _copyInfoPanelSystemToBuild(self, build_dir):
|
||||
"""复制core/script_system.py文件到构建目录"""
|
||||
try:
|
||||
# 源文件路径
|
||||
source_InfoPanel_system = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "core",
|
||||
"InfoPanelManager.py")
|
||||
|
||||
# 目标目录
|
||||
core_dest = os.path.join(build_dir, "core")
|
||||
|
||||
# 如果源文件存在
|
||||
if os.path.exists(source_InfoPanel_system):
|
||||
# 确保目标目录存在
|
||||
if not os.path.exists(core_dest):
|
||||
os.makedirs(core_dest)
|
||||
|
||||
# 复制文件
|
||||
shutil.copy2(source_InfoPanel_system, os.path.join(core_dest, "InfoPanelManager.py"))
|
||||
print("✓ core/InfoPanelManager.py文件已复制到build目录")
|
||||
else:
|
||||
print("⚠️ core/InfoPanelManager.py文件未找到")
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ 复制core/InfoPanelManager.py文件时出错: {str(e)}")
|
||||
|
||||
def _saveGUIElementsToJSON(self, build_dir, project_path):
|
||||
"""保存GUI元素到JSON文件,内容与_collectGUIElementInfo保持一致"""
|
||||
try:
|
||||
@ -1035,10 +1124,31 @@ setup(
|
||||
'direct.task',
|
||||
'direct.actor',
|
||||
'direct.interval',
|
||||
'direct.stdpy.file',
|
||||
'direct.stdpy.pickle',
|
||||
'panda3d.core',
|
||||
'panda3d.direct',
|
||||
'rpcore',
|
||||
'rpcore.util.movement_controller',
|
||||
'rpcore.native',
|
||||
'rpcore.render_pipeline',
|
||||
'rplibs',
|
||||
'rpplugins',
|
||||
'rpplugins.scattering',
|
||||
'rpplugins.pssm',
|
||||
'rpplugins.godrays',
|
||||
'json',
|
||||
'os',
|
||||
'sys',
|
||||
'six',
|
||||
'collections',
|
||||
'collections.abs',
|
||||
'weakref',
|
||||
'copy',
|
||||
'itertools',
|
||||
'importlib',
|
||||
'importlib.util',
|
||||
'importlib.machinery',
|
||||
],
|
||||
}},
|
||||
|
||||
|
||||
@ -902,6 +902,7 @@ class SceneManager:
|
||||
"tags": {},
|
||||
"parent_name":None,
|
||||
"video_path":gui_node.getTag("video_path") if gui_node.hasTag("video_path") else None,
|
||||
"panel_id":gui_node.getTag("panel_id") if gui_node.hasTag("panel_id") else None,
|
||||
}
|
||||
|
||||
parent = gui_node.getParent()
|
||||
@ -911,13 +912,13 @@ class SceneManager:
|
||||
gui_info["parent_name"] = parent_name
|
||||
|
||||
# 收集所有标签(仅对NodePath类型的对象)
|
||||
# if hasattr(gui_node, 'getTagNames'):
|
||||
# for tag in gui_node.getTagNames():
|
||||
# gui_info["tags"][tag] = gui_node.getTag(tag)
|
||||
# elif hasattr(gui_node, 'getTags'): # 对于DirectGUI对象
|
||||
# # DirectGUI对象使用不同的方法存储标签
|
||||
# if hasattr(gui_node, '_tags'):
|
||||
# gui_info["tags"] = gui_node._tags.copy()
|
||||
if hasattr(gui_node, 'getTagNames'):
|
||||
for tag in gui_node.getTagNames():
|
||||
gui_info["tags"][tag] = gui_node.getTag(tag)
|
||||
elif hasattr(gui_node, 'getTags'): # 对于DirectGUI对象
|
||||
# DirectGUI对象使用不同的方法存储标签
|
||||
if hasattr(gui_node, '_tags'):
|
||||
gui_info["tags"] = gui_node._tags.copy()
|
||||
|
||||
# 根据类型收集特定信息
|
||||
if gui_type == "button":
|
||||
@ -965,39 +966,37 @@ class SceneManager:
|
||||
gui_info["panel_id"] = gui_node.getTag("panel_id")
|
||||
|
||||
# 收集背景图片信息
|
||||
if hasattr(gui_node, 'hasTag') and gui_node.hasTag("bg_image_path"):
|
||||
gui_info["bg_image_path"] = gui_node.getTag("bg_image_path")
|
||||
if hasattr(gui_node, 'hasTag') and gui_node.hasTag("image_path"):
|
||||
gui_info["image_path"] = gui_node.getTag("image_path")
|
||||
|
||||
# 如果是信息面板,收集面板数据
|
||||
if hasattr(gui_node, 'hasTag') and gui_node.hasTag("info_panel_data"):
|
||||
gui_info["panel_data"] = gui_node.getTag("info_panel_data")
|
||||
|
||||
# 收集挂载的脚本信息
|
||||
if hasattr(self.world, 'script_manager') and self.world.script_manager:
|
||||
try:
|
||||
script_manager = self.world.script_manager
|
||||
scripts = script_manager.get_scripts_on_object(gui_node)
|
||||
scripts = script_manager.get_scripts_on_object(gui_node) # 修复:使用 gui_node 而不是 node
|
||||
if scripts:
|
||||
gui_info["scripts"] = []
|
||||
for script_component in scripts:
|
||||
try:
|
||||
script_name = script_manager.script_name
|
||||
#获取脚本路径
|
||||
script_name = script_component.script_name
|
||||
# 获取脚本路径
|
||||
script_class = script_component.script_instance.__class__
|
||||
script_file = self._get_script_file_path(script_class,script_name)
|
||||
#只有当脚本文件存在时才保存
|
||||
script_file = self._get_script_file_path(script_class, script_name)
|
||||
# 只有当脚本文件存在时才保存
|
||||
if script_file and os.path.exists(script_file):
|
||||
gui_info["scirpts"].append({
|
||||
"name":script_name,
|
||||
"file":script_file
|
||||
gui_info["scripts"].append({
|
||||
"name": script_name,
|
||||
"file": script_file
|
||||
})
|
||||
print(f"收集脚本信息{script_name}from {script_file}")
|
||||
print(f"收集脚本信息: {script_name} from {script_file}")
|
||||
else:
|
||||
print(f"警告: 脚本文件不存在: {script_file}")
|
||||
except Exception as e:
|
||||
print(f"收集单个脚本信息失败{script_name},错误{e}")
|
||||
print(f"收集单个脚本信息失败 {script_name}, 错误: {e}")
|
||||
continue
|
||||
except Exception as e:
|
||||
print(f"收集脚本信息失败{e}")
|
||||
print(f"收集脚本信息失败: {e}")
|
||||
|
||||
print(f"成功收集GUI元素信息: {gui_info}")
|
||||
return gui_info
|
||||
@ -2075,7 +2074,8 @@ class SceneManager:
|
||||
return task.done
|
||||
|
||||
taskMgr.doMethodLater(0.1, load_2d_video_file_task, 'load2DVideoFileTask')
|
||||
|
||||
elif gui_type == "info_panel":
|
||||
new_element = self.world.info_panel_manager.onCreateSampleInfoPanel()
|
||||
# 如果创建成功,设置属性
|
||||
if new_element:
|
||||
# 如果返回的是列表(多选创建),取第一个
|
||||
|
||||
@ -12,6 +12,7 @@ import json
|
||||
|
||||
from direct.actor.Actor import Actor
|
||||
from panda3d.core import TextNode, CardMaker, TextureStage, NodePath, Texture, TransparencyAttrib
|
||||
from core.InfoPanelManager import InfoPanelManager
|
||||
# 获取渲染管线路径
|
||||
# 在文件开头添加sys导入(如果还没有的话)
|
||||
import sys
|
||||
@ -33,52 +34,88 @@ os.chdir(project_root)
|
||||
render_pipeline_path = 'RenderPipelineFile'
|
||||
sys.path.insert(0, render_pipeline_path)
|
||||
|
||||
# 修改管线路径查找
|
||||
# 改进路径设置逻辑
|
||||
pipeline_path = os.path.join(project_root, "RenderPipelineFile")
|
||||
if not os.path.isfile(os.path.join(pipeline_path, "setup.py")):
|
||||
pipeline_path = os.path.join(project_root, "RenderPipelineFile")
|
||||
if os.path.exists(pipeline_path) and pipeline_path not in sys.path:
|
||||
sys.path.insert(0, pipeline_path)
|
||||
# 同时添加子目录以确保所有模块都能正确导入
|
||||
for root, dirs, files in os.walk(pipeline_path):
|
||||
if root not in sys.path:
|
||||
sys.path.insert(0, root)
|
||||
|
||||
|
||||
import math
|
||||
from random import random, randint, seed
|
||||
from panda3d.core import Vec3, load_prc_file_data, Filename
|
||||
from direct.showbase.ShowBase import ShowBase
|
||||
|
||||
from direct.task.TaskManagerGlobal import taskMgr
|
||||
|
||||
# os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
||||
|
||||
from core.script_system import ScriptManager
|
||||
|
||||
class MainApp(ShowBase):
|
||||
def __init__(self):
|
||||
# 在调用父类构造函数前确保必要的属性存在
|
||||
if not hasattr(self, 'appRunner'):
|
||||
self.appRunner = None
|
||||
|
||||
load_prc_file_data("", """
|
||||
win-size 1380 750
|
||||
window-title Render
|
||||
""")
|
||||
|
||||
pipeline_path = "../../"
|
||||
# 简化 sys.path 设置逻辑
|
||||
pipeline_path = os.path.join(project_root, "RenderPipelineFile")
|
||||
if os.path.exists(pipeline_path):
|
||||
if pipeline_path not in sys.path:
|
||||
sys.path.insert(0, pipeline_path)
|
||||
else:
|
||||
print(f"错误: 找不到渲染管线目录: {pipeline_path}")
|
||||
return
|
||||
|
||||
if not os.path.isfile(os.path.join(pipeline_path, "setup.py")):
|
||||
pipeline_path = "../../RenderPipeline"
|
||||
try:
|
||||
from rpcore import RenderPipeline
|
||||
self.render_pipeline = RenderPipeline()
|
||||
self.render_pipeline.create(self)
|
||||
#self.render_pipeline.pre_show_base_init()
|
||||
#ShowBase.__init__(self)
|
||||
|
||||
sys.path.insert(0, pipeline_path)
|
||||
|
||||
from rpcore import RenderPipeline, SpotLight
|
||||
self.render_pipeline = RenderPipeline()
|
||||
self.render_pipeline.create(self)
|
||||
except ImportError as e:
|
||||
|
||||
print(f"导入RenderPipeline模块失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
ShowBase.__init__(self)
|
||||
self.render_pipeline = None
|
||||
return
|
||||
|
||||
self.script_manager = ScriptManager(self)
|
||||
self.script_manager.start_system()
|
||||
|
||||
# 加载所有脚本
|
||||
self.script_manager.load_all_scripts_from_directory()
|
||||
|
||||
self.info_panel_manager = InfoPanelManager(self)
|
||||
|
||||
try:
|
||||
# 再导入controller模块
|
||||
from rpcore.util.movement_controller import MovementController
|
||||
self.controller = MovementController(self)
|
||||
self.controller.set_initial_position(
|
||||
Vec3(0, -50, 20), Vec3(0, 0, 0))
|
||||
self.controller.setup()
|
||||
except ImportError as e:
|
||||
print(f"导入MovementController失败: {e}")
|
||||
self.controller = None
|
||||
|
||||
from rpcore.util.movement_controller import MovementController
|
||||
|
||||
self.render_pipeline.daytime_mgr.time = "12:00"
|
||||
self._loadFont()
|
||||
|
||||
self.loadFullScene()
|
||||
self.loadGUIFromJSON()
|
||||
|
||||
self.controller = MovementController(self)
|
||||
self.controller.set_initial_position(
|
||||
Vec3(0, -50, 20), Vec3(0, 0, 0))
|
||||
self.controller.setup()
|
||||
|
||||
base.accept("l", self.tour)
|
||||
if hasattr(self, 'accept'):
|
||||
base.accept("l", self.tour)
|
||||
|
||||
def _loadFont(self):
|
||||
"""加载中文字体"""
|
||||
@ -97,15 +134,20 @@ class MainApp(ShowBase):
|
||||
"""获取中文字体"""
|
||||
return self.chinese_font
|
||||
|
||||
def getResourcePath(self,relative_path):
|
||||
if getattr(sys,'frozen',False):
|
||||
base_path = os.path.dirname(sys.executable)
|
||||
else:
|
||||
base_path = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
return os.path.join(base_path,relative_path)
|
||||
|
||||
def loadFullScene(self):
|
||||
"""加载完整场景,包括所有元素"""
|
||||
if not hasattr(self, 'loader') or not hasattr(self, 'render'):
|
||||
print("错误: Panda3D核心组件未正确初始化")
|
||||
return
|
||||
try:
|
||||
import sys
|
||||
# 获取场景文件路径
|
||||
if getattr(sys, 'frozen', False):
|
||||
scene_file = os.path.join(os.path.dirname(sys.executable), "scene.bam")
|
||||
else:
|
||||
scene_file = "scene.bam"
|
||||
scene_file = self.getResourcePath("scene.bam")
|
||||
|
||||
if os.path.exists(scene_file):
|
||||
# 使用readBamFile加载完整场景
|
||||
@ -118,6 +160,9 @@ class MainApp(ShowBase):
|
||||
self.render_pipeline.prepare_scene(scene)
|
||||
print("✓ 完整场景加载成功")
|
||||
|
||||
# 检测并播放模型动画
|
||||
#self._processModelAnimations(scene)
|
||||
|
||||
# 处理场景中的各种元素
|
||||
self.processSceneElements(scene)
|
||||
else:
|
||||
@ -129,6 +174,58 @@ class MainApp(ShowBase):
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def _processModelAnimations(self, node_path):
|
||||
"""处理节点中的动画模型"""
|
||||
try:
|
||||
# 查找场景中所有可能的动画模型
|
||||
char_nodes = node_path.findAllMatches("**/+Character")
|
||||
|
||||
for char_node in char_nodes:
|
||||
try:
|
||||
# 获取父节点(通常是模型根节点)
|
||||
model_root = char_node.getParent()
|
||||
model_name = model_root.getName()
|
||||
|
||||
print(f"检测到可能的动画模型: {model_name}")
|
||||
|
||||
# 尝试创建Actor
|
||||
actor = Actor(model_root)
|
||||
actor.reparentTo(self.render)
|
||||
|
||||
actor.setPos(node_path.getPos())
|
||||
actor.setHpr(node_path.getHpr())
|
||||
actor.setScale(node_path.getScale())
|
||||
|
||||
|
||||
|
||||
# 获取动画名称
|
||||
anim_names = actor.getAnimNames()
|
||||
if anim_names:
|
||||
print(f"✓ 成功创建动画模型: {model_name}")
|
||||
print(f" 可用动画: {anim_names}")
|
||||
|
||||
# 循环播放所有动画
|
||||
for anim_name in anim_names:
|
||||
print(f" 循环播放动画: {anim_name}")
|
||||
actor.loop(anim_name)
|
||||
break # 只播放第一个动画,避免同时播放多个动画
|
||||
|
||||
# 替换原始模型
|
||||
model_root.detachNode()
|
||||
|
||||
else:
|
||||
# 没有动画,使用原始模型
|
||||
print(f"模型 {model_name} 不包含动画")
|
||||
actor.detachNode() # 移除创建的Actor
|
||||
|
||||
except Exception as e:
|
||||
print(f"处理动画模型 {char_node.getName()} 时出错: {str(e)}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"处理模型动画时出错: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def processSceneElements(self, scene):
|
||||
"""处理场景中的各种元素"""
|
||||
try:
|
||||
@ -137,6 +234,14 @@ class MainApp(ShowBase):
|
||||
def processNode(nodePath,depth=0):
|
||||
loaded_nodes[nodePath.getName()] = nodePath
|
||||
|
||||
if nodePath.hasTag("scripts_info"):
|
||||
try:
|
||||
import json
|
||||
scripts_info = json.loads(nodePath.getTag("scripts_info"))
|
||||
self.processScripts(nodePath,scripts_info)
|
||||
except Exception as e:
|
||||
print(f"处理节点 {nodePath.getName()} 的脚本时出错: {str(e)}")
|
||||
|
||||
if nodePath.hasTag("light_type"):
|
||||
light_type = nodePath.getTag("light_type")
|
||||
|
||||
@ -238,20 +343,16 @@ class MainApp(ShowBase):
|
||||
self.controller.play_motion_path(mopath, 3.0)
|
||||
|
||||
def loadGUIFromJSON(self):
|
||||
import sys
|
||||
# 获取GUI元素JSON文件路径
|
||||
if getattr(sys, 'frozen', False):
|
||||
gui_json_path = os.path.join(os.path.dirname(sys.executable), "gui", "gui_elements.json")
|
||||
else:
|
||||
gui_json_path = "gui/gui_elements.json"
|
||||
|
||||
try:
|
||||
gui_json_path = self.getResourcePath("gui/gui_elements.json")
|
||||
if os.path.exists(gui_json_path):
|
||||
with open(gui_json_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read().strip()
|
||||
if content:
|
||||
gui_data = json.loads(content)
|
||||
self.createGUIElement(gui_data)
|
||||
else:
|
||||
print("GUI配置文件为空 ")
|
||||
except Exception as e:
|
||||
print(f"加载GUI元素时出错: {str(e)}")
|
||||
import traceback
|
||||
@ -354,6 +455,12 @@ class MainApp(ShowBase):
|
||||
video_path=video_path,
|
||||
size=absolute_scale
|
||||
)
|
||||
elif gui_type == "info_panel":
|
||||
new_element = self.info_panel_manager.onCreateSampleInfoPanel()
|
||||
|
||||
if "scripts" in gui_info and new_element:
|
||||
self.processScripts(new_element,gui_info["scripts"])
|
||||
|
||||
except Exception as e:
|
||||
print(f"重建GUI元素失败 {name}: {e}")
|
||||
import traceback
|
||||
@ -504,6 +611,12 @@ class MainApp(ShowBase):
|
||||
traceback.print_exc()
|
||||
|
||||
def createGUI2DImage(self, pos=(0, 0, 0), image_path=None, size=0.2):
|
||||
# 添加属性检查
|
||||
if not hasattr(self, 'aspect2d'):
|
||||
print("错误: aspect2d未初始化")
|
||||
return None
|
||||
if image_path and not os.path.isabs(image_path):
|
||||
image_path = self.getResourcePath(image_path)
|
||||
# 处理非均匀缩放
|
||||
if isinstance(size, (list, tuple)) and len(size) >= 2:
|
||||
# 分别处理宽度和高度的缩放
|
||||
@ -542,8 +655,12 @@ class MainApp(ShowBase):
|
||||
print(f"无法加载图片: {image_path}")
|
||||
except Exception as e:
|
||||
print(f"加载图片时出错: {e}")
|
||||
return image_node
|
||||
|
||||
def createGUI2DVideoScreen(self, pos=(0, 0, 0), size=0.2, video_path=None):
|
||||
import os
|
||||
if video_path and not os.path.isabs(video_path):
|
||||
image_path = self.getResourcePath(video_path)
|
||||
from direct.gui.DirectGui import DirectFrame
|
||||
from panda3d.core import TransparencyAttrib, Texture, TextureStage
|
||||
import os
|
||||
@ -635,16 +752,53 @@ class MainApp(ShowBase):
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def processScripts(self, element, script_info_list):
|
||||
"""处理元素上挂载的脚本 - 使用新的脚本系统"""
|
||||
try:
|
||||
print(f"正在为元素 {element.getName()} 挂载脚本")
|
||||
print(f"可用脚本列表: {self.script_manager.get_available_scripts()}")
|
||||
|
||||
for script_info in script_info_list:
|
||||
script_name = script_info["name"]
|
||||
script_file = script_info.get("file", "")
|
||||
|
||||
# 从文件路径中提取脚本类名
|
||||
if script_file:
|
||||
# 获取脚本文件名(不含路径和扩展名)
|
||||
script_filename = os.path.basename(script_file)
|
||||
script_class_name = os.path.splitext(script_filename)[0]
|
||||
print(f"尝试挂载脚本: {script_class_name} (来自文件: {script_file})")
|
||||
|
||||
# 使用脚本管理器为元素添加脚本
|
||||
script_component = self.script_manager.add_script_to_object(element, script_class_name)
|
||||
|
||||
if script_component:
|
||||
print(f"✓ 脚本 {script_class_name} 已挂载到元素 {element.getName()}")
|
||||
else:
|
||||
print(f"⚠️ 脚本 {script_class_name} 挂载失败")
|
||||
# 列出可用脚本帮助调试
|
||||
available_scripts = self.script_manager.get_available_scripts()
|
||||
print(f"当前可用脚本: {available_scripts}")
|
||||
else:
|
||||
print(f"⚠️ 脚本信息不完整: {script_name}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"挂载脚本到元素 {element.getName()} 失败: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
# 在 main.py 的最后部分,修改为:
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
app = MainApp()
|
||||
app.run()
|
||||
if hasattr(app, 'run'):
|
||||
app.run()
|
||||
else:
|
||||
print("应用程序初始化失败")
|
||||
except Exception as e:
|
||||
print(f"应用程序启动失败: {str(e)}")
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
# 在Windows上可以使用下面的代码显示错误对话框
|
||||
# input("按回车键退出...")
|
||||
|
||||
|
||||
|
||||
@ -734,7 +734,7 @@ class MainWindow(QMainWindow):
|
||||
self.infoPanelMenu = menubar.addMenu('信息面板')
|
||||
# 创建示例面板动作
|
||||
self.createSamplePanelAction = self.infoPanelMenu.addAction('创建示例面板')
|
||||
self.createSamplePanelAction.triggered.connect(self.onCreateSampleInfoPanel)
|
||||
self.createSamplePanelAction.triggered.connect(self.world.info_panel_manager.onCreateSampleInfoPanel)
|
||||
# 添加更多面板创建选项
|
||||
# self.createSystemStatusPanelAction = self.infoPanelMenu.addAction('创建系统状态面板')
|
||||
# self.createSystemStatusPanelAction.triggered.connect(self.onCreateSystemStatusPanel)
|
||||
@ -2493,113 +2493,7 @@ class MainWindow(QMainWindow):
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
def onCreateSampleInfoPanel(self):
|
||||
"""创建示例天气信息面板(模拟数据)"""
|
||||
try:
|
||||
# 获取中文字体
|
||||
from panda3d.core import TextNode
|
||||
font = self.world.getChineseFont() if self.world.getChineseFont() else None
|
||||
|
||||
# 创建面板
|
||||
info_manager = self.world.info_panel_manager
|
||||
info_manager.setParent(aspect2d)
|
||||
|
||||
# 使用唯一的面板ID
|
||||
import time
|
||||
unique_id = f"weather_info_{int(time.time())}"
|
||||
|
||||
# 创建示例面板
|
||||
weather_panel = info_manager.createInfoPanel(
|
||||
panel_id=unique_id, # 使用唯一ID
|
||||
position=(1.32, 0.68),
|
||||
size=(1, 0.6),
|
||||
bg_color=(0.15, 0.25, 0.35, 0), # 蓝色背景
|
||||
border_color=(0.3, 0.5, 0.7, 0), # 蓝色边框
|
||||
title_color=(0.7, 0.9, 1.0, 1.0), # 浅蓝色标题
|
||||
content_color=(0.95, 0.95, 0.95, 1.0),
|
||||
font=font,
|
||||
bg_image="/home/tiger/图片/内部信息框2@2x.png"
|
||||
)
|
||||
|
||||
# 更新面板标题
|
||||
info_manager.updatePanelContent(unique_id, title="北京天气")
|
||||
|
||||
# 添加到场景树
|
||||
self.addInfoPanelToTree(weather_panel, "天气信息面板")
|
||||
|
||||
# 立即显示加载中信息
|
||||
info_manager.updatePanelContent(unique_id, content="正在获取天气数据...")
|
||||
|
||||
info_manager.registerDataSource(unique_id, self.getRealWeatherData, update_interval=5.0)
|
||||
|
||||
# # 立即显示示例数据
|
||||
# sample_data = self.getSampleWeatherData()
|
||||
# info_manager.updatePanelContent(unique_id, content=sample_data)
|
||||
#
|
||||
# # 注册数据源,定期更新示例数据
|
||||
# info_manager.registerDataSource(unique_id, self.getSampleWeatherData, update_interval=2.0)
|
||||
|
||||
print("✓ 示例天气信息面板已创建")
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ 创建示例天气信息面板失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
QMessageBox.critical(self, "错误", f"创建示例天气信息面板时出错: {str(e)}")
|
||||
|
||||
def getRealWeatherData(self):
|
||||
"""获取真实天气数据"""
|
||||
try:
|
||||
import requests
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
# 请求天气数据
|
||||
url = "https://wttr.in/Beijing?format=j1"
|
||||
response = requests.get(url, timeout=10)
|
||||
response.raise_for_status()
|
||||
|
||||
# 解析JSON数据
|
||||
weather_data = response.json()
|
||||
|
||||
# 提取当前天气信息
|
||||
current_condition = weather_data['current_condition'][0]
|
||||
weather_desc = current_condition['weatherDesc'][0]['value']
|
||||
temp_c = current_condition['temp_C']
|
||||
feels_like = current_condition['FeelsLikeC']
|
||||
humidity = current_condition['humidity']
|
||||
pressure = current_condition['pressure']
|
||||
visibility = current_condition['visibility']
|
||||
wind_speed = current_condition['windspeedKmph']
|
||||
wind_dir = current_condition['winddir16Point']
|
||||
|
||||
# 提取空气质量(如果可用)
|
||||
air_quality = "N/A"
|
||||
if 'air_quality' in weather_data and weather_data['air_quality']:
|
||||
if 'us-epa-index' in current_condition:
|
||||
air_quality_index = current_condition['air_quality_index']
|
||||
air_quality = f"指数: {air_quality_index}"
|
||||
|
||||
# 获取更新时间
|
||||
update_time = datetime.now().strftime("%Y-%m-%d %H:%M")
|
||||
|
||||
# 格式化显示内容
|
||||
content = f"天气状况: {weather_desc}\n温度: {temp_c}°C (体感 {feels_like}°C)\n湿度: {humidity}%\n气压: {pressure} hPa\n能见度: {visibility} km\n风速: {wind_speed} km/h ({wind_dir})\n空气质量: {air_quality}\n更新时间: {update_time}"
|
||||
|
||||
return content
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
return "错误: 获取天气数据超时"
|
||||
except requests.exceptions.ConnectionError:
|
||||
return "错误: 网络连接失败"
|
||||
except requests.exceptions.HTTPError as e:
|
||||
return f"HTTP错误: {e}"
|
||||
except json.JSONDecodeError:
|
||||
return "错误: 无法解析天气数据"
|
||||
except KeyError as e:
|
||||
return f"错误: 天气数据格式不正确 (缺少字段: {e})"
|
||||
except Exception as e:
|
||||
return f"获取天气数据失败: {str(e)}"
|
||||
|
||||
def getSampleWeatherData(self):
|
||||
"""获取示例天气数据"""
|
||||
@ -2972,17 +2866,6 @@ class MainWindow(QMainWindow):
|
||||
except Exception as e:
|
||||
return f"获取场景信息失败: {str(e)}"
|
||||
|
||||
def onCreateAllInfoPanels(self):
|
||||
"""创建所有信息面板"""
|
||||
try:
|
||||
self.onCreateSampleInfoPanel()
|
||||
self.onCreateSystemStatusPanel()
|
||||
self.onCreateSensorDataPanel()
|
||||
self.onCreateSceneInfoPanel()
|
||||
QMessageBox.information(self, "成功",
|
||||
"所有信息面板已创建完成!\n快捷键:\nF1 - 示例面板\nF2 - 系统状态面板\nF3 - 传感器数据面板\nF4 - 场景信息面板")
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "错误", f"创建信息面板时出错: {str(e)}")
|
||||
|
||||
# ==================== 脚本管理事件处理 ====================
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user