diff --git a/RenderPipelineFile/rpcore/render_pipeline.py b/RenderPipelineFile/rpcore/render_pipeline.py index a7ac29df..e4c46dbb 100644 --- a/RenderPipelineFile/rpcore/render_pipeline.py +++ b/RenderPipelineFile/rpcore/render_pipeline.py @@ -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: diff --git a/core/InfoPanelManager.py b/core/InfoPanelManager.py index 35000ffe..786a4763 100644 --- a/core/InfoPanelManager.py +++ b/core/InfoPanelManager.py @@ -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(): diff --git a/gui/gui_manager.py b/gui/gui_manager.py index d4c3a84d..ba9febdd 100644 --- a/gui/gui_manager.py +++ b/gui/gui_manager.py @@ -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 diff --git a/main.py b/main.py index ba87d0cf..8376d4a5 100644 --- a/main.py +++ b/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() diff --git a/project/project_manager.py b/project/project_manager.py index a78704e8..75e83517 100644 --- a/project/project_manager.py +++ b/project/project_manager.py @@ -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', ], }}, diff --git a/scene/scene_manager.py b/scene/scene_manager.py index a8004d45..fd9fadc0 100644 --- a/scene/scene_manager.py +++ b/scene/scene_manager.py @@ -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: # 如果返回的是列表(多选创建),取第一个 diff --git a/templates/main_template.py b/templates/main_template.py index cb1ec1a1..2c6ce33b 100644 --- a/templates/main_template.py +++ b/templates/main_template.py @@ -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("按回车键退出...") + diff --git a/ui/main_window.py b/ui/main_window.py index daf4096e..a05d0ae9 100644 --- a/ui/main_window.py +++ b/ui/main_window.py @@ -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)}") # ==================== 脚本管理事件处理 ====================