EG/project/project_manager.py
2026-03-06 09:52:57 +08:00

891 lines
34 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import sys
import json
import datetime
import subprocess
import shutil
from project.webgl_packager import WebGLPackager
class ProjectManager:
def __init__(self, world):
self.world = world
self.current_project_path = None
self.project_config = None
self.last_webgl_export_report = None
print("✓ 项目管理系统初始化完成")
# ==================== 项目生命周期管理 ====================
def createNewProject(self, project_path, project_name):
"""创建新项目
Args:
project_path: 项目路径
project_name: 项目名称
Returns:
bool: 创建是否成功
"""
if not project_path or not project_name:
print("错误: 项目路径和名称不能为空")
return False
# full_project_path = os.path.join(project_path, project_name)
full_project_path = os.path.normpath(os.path.join(project_path, project_name))
print(f"full_project_path: {full_project_path}")
try:
# 创建项目文件夹结构
os.makedirs(full_project_path)
os.makedirs(os.path.join(full_project_path, "models")) # 模型文件夹
os.makedirs(os.path.join(full_project_path, "textures")) # 贴图文件夹
scenes_path = os.path.join(full_project_path, "scenes") # 场景文件夹
os.makedirs(scenes_path)
# 创建项目配置文件
project_config = {
"name": project_name,
"path": full_project_path,
"created_at": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"last_modified": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"version": "1.0.0",
"engine_version": "1.0.0"
}
config_file = os.path.join(full_project_path, "project.json")
with open(config_file, "w", encoding="utf-8") as f:
json.dump(project_config, f, ensure_ascii=False, indent=4)
print(f"项目配置文件已创建: {config_file}")
# 清空当前场景
self._clearCurrentScene()
# 自动保存初始场景
scene_file = os.path.join(scenes_path, "scene.bam")
if self.world.scene_manager.saveScene(scene_file, project_path):
# 更新配置文件中的场景路径
project_config["scene_file"] = os.path.relpath(scene_file, full_project_path)
with open(config_file, "w", encoding="utf-8") as f:
json.dump(project_config, f, ensure_ascii=False, indent=4)
print(f"初始场景已保存到: {scene_file}")
else:
print("初始场景保存失败")
# 更新项目状态
self.current_project_path = full_project_path
self.project_config = project_config
print(f"项目 '{project_name}' 创建成功!")
return True
except Exception as e:
print(f"创建项目失败: {str(e)}")
return False
def openProject(self, project_path):
"""打开项目
Args:
project_path: 项目路径
Returns:
bool: 打开是否成功
"""
# print(f"\n[DEBUG] ===== 开始打开项目: {project_path} =====")
try:
if not project_path:
print("错误: 项目路径不能为空")
return False
# 检查是否是有效的项目文件夹
config_file = os.path.join(project_path, "project.json")
# print(f"[DEBUG] 项目配置文件路径: {config_file}")
# print(f"[DEBUG] 配置文件是否存在: {os.path.exists(config_file)}")
if not os.path.exists(config_file):
print("错误: 选择的不是有效的项目文件夹!")
return False
# 读取项目配置
# print(f"[DEBUG] 开始读取项目配置文件...")
with open(config_file, "r", encoding="utf-8") as f:
project_config = json.load(f)
# print(f"[DEBUG] 项目配置读取成功: {project_config.get('name', 'Unknown')}")
# 检查场景文件
scene_file = os.path.join(project_path, "scenes", "scene.bam")
# print(f"[DEBUG] 场景文件路径: {scene_file}")
# print(f"[DEBUG] 场景文件是否存在: {os.path.exists(scene_file)}")
if os.path.exists(scene_file):
# 加载场景前的安全检查
# print(f"[DEBUG] 开始加载场景文件...")
# print(f"[DEBUG] 当前world状态检查:")
# print(f"[DEBUG] - render节点存在: {hasattr(self.world, 'render') and self.world.render is not None}")
# print(f"[DEBUG] - loader存在: {hasattr(self.world, 'loader') and self.world.loader is not None}")
# print(f"[DEBUG] - scene_manager存在: {hasattr(self.world, 'scene_manager') and self.world.scene_manager is not None}")
# 检查world的基本状态
if not hasattr(self.world, 'render') or self.world.render is None:
# print(f"[DEBUG] 错误: world.render不存在或为None")
return False
if not hasattr(self.world, 'loader') or self.world.loader is None:
# print(f"[DEBUG] 错误: world.loader不存在或为None")
return False
# 清理可能的残留状态
try:
# 清理选择状态
if hasattr(self.world, 'selection') and self.world.selection:
self.world.selection.clearSelection()
# print(f"[DEBUG] 选择状态已清理")
# 清理GUI状态
if hasattr(self.world, 'gui_elements'):
for gui_elem in self.world.gui_elements:
if gui_elem and not gui_elem.isEmpty():
gui_elem.detachNode()
self.world.gui_elements.clear()
# print(f"[DEBUG] GUI元素已清理")
# 验证BAM文件
# print(f"[DEBUG] 验证BAM文件完整性...")
if os.path.getsize(scene_file) == 0:
# print(f"[DEBUG] 错误: BAM文件为空")
return False
# 检查文件权限
if not os.access(scene_file, os.R_OK):
# print(f"[DEBUG] 错误: BAM文件不可读")
return False
# print(f"[DEBUG] BAM文件验证通过")
except Exception as e:
# print(f"[DEBUG] 清理状态时发生异常: {e}")
return False
if self.world.scene_manager.loadScene(scene_file):
# print(f"[DEBUG] 场景加载成功")
# 更新项目配置
project_config["scene_file"] = os.path.relpath(scene_file, project_path)
else:
# print(f"[DEBUG] 场景加载失败")
return False
else:
# print(f"[DEBUG] 场景文件不存在,跳过场景加载")
pass
project_config["last_modified"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
with open(config_file, "w", encoding="utf-8") as f:
json.dump(project_config, f, ensure_ascii=False, indent=4)
# 更新项目状态
self.current_project_path = project_path
self.project_config = project_config
# print(f"[DEBUG] ===== 项目打开成功: {project_path} =====")
print("项目加载成功!")
return True
except Exception as e:
# print(f"[DEBUG] ===== 项目打开失败 =====")
print(f"加载项目时发生错误:{str(e)}")
import traceback
traceback.print_exc()
return False
def openProjectForPath(self, project_path):
"""通过路径打开项目
Args:
project_path: 项目路径
Returns:
bool: 打开是否成功
"""
try:
if not project_path:
return False
# 检查是否是有效的项目文件夹
config_file = os.path.join(project_path, "project.json")
if not os.path.exists(config_file):
print("警告: 选择的不是有效的项目文件夹!")
return False
# 读取项目配置
with open(config_file, "r", encoding="utf-8") as f:
project_config = json.load(f)
# 检查场景文件
scene_file = os.path.join(project_path, "scenes", "scene.bam")
if os.path.exists(scene_file):
# 加载场景
if self.world.scene_manager.loadScene(scene_file):
# 更新项目配置
project_config["scene_file"] = os.path.relpath(scene_file, project_path)
project_config["last_modified"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
with open(config_file, "w", encoding="utf-8") as f:
json.dump(project_config, f, ensure_ascii=False, indent=4)
# 更新项目状态
self.current_project_path = project_path
self.project_config = project_config
print(f"项目 '{project_path}' 加载成功!")
return True
except Exception as e:
error_msg = f"加载项目时发生错误:{str(e)}"
print(error_msg)
return False
def saveProject(self):
"""保存项目
Returns:
bool: 保存是否成功
"""
try:
# 检查是否有当前项目路径
if not self.current_project_path:
print("错误: 请先创建或打开一个项目!")
return False
project_path = self.current_project_path
scenes_path = os.path.join(project_path, "scenes")
# 固定的场景文件名
scene_file = os.path.join(scenes_path, "scene.bam")
# 如果存在旧文件,先删除
if os.path.exists(scene_file):
try:
os.remove(scene_file)
print(f"已删除旧场景文件: {scene_file}")
except Exception as e:
print(f"删除旧场景文件失败: {str(e)}")
return False
# 保存场景
if self.world.scene_manager.saveScene(scene_file, project_path):
# 更新项目配置文件
config_file = os.path.join(project_path, "project.json")
if os.path.exists(config_file):
with open(config_file, "r", encoding="utf-8") as f:
project_config = json.load(f)
# 更新最后修改时间
project_config["last_modified"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 记录场景文件路径
project_config["scene_file"] = os.path.relpath(scene_file, project_path)
with open(config_file, "w", encoding="utf-8") as f:
json.dump(project_config, f, ensure_ascii=False, indent=4)
# 更新项目配置
self.project_config = project_config
print("项目保存成功!")
return True
else:
print("错误: 保存场景失败!")
return False
except Exception as e:
print(f"保存项目时发生错误:{str(e)}")
return False
# ==================== 项目打包功能 ====================
def buildWebGLPackage(self, output_dir):
"""将当前项目打包为 WebGL 静态站点目录。
Args:
output_dir (str): 用户选择的输出根目录
Returns:
bool: success/partial 返回 Truefailed 返回 False
"""
try:
if not self.current_project_path:
print("错误: 请先创建或打开一个项目!")
return False
if not output_dir:
print("错误: 请指定 WebGL 打包输出目录!")
return False
project_path = os.path.normpath(self.current_project_path)
output_dir = os.path.normpath(output_dir)
packager = WebGLPackager(self.world)
report = packager.package(project_path, output_dir)
self.last_webgl_export_report = report
status = report.get("status", "failed")
report_path = os.path.join(
report.get("output_dir", ""),
"reports",
"export_report.json",
)
if status in ("success", "partial"):
print(f"WebGL打包完成: {status}")
print(f"输出目录: {report.get('output_dir', '')}")
print(f"报告路径: {report_path}")
return True
print("WebGL打包失败")
print(f"报告路径: {report_path}")
return False
except Exception as e:
print(f"WebGL打包过程出错: {str(e)}")
return False
def buildPackage(self, build_dir):
"""打包项目为可执行文件 - 按照Panda3D官方标准方法
Args:
build_dir: 打包输出目录
Returns:
bool: 打包是否成功
"""
try:
# 检查是否有当前项目路径
if not self.current_project_path:
print("错误: 请先创建或打开一个项目!")
return False
project_path = self.current_project_path
scenes_path = os.path.join(project_path, "scenes")
scene_file = os.path.join(scenes_path, "scene.bam")
# 检查场景文件是否存在
if not os.path.exists(scene_file):
print("错误: 请先保存场景!")
return False
if hasattr(self.world,'selection') and self.world.selection:
self.world.selection.clearSelection()
print("已取消场景中的物体选中状态")
if self.world.scene_manager.saveScene(scene_file, project_path):
print("场景保存成功")
config_file = os.path.join(project_path,"project.json")
if os.path.exists(config_file):
with open(config_file,"r",encoding="utf-8") as f:
project_config = json.load(f)
project_config["last_modified"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
project_config["scene_file"] = os.path.relpath(scene_file,project_path)
with open(config_file,"w",encoding = "utf-8") as f:
json.dump(project_config,f,ensure_ascii = False,indent=4)
self.project_config = project_config
project_name = os.path.basename(project_path)
else:
print("错误: 保存场景失败!")
return False
if not build_dir:
print("错误: 请指定打包输出目录!")
return False
# 创建构建目录
build_dir = os.path.join(build_dir, "build")
if not os.path.exists(build_dir):
os.makedirs(build_dir)
# 创建标准的打包文件
self._createStandardBuildFiles(build_dir, project_path, scene_file)
# 执行打包命令
success = self._executeStandardBuild(build_dir)
if success:
# 根据操作系统创建对应的启动脚本文件
try:
import stat
# 检查操作系统类型
if os.name == 'nt': # Windows系统
run_bat_path = os.path.join(build_dir, "run.bat")
with open(run_bat_path, "w") as f:
f.write("@echo off\n")
f.write("python main.py %*\n")
else: # Unix-like系统 (Linux, macOS等)
run_sh_path = os.path.join(build_dir, "run.sh")
with open(run_sh_path, "w") as f:
f.write("#!/bin/bash\n")
f.write("python3.10 main.py \"$@\"\n")
# 为Unix-like系统添加执行权限
st = os.stat(run_sh_path)
os.chmod(run_sh_path, st.st_mode | stat.S_IEXEC)
except Exception as e:
print(f"创建启动脚本文件失败: {str(e)}")
print("打包完成!可执行文件在 build 目录中。")
return True
else:
return False
except Exception as e:
print(f"打包过程出错:{str(e)}")
return False
def _createStandardBuildFiles(self, build_dir, project_path, scene_file):
"""创建标准的Panda3D打包文件"""
project_name = os.path.basename(project_path)
# 确保构建目录存在
if not os.path.exists(build_dir):
os.makedirs(build_dir)
# 复制场景文件到构建目录
shutil.copy2(scene_file, os.path.join(build_dir, "scene.bam"))
# 复制Resources文件夹到build目录
source_resources = os.path.join(project_path, "scenes", "resources")
self.copy_folder(source_resources, build_dir)
self._saveGUIElementsToJSON(build_dir, project_path)
self._copyScriptsToBuild(build_dir, project_path)
self._copyScriptSystemToBuild(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")
if os.path.exists(source_render_pipeline):
if os.path.exists(dest_render_pipeline):
shutil.rmtree(dest_render_pipeline)
shutil.copytree(
source_render_pipeline,
dest_render_pipeline,
ignore=shutil.ignore_patterns('__pycache__','*.pyc','.git','.vscode','*.log','samples')
)
print("✓ RenderPipelineFile文件夹已复制到build目录")
else:
print("⚠️ RenderPipelineFile文件夹未找到")
# 创建标准的应用程序入口文件
self._createAppFile(build_dir, project_name)
# 创建标准的setup.py文件
self._createStandardSetupFile(build_dir, project_name)
#创建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_files = [
"script_system.py",
"CustomMouseController.py"
]
source_core_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),"core")
core_dest = os.path.join(build_dir,"core")
if not os.path.exists(core_dest):
os.makedirs(core_dest)
for file_name in core_files:
source_file = os.path.join(source_core_dir,file_name)
if os.path.exists(source_file):
shutil.copy2(source_file,os.path.join(core_dest,file_name))
def _saveGUIElementsToJSON(self, build_dir, project_path):
"""保存GUI元素到JSON文件内容与_collectGUIElementInfo保持一致"""
try:
# 创建目标gui目录
gui_dest = os.path.join(build_dir, "gui")
if not os.path.exists(gui_dest):
os.makedirs(gui_dest)
# 收集所有GUI元素信息
gui_data = []
# 获取当前场景中的GUI元素
if hasattr(self.world, 'gui_elements'):
for gui_node in self.world.gui_elements:
if gui_node and not gui_node.isEmpty():
# 使用_collectGUIElementInfo方法收集信息
gui_info = self.world.scene_manager._collectGUIElementInfo(gui_node)
if gui_info:
self._updateResourcePaths(gui_info,project_path,build_dir)
gui_data.append(gui_info)
print(f"收集GUI元素信息: {gui_info['name']}")
# 保存GUI信息到JSON文件
gui_file_path = os.path.join(gui_dest, "gui_elements.json")
with open(gui_file_path, "w", encoding="utf-8") as f:
json.dump(gui_data, f, ensure_ascii=False, indent=4)
print(f"✓ GUI元素数据已保存到 {gui_file_path},共 {len(gui_data)} 个元素")
return True
except Exception as e:
print(f"⚠️ 保存GUI元素时出错: {str(e)}")
import traceback
traceback.print_exc()
return False
def _updateResourcePaths(self,gui_info,project_path,build_dir):
def normalize_resource_path(old_path):
if not old_path:
return old_path
if old_path.startswith(("http://","https://")):
return old_path
if os.path.isabs(old_path):
try:
rel_path = os.path.relpath(old_path,project_path)
return os.path.join("resources",os.path.basename(rel_path)).replace("\\","/")
except Exception:
return os.path.join("resources",os.path.basename(old_path)).replace("\\","/")
else:
return os.path.join("resources",os.path.basename(old_path)).replace("\\","/")
if "image_path" in gui_info and gui_info["image_path"]:
gui_info["image_path"] = normalize_resource_path(gui_info["image_path"])
if "video_path" in gui_info and gui_info["video_path"]:
gui_info["video_path"] = normalize_resource_path(gui_info["video_path"])
if "bg_image_path" in gui_info and gui_info["bg_image_path"]:
gui_info["bg_image_path"] = normalize_resource_path(gui_info["bg_image_path"])
def _createRequirementsFile(self,build_dir):
requirements_content = """panda3d>=1.10.13"""
requirements_path = os.path.join(build_dir,"requirements.txt")
with open(requirements_path,"w",encoding="utf-8") as f:
f.write(requirements_content)
def copy_folder(self, source_folder, destination_folder):
"""将一个文件夹从源路径复制到目标路径下的resources文件夹中
Args:
source_folder (str): 源文件夹路径
destination_folder (str): 目标文件夹路径
"""
try:
# 创建resources文件夹作为目标
resources_dest = os.path.join(destination_folder, "resources")
# 确保目标目录存在
if not os.path.exists(destination_folder):
os.makedirs(destination_folder)
# 如果目标resources文件夹已存在先删除
if os.path.exists(resources_dest):
shutil.rmtree(resources_dest)
# 检查源文件夹是否存在
if os.path.exists(source_folder):
# 复制整个文件夹到resources目录下
shutil.copytree(
source_folder,
resources_dest,
ignore=shutil.ignore_patterns('__pycache__', '*.pyc', '.git', '.vscode', '*.log')
)
print(f"✓ 文件夹已从 {source_folder} 复制到 {resources_dest}")
return True
else:
print(f"⚠️ 源文件夹不存在: {source_folder}")
# 即使源文件夹不存在也创建空的resources目录
if not os.path.exists(resources_dest):
os.makedirs(resources_dest)
return False
except Exception as e:
print(f"⚠️ 复制文件夹时出错: {str(e)}")
return False
def _copyResourcesToBuild(self, build_dir, project_path):
"""复制GUI资源到构建目录的resources文件夹"""
try:
# 创建目标resources目录
resources_dest = os.path.join(build_dir, "resources")
# 源Resources目录
resources_src = os.path.join(project_path, "Resources")
if os.path.exists(resources_src):
# 直接复制整个Resources目录
if os.path.exists(resources_dest):
shutil.rmtree(resources_dest)
shutil.copytree(
resources_src,
resources_dest,
ignore=shutil.ignore_patterns('__pycache__', '*.pyc', '.git', '.vscode', '*.log')
)
print("✓ Resources目录已复制到build/resources")
# 统计复制的文件数量
file_count = 0
for root, dirs, files in os.walk(resources_dest):
file_count += len(files)
print(f"✓ 共复制了 {file_count} 个资源文件")
else:
# 创建空的resources目录
if not os.path.exists(resources_dest):
os.makedirs(resources_dest)
print("⚠️ 项目中没有Resources目录")
except Exception as e:
print(f"⚠️ 复制资源文件时出错: {str(e)}")
def _collectResourceFiles(self, project_path):
"""收集项目中GUI使用的资源文件"""
resource_files = set()
try:
# 收集Resources目录中的所有文件这是最主要的资源来源
resources_dir = os.path.join(project_path, "Resources")
if os.path.exists(resources_dir):
for root, dirs, files in os.walk(resources_dir):
for file in files:
file_path = os.path.join(root, file)
# 收集所有文件,不仅仅是媒体文件
resource_files.add(file_path)
# 同时收集场景中引用的特定资源
scene_file = os.path.join(project_path, "scenes", "scene.bam")
if os.path.exists(scene_file):
# 从场景文件中提取资源引用
referenced_files = self._extractResourcesFromScene(scene_file, project_path)
for file_path in referenced_files:
if os.path.isabs(file_path):
if os.path.exists(file_path):
resource_files.add(file_path)
else:
# 相对路径
full_path = os.path.join(project_path, file_path)
if os.path.exists(full_path):
resource_files.add(full_path)
except Exception as e:
print(f"收集资源文件时出错: {str(e)}")
return list(resource_files)
def _extractResourcesFromScene(self, scene_file, project_path):
"""从场景文件中提取资源引用"""
referenced_files = []
try:
# 这里应该实现从BAM文件中提取贴图、视频等资源引用的逻辑
# 由于直接解析BAM文件比较复杂我们采用间接方式
# 检查项目配置文件或其他元数据文件中可能包含的资源引用
config_file = os.path.join(project_path, "project.json")
if os.path.exists(config_file):
with open(config_file, "r", encoding="utf-8") as f:
project_config = json.load(f)
# 这里可以根据实际需要提取资源引用
except Exception as e:
print(f"从场景文件提取资源引用时出错: {str(e)}")
return referenced_files
# ==================== 辅助方法 ====================
def _clearCurrentScene(self):
"""清空当前场景"""
try:
if hasattr(self.world, 'scene_manager') and self.world.scene_manager:
self.world.scene_manager.clearScene()
print("✓ 当前场景已清空")
except Exception as e:
print(f"清空场景失败: {str(e)}")
def updateWindowTitle(self, parent_window, project_name):
"""更新窗口标题(保留方法以兼容旧代码)"""
# 这个方法现在不需要做任何事情因为我们不再处理UI
pass
def _createAppFile(self, build_dir, project_name):
"""创建标准的应用程序入口文件"""
try:
app_content = f'''#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
{project_name} - Panda3D应用程序
自动生成的入口文件
"""
import sys
import os
# 添加当前目录到Python路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
try:
from panda3d.core import loadPrcFileData
# 设置基本的Panda3D配置
loadPrcFileData("", """
window-title {project_name}
sync-video false
show-frame-rate-meter true
""")
# 导入主应用程序类
# 注意:这里需要根据实际情况修改导入路径
from main import MyWorld
# 创建并运行应用程序
app = MyWorld()
app.run()
except ImportError as e:
print(f"导入错误: {{e}}")
print("请确保已正确安装Panda3D")
sys.exit(1)
except Exception as e:
print(f"运行错误: {{e}}")
sys.exit(1)
'''
app_file = os.path.join(build_dir, "main.py")
with open(app_file, "w", encoding="utf-8") as f:
f.write(app_content)
print(f"✓ 应用程序入口文件已创建: {app_file}")
except Exception as e:
print(f"创建应用程序入口文件失败: {str(e)}")
def _createStandardSetupFile(self, build_dir, project_name):
"""创建标准的setup.py文件用于打包"""
try:
setup_content = f'''#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
{project_name} - Panda3D项目打包配置
"""
from setuptools import setup, find_packages
import sys
# 确保Python版本兼容性
if sys.version_info < (3, 6):
sys.exit("Python 3.6或更高版本是必需的")
setup(
name="{project_name}",
version="1.0.0",
description="基于Panda3D的3D应用程序",
author="",
author_email="",
url="",
# 包含所有Python包
packages=find_packages(),
# 包含非Python文件
include_package_data=True,
package_data={{
"": ["*.bam", "*.egg", "*.png", "*.jpg", "*.jpeg", "*.ogg", "*.wav", "*.mp3"]
}},
# 依赖项
install_requires=[
"panda3d>=1.10.13",
],
# Python版本要求
python_requires=">=3.6",
# 分类信息
classifiers=[
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
],
# 入口点
entry_points={{
"console_scripts": [
"{project_name}=main:main",
],
}},
)
'''
setup_file = os.path.join(build_dir, "setup.py")
with open(setup_file, "w", encoding="utf-8") as f:
f.write(setup_content)
print(f"✓ setup.py文件已创建: {setup_file}")
except Exception as e:
print(f"创建setup.py文件失败: {str(e)}")
def _executeStandardBuild(self, build_dir):
"""执行标准的Panda3D打包命令"""
try:
# 这里可以实现具体的打包逻辑
# 例如使用pdeploy或其他打包工具
print(f"✓ 项目文件已准备到: {build_dir}")
print("注意: 请使用Panda3D的pdeploy工具进行最终打包")
return True
except Exception as e:
print(f"执行打包命令失败: {str(e)}")
return False