ui替换
@ -1,21 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
验证ImGui docking功能是否启用
|
||||
"""
|
||||
from imgui_bundle import imgui
|
||||
|
||||
# 创建一个虚拟的IO对象来检查标志
|
||||
import imgui_bundle
|
||||
print("检查ImGui docking支持...")
|
||||
print(f"ConfigFlags_.docking_enable 存在: {hasattr(imgui.ConfigFlags_, 'docking_enable')}")
|
||||
print(f"dock_space_over_viewport 存在: {hasattr(imgui, 'dock_space_over_viewport')}")
|
||||
|
||||
# 检查docking标志值
|
||||
docking_flag = imgui.ConfigFlags_.docking_enable
|
||||
print(f"Docking标志值: {docking_flag}")
|
||||
|
||||
# 检查窗口标志
|
||||
print(f"WindowFlags_.no_title_bar 存在: {hasattr(imgui.WindowFlags_, 'no_title_bar')}")
|
||||
print(f"WindowFlags_.no_collapse 存在: {hasattr(imgui.WindowFlags_, 'no_collapse')}")
|
||||
|
||||
print("\n✓ ImGui docking API 可用!")
|
||||
@ -13,7 +13,7 @@ from panda3d.core import Filename
|
||||
class ImGuiStyleManager:
|
||||
"""ImGui样式管理器 - 负责UI样式的统一管理"""
|
||||
|
||||
def __init__(self, imgui_backend):
|
||||
def __init__(self, imgui_backend, world=None):
|
||||
"""
|
||||
初始化样式管理器
|
||||
|
||||
@ -21,6 +21,7 @@ class ImGuiStyleManager:
|
||||
imgui_backend: ImGui后端对象
|
||||
"""
|
||||
self.imgui_backend = imgui_backend
|
||||
self.world = world
|
||||
self.io = imgui_backend.io
|
||||
self.style = None # 延迟初始化,在apply_style中设置
|
||||
|
||||
@ -60,25 +61,22 @@ class ImGuiStyleManager:
|
||||
# 字体设置在demo.py中直接处理,这里不再初始化
|
||||
|
||||
def _setup_fonts(self):
|
||||
"""设置字体,包括中文字体支持"""
|
||||
"""设置字体,优先确保中文正常显示"""
|
||||
try:
|
||||
# 尝试加载中文字体
|
||||
font_path = self._get_chinese_font_path()
|
||||
if font_path:
|
||||
# 使用p3dimgui的字体加载方法
|
||||
self.imgui_backend.load_font(font_path, self.sizes['font_size'])
|
||||
print(f"✓ ImGui中文字体加载成功: {font_path}")
|
||||
# 获取中文字体路径
|
||||
chinese_font_path = self._get_chinese_font_path()
|
||||
|
||||
if chinese_font_path:
|
||||
# 使用原始的字体加载方式
|
||||
self._load_chinese_font_simple(chinese_font_path)
|
||||
else:
|
||||
print("⚠ 无法加载中文字体,使用默认字体")
|
||||
print("⚠ 未找到中文字体,使用默认字体")
|
||||
self._load_default_font()
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠ ImGui字体设置失败: {e}")
|
||||
# 备用方案:尝试使用默认字体
|
||||
try:
|
||||
# 使用p3dimgui的默认字体
|
||||
self.imgui_backend.load_font(None, self.sizes['font_size'])
|
||||
except:
|
||||
pass
|
||||
# 备用方案:使用默认字体
|
||||
self._load_default_font()
|
||||
|
||||
def _get_chinese_font_path(self):
|
||||
"""获取中文字体路径"""
|
||||
@ -112,6 +110,148 @@ class ImGuiStyleManager:
|
||||
|
||||
return None
|
||||
|
||||
def _get_emoji_font_path(self):
|
||||
"""获取Emoji字体路径"""
|
||||
system = platform.system().lower()
|
||||
|
||||
# 候选Emoji字体路径
|
||||
if system == "windows":
|
||||
win_dir = os.environ.get("WINDIR") or r"C:\Windows"
|
||||
font_candidates = [
|
||||
Path(win_dir) / "Fonts" / "seguiemj.ttf", # Segoe UI Emoji
|
||||
Path(win_dir) / "Fonts" / "NotoColorEmoji.ttf",
|
||||
]
|
||||
elif system == "darwin":
|
||||
font_candidates = [
|
||||
Path("/System/Library/Fonts/Apple Color Emoji.ttc"),
|
||||
Path("/System/Library/Fonts/AppleColorEmoji.ttf"),
|
||||
]
|
||||
else: # Linux
|
||||
font_candidates = [
|
||||
Path("/usr/share/fonts/truetype/noto/NotoColorEmoji.ttf"),
|
||||
Path("/usr/share/fonts/opentype/noto/NotoColorEmoji.ttf"),
|
||||
Path("/usr/share/fonts/truetype/emoji/Emoji.ttf"),
|
||||
]
|
||||
|
||||
# 尝试找到存在的字体文件
|
||||
for font_path in font_candidates:
|
||||
if font_path.exists():
|
||||
return str(font_path)
|
||||
|
||||
return None
|
||||
|
||||
def _load_merged_fonts(self, chinese_font_path, emoji_font_path):
|
||||
"""合并加载中文字体和Emoji字体"""
|
||||
try:
|
||||
# 简化的字体加载:先确保中文正常显示
|
||||
font_config = self.imgui_backend.io.fonts
|
||||
|
||||
# 清除现有字体
|
||||
font_config.clear()
|
||||
|
||||
# 添加中文字体(使用默认字符范围)
|
||||
chinese_font = font_config.add_font_from_file_ttf(
|
||||
chinese_font_path,
|
||||
self.sizes['font_size']
|
||||
)
|
||||
|
||||
# 尝试添加Emoji字体(如果支持的话)
|
||||
try:
|
||||
font_config.merge_mode = True
|
||||
font_config.add_font_from_file_ttf(
|
||||
emoji_font_path,
|
||||
self.sizes['font_size']
|
||||
)
|
||||
font_config.merge_mode = False
|
||||
print(f"✓ ImGui合并字体加载成功: 中文={chinese_font_path}, Emoji={emoji_font_path}")
|
||||
except Exception as emoji_error:
|
||||
print(f"⚠ Emoji字体合并失败,仅使用中文字体: {emoji_error}")
|
||||
print(f"✓ ImGui中文字体加载成功: {chinese_font_path}")
|
||||
|
||||
# 构建字体图集
|
||||
font_config.build()
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠ 字体加载失败,使用备用方案: {e}")
|
||||
self._load_simple_chinese_font(chinese_font_path)
|
||||
|
||||
def _load_font_with_emoji_range(self, font_path):
|
||||
"""加载单一字体但包含Emoji字符范围"""
|
||||
# 简化实现,直接调用简单字体加载
|
||||
self._load_simple_chinese_font(font_path)
|
||||
|
||||
def _load_chinese_font_simple(self, font_path):
|
||||
"""简单加载中文字体"""
|
||||
try:
|
||||
# 清除现有字体
|
||||
self.imgui_backend.io.fonts.clear()
|
||||
|
||||
# 使用标准的中文字符范围
|
||||
glyph_ranges = imgui.get_io().fonts.get_glyph_ranges_chinese_full()
|
||||
|
||||
# 添加中文字体
|
||||
self.imgui_backend.io.fonts.add_font_from_file_ttf(
|
||||
font_path,
|
||||
self.sizes['font_size'],
|
||||
None,
|
||||
glyph_ranges
|
||||
)
|
||||
|
||||
print(f"✓ ImGui中文字体加载成功: {font_path}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠ 中文字体加载失败: {e}")
|
||||
self._load_default_font()
|
||||
|
||||
def _load_default_font(self):
|
||||
"""加载默认字体"""
|
||||
try:
|
||||
# 清除现有字体
|
||||
self.imgui_backend.io.fonts.clear()
|
||||
# 添加默认字体
|
||||
self.imgui_backend.io.fonts.add_font_default()
|
||||
print("✓ 使用默认字体")
|
||||
except Exception as e:
|
||||
print(f"⚠ 默认字体加载失败: {e}")
|
||||
|
||||
def _get_chinese_glyph_ranges(self):
|
||||
"""获取中文字符范围"""
|
||||
# 基本拉丁 + 中文扩展
|
||||
ranges = [
|
||||
0x0020, 0x00FF, # 基本拉丁
|
||||
0x2000, 0x206F, # 标点符号
|
||||
0x3000, 0x30FF, # 中文符号
|
||||
0xFF00, 0xFFEF, # 半角/全角
|
||||
0x4E00, 0x9FAF, # CJK统一汉字
|
||||
]
|
||||
return ranges
|
||||
|
||||
def _get_emoji_glyph_ranges(self):
|
||||
"""获取Emoji字符范围"""
|
||||
# Emoji字符范围
|
||||
ranges = [
|
||||
0x1F300, 0x1F5FF, # 杂项符号和象形文字
|
||||
0x1F600, 0x1F64F, # 表情符号
|
||||
0x1F680, 0x1F6FF, # 交通和地图符号
|
||||
0x1F700, 0x1F77F, # 炼金术符号
|
||||
0x1F780, 0x1F7FF, # 几何形状扩展
|
||||
0x1F800, 0x1F8FF, # 补充箭头-C
|
||||
0x1F900, 0x1F9FF, # 补充符号和象形文字
|
||||
0x2600, 0x26FF, # 杂项符号
|
||||
0x2700, 0x27BF, # 装饰符号
|
||||
]
|
||||
return ranges
|
||||
|
||||
def _get_combined_glyph_ranges(self):
|
||||
"""获取合并的字符范围(中文+Emoji)"""
|
||||
# 合并所有字符范围
|
||||
chinese_ranges = self._get_chinese_glyph_ranges()
|
||||
emoji_ranges = self._get_emoji_glyph_ranges()
|
||||
|
||||
# 将两个范围合并
|
||||
combined = chinese_ranges + emoji_ranges
|
||||
return combined
|
||||
|
||||
def apply_style(self):
|
||||
"""应用与Qt UI一致的样式"""
|
||||
# 先应用深色主题作为基础
|
||||
@ -253,14 +393,19 @@ class ImGuiStyleManager:
|
||||
return changed, new_value
|
||||
|
||||
def load_icon(self, icon_name):
|
||||
"""加载图标纹理"""
|
||||
"""加载图标纹理为ImGui可用的格式"""
|
||||
try:
|
||||
# 构建图标路径
|
||||
project_root = Path(__file__).resolve().parent.parent
|
||||
icon_path = project_root / "icons" / f"{icon_name}.png"
|
||||
|
||||
if icon_path.exists():
|
||||
return self.imgui_backend.loadTexture(str(icon_path))
|
||||
# 使用base.imgui.loadTexture方法
|
||||
if hasattr(base, 'imgui') and hasattr(base.imgui, 'loadTexture'):
|
||||
return base.imgui.loadTexture(str(icon_path))
|
||||
else:
|
||||
print(f"⚠ ImGui后端未初始化")
|
||||
return None
|
||||
else:
|
||||
print(f"⚠ 图标文件不存在: {icon_path}")
|
||||
return None
|
||||
|
||||
@ -54,99 +54,93 @@ class ResourceManager:
|
||||
|
||||
# 文件图标映射(Unicode Emoji)
|
||||
self._init_icon_map()
|
||||
|
||||
|
||||
def _init_icon_map(self):
|
||||
"""初始化文件图标映射"""
|
||||
"""初始化文件图标映射(使用PNG图标文件)"""
|
||||
self.icon_map = {
|
||||
# 编程语言文件
|
||||
'.py': '🐍', # Python文件
|
||||
'.js': '⚡', # JavaScript文件
|
||||
'.ts': '⚡', # TypeScript文件
|
||||
'.html': '🌐', # HTML文件
|
||||
'.css': '🎨', # CSS文件
|
||||
'.json': '📋', # JSON文件
|
||||
'.xml': '📄', # XML文件
|
||||
'.yaml': '📄', # YAML文件
|
||||
'.yml': '📄', # YAML文件
|
||||
'.md': '📝', # Markdown文档
|
||||
|
||||
# 3D模型文件
|
||||
'.fbx': '🎭', # FBX模型文件
|
||||
'.obj': '🎭', # OBJ模型文件
|
||||
'.gltf': '🎭', # glTF模型
|
||||
'.glb': '🎭', # glTF二进制模型
|
||||
'.bam': '🎭', # BAM模型文件
|
||||
'.egg': '🎭', # EGG模型文件
|
||||
'.dae': '🎭', # Collada模型
|
||||
'.3ds': '🎭', # 3DS模型
|
||||
'.blend': '🎭', # Blender文件
|
||||
'.fbx': 'model', # FBX模型文件
|
||||
'.obj': 'model', # OBJ模型文件
|
||||
'.gltf': 'model', # glTF模型
|
||||
'.glb': 'model', # glTF二进制模型
|
||||
'.bam': 'model', # BAM模型文件
|
||||
|
||||
# 图像文件
|
||||
'.jpg': '🖼️', # JPEG图像
|
||||
'.jpeg': '🖼️', # JPEG图像
|
||||
'.png': '🖼️', # PNG图像
|
||||
'.gif': '🖼️', # GIF图像
|
||||
'.bmp': '🖼️', # BMP图像
|
||||
'.tga': '🖼️', # TGA图像
|
||||
'.tiff': '🖼️', # TIFF图像
|
||||
'.webp': '🖼️', # WebP图像
|
||||
'.svg': '🖼️', # SVG图像
|
||||
'.ico': '🖼️', # ICO图标
|
||||
'.jpg': 'image', # JPEG图像
|
||||
'.jpeg': 'image', # JPEG图像
|
||||
'.png': 'image', # PNG图像
|
||||
'.bmp': 'image', # BMP图像
|
||||
'.tga': 'image', # TGA图像
|
||||
'.tif': 'image', # TIFF图像
|
||||
'.tiff': 'image', # TIFF图像
|
||||
'.hdr': 'image', # HDR图像
|
||||
'.exr': 'image', # EXR图像
|
||||
|
||||
# 音频文件
|
||||
'.mp3': '🎵', # MP3音频
|
||||
'.wav': '🎵', # WAV音频
|
||||
'.ogg': '🎵', # OGG音频
|
||||
'.flac': '🎵', # FLAC音频
|
||||
'.m4a': '🎵', # M4A音频
|
||||
'.mp3': 'audio', # MP3音频
|
||||
'.wav': 'audio', # WAV音频
|
||||
'.ogg': 'audio', # OGG音频
|
||||
'.flac': 'audio', # FLAC音频
|
||||
|
||||
# 视频文件
|
||||
'.mp4': '🎬', # MP4视频
|
||||
'.avi': '🎬', # AVI视频
|
||||
'.mkv': '🎬', # MKV视频
|
||||
'.mov': '🎬', # MOV视频
|
||||
'.wmv': '🎬', # WMV视频
|
||||
'.flv': '🎬', # FLV视频
|
||||
'.mp4': 'video', # MP4视频
|
||||
'.avi': 'video', # AVI视频
|
||||
'.mov': 'video', # MOV视频
|
||||
'.mkv': 'video', # MKV视频
|
||||
|
||||
# 文档文件
|
||||
'.txt': '📄', # 纯文本文件
|
||||
'.pdf': '📕', # PDF文档
|
||||
'.doc': '📘', # Word文档
|
||||
'.docx': '📘', # Word文档
|
||||
'.xls': '📗', # Excel表格
|
||||
'.xlsx': '📗', # Excel表格
|
||||
'.ppt': '📙', # PowerPoint演示
|
||||
'.pptx': '📙', # PowerPoint演示
|
||||
'.txt': 'document', # 文本文件
|
||||
'.md': 'document', # Markdown文件
|
||||
'.pdf': 'document', # PDF文件
|
||||
'.doc': 'document', # Word文档
|
||||
'.docx': 'document', # Word文档
|
||||
'.rtf': 'document', # RTF文档
|
||||
|
||||
# 压缩文件
|
||||
'.zip': '📦', # ZIP压缩包
|
||||
'.rar': '📦', # RAR压缩包
|
||||
'.7z': '📦', # 7Z压缩包
|
||||
'.tar': '📦', # TAR压缩包
|
||||
'.gz': '📦', # GZ压缩包
|
||||
# 代码文件
|
||||
'.py': 'python', # Python文件
|
||||
'.js': 'code', # JavaScript文件
|
||||
'.ts': 'code', # TypeScript文件
|
||||
'.cpp': 'code', # C++文件
|
||||
'.c': 'code', # C文件
|
||||
'.h': 'code', # 头文件
|
||||
'.java': 'code', # Java文件
|
||||
'.cs': 'code', # C#文件
|
||||
'.html': 'code', # HTML文件
|
||||
'.css': 'code', # CSS文件
|
||||
'.xml': 'code', # XML文件
|
||||
'.json': 'config', # JSON文件
|
||||
'.yaml': 'config', # YAML文件
|
||||
'.yml': 'config', # YAML文件
|
||||
|
||||
# 配置文件
|
||||
'.ini': '⚙️', # INI配置文件
|
||||
'.cfg': '⚙️', # CFG配置文件
|
||||
'.conf': '⚙️', # CONF配置文件
|
||||
'.toml': '⚙️', # TOML配置文件
|
||||
'.ini': 'config', # 配置文件
|
||||
'.cfg': 'config', # 配置文件
|
||||
'.conf': 'config', # 配置文件
|
||||
'.toml': 'config', # 配置文件
|
||||
|
||||
# 压缩文件
|
||||
'.zip': 'archive', # ZIP压缩包
|
||||
'.rar': 'archive', # RAR压缩包
|
||||
'.7z': 'archive', # 7Z压缩包
|
||||
'.tar': 'archive', # TAR压缩包
|
||||
'.gz': 'archive', # GZ压缩包
|
||||
|
||||
# 字体文件
|
||||
'.ttf': '🔤', # TrueType字体
|
||||
'.otf': '🔤', # OpenType字体
|
||||
'.woff': '🔤', # WOFF字体
|
||||
'.woff2': '🔤', # WOFF2字体
|
||||
'.ttf': 'font', # TrueType字体
|
||||
'.otf': 'font', # OpenType字体
|
||||
'.woff': 'font', # WOFF字体
|
||||
'.woff2': 'font', # WOFF2字体
|
||||
|
||||
# 文件夹图标
|
||||
'folder': 'folder', # 文件夹图标
|
||||
'folder_open': 'folder', # 打开的文件夹图标
|
||||
|
||||
# 默认图标
|
||||
'default': '📄', # 默认文件图标
|
||||
'folder': '📁', # 文件夹图标
|
||||
'folder_open': '📂', # 打开的文件夹图标
|
||||
'drive': '💾', # 驱动器图标
|
||||
'home': '🏠', # 主目录图标
|
||||
'default': 'file', # 默认文件图标
|
||||
}
|
||||
|
||||
def get_file_icon(self, filename: str, is_folder: bool = False) -> str:
|
||||
"""根据文件名获取图标"""
|
||||
"""根据文件名获取图标名称"""
|
||||
if is_folder:
|
||||
return self.icon_map['folder']
|
||||
|
||||
|
||||
207
demo.py
@ -15,6 +15,7 @@ import os
|
||||
import warnings
|
||||
import threading
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
# 导入MyWorld类和必要的模块
|
||||
from core.world import CoreWorld
|
||||
@ -192,66 +193,46 @@ class MyWorld(CoreWorld):
|
||||
|
||||
# 初始化样式管理器
|
||||
from core.imgui_style_manager import ImGuiStyleManager
|
||||
self.style_manager = ImGuiStyleManager(self.imgui)
|
||||
self.style_manager = ImGuiStyleManager(self.imgui, self)
|
||||
|
||||
# 尝试直接设置中文字体
|
||||
# 简化的初始化字体设置(只使用中文字体)
|
||||
try:
|
||||
# 先清除默认字体
|
||||
self.imgui.io.fonts.clear()
|
||||
|
||||
# 尝试加载中文字体
|
||||
import platform
|
||||
from pathlib import Path
|
||||
|
||||
# 获取中文字体路径
|
||||
system = platform.system().lower()
|
||||
if system == "linux":
|
||||
font_path = "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc"
|
||||
font_path = "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc"
|
||||
elif system == "windows":
|
||||
font_path = "C:/Windows/Fonts/msyh.ttc"
|
||||
elif system == "darwin":
|
||||
font_path = "/System/Library/Fonts/PingFang.ttc"
|
||||
else:
|
||||
font_path = None
|
||||
|
||||
|
||||
if font_path and Path(font_path).exists():
|
||||
# 先清除默认字体
|
||||
self.imgui.io.fonts.clear()
|
||||
|
||||
# 创建中文字符范围(基本中文字符)
|
||||
# 这个范围包含了常用中文字符的Unicode范围
|
||||
chinese_ranges = []
|
||||
# 基本拉丁字母
|
||||
for i in range(0x0020, 0x00FF):
|
||||
chinese_ranges.append(i)
|
||||
# 中文字符范围
|
||||
for i in range(0x4E00, 0x9FFF): # CJK统一汉字
|
||||
chinese_ranges.append(i)
|
||||
for i in range(0x3400, 0x4DBF): # CJK扩展A
|
||||
chinese_ranges.append(i)
|
||||
for i in range(0x20000, 0x2A6DF): # CJK扩展B
|
||||
chinese_ranges.append(i)
|
||||
for i in range(0x2A700, 0x2B73F): # CJK扩展C
|
||||
chinese_ranges.append(i)
|
||||
for i in range(0x2B740, 0x2B81F): # CJK扩展D
|
||||
chinese_ranges.append(i)
|
||||
for i in range(0x2B820, 0x2CEAF): # CJK扩展E
|
||||
chinese_ranges.append(i)
|
||||
for i in range(0x2CEB0, 0x2EBEF): # CJK扩展F
|
||||
chinese_ranges.append(i)
|
||||
for i in range(0x3000, 0x303F): # CJK符号和标点
|
||||
chinese_ranges.append(i)
|
||||
for i in range(0xFF00, 0xFFEF): # 全角字符
|
||||
chinese_ranges.append(i)
|
||||
|
||||
# 添加中文字体(不指定字符范围,让ImGui自动处理)
|
||||
font = self.imgui.io.fonts.add_font_from_file_ttf(font_path, 14.0)
|
||||
print(f"✓ 直接设置中文字体成功: {font_path}")
|
||||
print(f" 使用自动字符范围")
|
||||
self.imgui.io.fonts.add_font_from_file_ttf(font_path, 14.0)
|
||||
print(f"✓ 初始化中文字体成功: {font_path}")
|
||||
else:
|
||||
print("⚠ 中文字体文件不存在")
|
||||
# 回退到原来的字体
|
||||
fallback_font_path = "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc"
|
||||
if fallback_font_path and Path(fallback_font_path).exists():
|
||||
self.imgui.io.fonts.add_font_from_file_ttf(fallback_font_path, 14.0)
|
||||
print(f"✓ 初始化回退字体成功: {fallback_font_path}")
|
||||
else:
|
||||
self.imgui.io.fonts.add_font_default()
|
||||
print("✓ 初始化使用默认字体")
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠ 直接设置中文字体失败: {e}")
|
||||
print(f"⚠ 初始化字体系统失败: {e}")
|
||||
# 备用方案:使用默认字体
|
||||
try:
|
||||
self.imgui.io.fonts.add_font_default()
|
||||
print("✓ 使用默认字体")
|
||||
print("✓ 使用备用默认字体")
|
||||
except:
|
||||
pass
|
||||
|
||||
@ -485,34 +466,45 @@ class MyWorld(CoreWorld):
|
||||
'delete_fail_icon': self.style_manager.load_icon('delete_fail_icon'),
|
||||
}
|
||||
|
||||
# 确保中文字体被正确应用
|
||||
# 简化字体加载(只使用中文字体)
|
||||
try:
|
||||
# 强制刷新字体
|
||||
# 清除现有字体
|
||||
self.imgui.io.fonts.clear()
|
||||
# 重新添加中文字体
|
||||
|
||||
# 尝试加载中文字体
|
||||
import platform
|
||||
from pathlib import Path
|
||||
|
||||
system = platform.system().lower()
|
||||
if system == "linux":
|
||||
font_path = "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc"
|
||||
font_path = "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc"
|
||||
elif system == "windows":
|
||||
font_path = "C:/Windows/Fonts/msyh.ttc"
|
||||
elif system == "darwin":
|
||||
font_path = "/System/Library/Fonts/PingFang.ttc"
|
||||
else:
|
||||
font_path = None
|
||||
|
||||
if font_path and Path(font_path).exists():
|
||||
# 添加中文字体,包含中文字符集
|
||||
try:
|
||||
chinese_ranges = imgui.get_io().fonts.get_glyph_ranges_chinese_full()
|
||||
self.imgui.io.fonts.add_font_from_file_ttf(font_path, 14.0, None, chinese_ranges)
|
||||
print("✓ 第一帧重新设置中文字体(包含中文字符集)")
|
||||
except:
|
||||
# 如果获取字符集失败,使用简单方式
|
||||
self.imgui.io.fonts.add_font_from_file_ttf(font_path, 14.0)
|
||||
print("✓ 第一帧重新设置中文字体(简单方式)")
|
||||
self.imgui.io.fonts.add_font_from_file_ttf(font_path, 14.0)
|
||||
print(f"✓ 中文字体加载成功: {font_path}")
|
||||
else:
|
||||
# 回退到原来的字体
|
||||
fallback_font_path = "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc"
|
||||
if fallback_font_path and Path(fallback_font_path).exists():
|
||||
self.imgui.io.fonts.add_font_from_file_ttf(fallback_font_path, 14.0)
|
||||
print(f"✓ 回退字体加载成功: {fallback_font_path}")
|
||||
else:
|
||||
self.imgui.io.fonts.add_font_default()
|
||||
print("✓ 使用默认字体")
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠ 第一帧设置中文字体失败: {e}")
|
||||
print(f"⚠ 字体初始化失败: {e}")
|
||||
try:
|
||||
self.imgui.io.fonts.add_font_default()
|
||||
print("✓ 使用备用默认字体")
|
||||
except:
|
||||
pass
|
||||
|
||||
# 获取窗口尺寸
|
||||
display_size = imgui.get_io().display_size
|
||||
@ -780,10 +772,10 @@ class MyWorld(CoreWorld):
|
||||
if imgui.button("▲"):
|
||||
rm.navigate_up()
|
||||
imgui.same_line()
|
||||
if imgui.button("🏠"):
|
||||
if imgui.button("主页"):
|
||||
rm.navigate_to(rm.project_root / "Resources")
|
||||
imgui.same_line()
|
||||
if imgui.button("🔄"):
|
||||
if imgui.button("刷新"):
|
||||
rm.force_refresh()
|
||||
|
||||
# 自动刷新开关
|
||||
@ -823,7 +815,7 @@ class MyWorld(CoreWorld):
|
||||
continue
|
||||
|
||||
# 目录图标和名称
|
||||
icon = rm.get_file_icon(dir_path.name, is_folder=True)
|
||||
icon_name = rm.get_file_icon(dir_path.name, is_folder=True)
|
||||
node_open = False
|
||||
|
||||
# 检查是否被选中
|
||||
@ -831,9 +823,24 @@ class MyWorld(CoreWorld):
|
||||
|
||||
# 使用TreeNode来显示目录
|
||||
if is_selected:
|
||||
imgui.push_style_color(imgui.Col_.header, imgui.get_style().colors[imgui.Col_.header_hovered])
|
||||
imgui.push_style_color(imgui.Col_.header, imgui.IM_COL32(100, 150, 200, 255))
|
||||
|
||||
node_open = imgui.tree_node(f"{icon} {dir_path.name}")
|
||||
# 尝试加载PNG图标
|
||||
icon_texture = None
|
||||
try:
|
||||
# 直接使用图标名称,load_icon会自动添加.png
|
||||
icon_texture = self.style_manager.load_icon(f"file_types/{icon_name}")
|
||||
except:
|
||||
pass
|
||||
|
||||
if icon_texture:
|
||||
# 使用PNG图标
|
||||
imgui.image(icon_texture, (16, 16))
|
||||
imgui.same_line()
|
||||
node_open = imgui.tree_node(f"{dir_path.name}")
|
||||
else:
|
||||
# 回退到文本标识符
|
||||
node_open = imgui.tree_node(f"[{icon_name.upper()}]{dir_path.name}")
|
||||
|
||||
if is_selected:
|
||||
imgui.pop_style_color()
|
||||
@ -872,13 +879,29 @@ class MyWorld(CoreWorld):
|
||||
if not rm.should_show_file(subdir):
|
||||
continue
|
||||
|
||||
subicon = rm.get_file_icon(subdir.name, is_folder=True)
|
||||
# 初始化变量
|
||||
subicon_name = "folder"
|
||||
sub_is_selected = False
|
||||
|
||||
# 获取子目录图标名称
|
||||
subicon_name = rm.get_file_icon(subdir.name, is_folder=True)
|
||||
sub_is_selected = subdir in rm.selected_files
|
||||
|
||||
if sub_is_selected:
|
||||
imgui.push_style_color(imgui.Col_.header, imgui.get_style().colors[imgui.Col_.header_hovered])
|
||||
# 尝试加载PNG图标
|
||||
subicon_texture = None
|
||||
try:
|
||||
subicon_texture = self.style_manager.load_icon(f"file_types/{subicon_name}")
|
||||
except:
|
||||
pass
|
||||
|
||||
sub_node_open = imgui.tree_node(f" {subicon} {subdir.name}")
|
||||
if subicon_texture:
|
||||
# 使用PNG图标
|
||||
imgui.image(subicon_texture, (16, 16))
|
||||
imgui.same_line()
|
||||
sub_node_open = imgui.tree_node(f" {subdir.name}")
|
||||
else:
|
||||
# 回退到文本标识符
|
||||
sub_node_open = imgui.tree_node(f" [{subicon_name.upper()}]{subdir.name}")
|
||||
|
||||
if sub_is_selected:
|
||||
imgui.pop_style_color()
|
||||
@ -913,10 +936,24 @@ class MyWorld(CoreWorld):
|
||||
if not rm.should_show_file(subfile):
|
||||
continue
|
||||
|
||||
subicon = rm.get_file_icon(subfile.name)
|
||||
subicon_name = rm.get_file_icon(subfile.name)
|
||||
sub_is_selected = subfile in rm.selected_files
|
||||
|
||||
selected = imgui.selectable(f" {subicon} {subfile.name}", sub_is_selected)
|
||||
# 尝试加载PNG图标
|
||||
subicon_texture = None
|
||||
try:
|
||||
subicon_texture = self.style_manager.load_icon(f"file_types/{subicon_name}")
|
||||
except:
|
||||
pass
|
||||
|
||||
if subicon_texture:
|
||||
# 使用PNG图标
|
||||
imgui.image(subicon_texture, (16, 16))
|
||||
imgui.same_line()
|
||||
selected = imgui.selectable(f" {subfile.name}", sub_is_selected)
|
||||
else:
|
||||
# 回退到文本标识符
|
||||
selected = imgui.selectable(f" [{subicon_name.upper()}] {subfile.name}", sub_is_selected)
|
||||
|
||||
# 处理子文件的选择
|
||||
if selected:
|
||||
@ -944,41 +981,11 @@ class MyWorld(CoreWorld):
|
||||
rm.context_menu_file = subfile
|
||||
rm.context_menu_position = (imgui.get_mouse_pos().x, imgui.get_mouse_pos().y)
|
||||
|
||||
imgui.tree_pop()
|
||||
# 只有在节点展开时才调用tree_pop
|
||||
if node_open:
|
||||
imgui.tree_pop()
|
||||
|
||||
|
||||
# 显示文件
|
||||
for file_path in files:
|
||||
if not rm.should_show_file(file_path):
|
||||
continue
|
||||
|
||||
# 文件图标和名称
|
||||
icon = rm.get_file_icon(file_path.name)
|
||||
file_size = rm.get_file_size_string(file_path)
|
||||
|
||||
# 检查是否被选中
|
||||
is_selected = file_path in rm.selected_files
|
||||
|
||||
# 使用Selectable来显示文件
|
||||
selected = imgui.selectable(f"{icon} {file_path.name}", is_selected)
|
||||
|
||||
# 显示文件大小
|
||||
if file_size:
|
||||
imgui.same_line()
|
||||
imgui.text_disabled(file_size)
|
||||
|
||||
# 处理选择
|
||||
if selected:
|
||||
if imgui.get_io().key_ctrl:
|
||||
# 多选模式
|
||||
if is_selected:
|
||||
rm.selected_files.discard(file_path)
|
||||
else:
|
||||
rm.selected_files.add(file_path)
|
||||
else:
|
||||
# 单选模式
|
||||
rm.selected_files.clear()
|
||||
rm.selected_files.add(file_path)
|
||||
rm.focused_file = file_path
|
||||
|
||||
# 处理拖拽开始
|
||||
if imgui.is_item_active() and imgui.is_item_hovered():
|
||||
|
||||
BIN
icons/file_types/archive.png
Normal file
|
After Width: | Height: | Size: 323 B |
BIN
icons/file_types/audio.png
Normal file
|
After Width: | Height: | Size: 316 B |
BIN
icons/file_types/code.png
Normal file
|
After Width: | Height: | Size: 543 B |
BIN
icons/file_types/config.png
Normal file
|
After Width: | Height: | Size: 774 B |
BIN
icons/file_types/document.png
Normal file
|
After Width: | Height: | Size: 327 B |
BIN
icons/file_types/file.png
Normal file
|
After Width: | Height: | Size: 332 B |
BIN
icons/file_types/folder.png
Normal file
|
After Width: | Height: | Size: 323 B |
BIN
icons/file_types/font.png
Normal file
|
After Width: | Height: | Size: 331 B |
BIN
icons/file_types/image.png
Normal file
|
After Width: | Height: | Size: 329 B |
BIN
icons/file_types/model.png
Normal file
|
After Width: | Height: | Size: 327 B |
BIN
icons/file_types/python.png
Normal file
|
After Width: | Height: | Size: 328 B |
BIN
icons/file_types/video.png
Normal file
|
After Width: | Height: | Size: 325 B |
16
imgui.ini
@ -31,7 +31,7 @@ DockId=0x00000007,0
|
||||
|
||||
[Window][场景树]
|
||||
Pos=0,20
|
||||
Size=285,753
|
||||
Size=285,739
|
||||
Collapsed=0
|
||||
DockId=0x00000001,0
|
||||
|
||||
@ -42,8 +42,8 @@ Collapsed=0
|
||||
DockId=0x00000005,0
|
||||
|
||||
[Window][控制台]
|
||||
Pos=880,775
|
||||
Size=644,241
|
||||
Pos=880,761
|
||||
Size=644,255
|
||||
Collapsed=0
|
||||
DockId=0x0000000C,0
|
||||
|
||||
@ -99,20 +99,20 @@ Size=600,500
|
||||
Collapsed=0
|
||||
|
||||
[Window][资源管理器]
|
||||
Pos=0,775
|
||||
Size=878,241
|
||||
Pos=0,761
|
||||
Size=878,255
|
||||
Collapsed=0
|
||||
DockId=0x0000000B,0
|
||||
|
||||
[Docking][Data]
|
||||
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,20 Size=1850,996 Split=X
|
||||
DockNode ID=0x00000003 Parent=0x08BD597D SizeRef=1524,996 Split=Y
|
||||
DockNode ID=0x00000009 Parent=0x00000003 SizeRef=1380,753 Split=X
|
||||
DockNode ID=0x00000009 Parent=0x00000003 SizeRef=1380,739 Split=X
|
||||
DockNode ID=0x00000001 Parent=0x00000009 SizeRef=285,730 HiddenTabBar=1 Selected=0xE0015051
|
||||
DockNode ID=0x00000002 Parent=0x00000009 SizeRef=1237,730 Split=Y
|
||||
DockNode ID=0x00000007 Parent=0x00000002 SizeRef=1380,32 HiddenTabBar=1 Selected=0x43A39006
|
||||
DockNode ID=0x00000008 Parent=0x00000002 SizeRef=1380,719 CentralNode=1 Selected=0x5E5F7166
|
||||
DockNode ID=0x0000000A Parent=0x00000003 SizeRef=1380,241 Split=X Selected=0x5428E753
|
||||
DockNode ID=0x00000008 Parent=0x00000002 SizeRef=1380,705 CentralNode=1 Selected=0x5E5F7166
|
||||
DockNode ID=0x0000000A Parent=0x00000003 SizeRef=1380,255 Split=X Selected=0x5428E753
|
||||
DockNode ID=0x0000000B Parent=0x0000000A SizeRef=878,111 HiddenTabBar=1 Selected=0x3A2E05C3
|
||||
DockNode ID=0x0000000C Parent=0x0000000A SizeRef=644,111 HiddenTabBar=1 Selected=0x5428E753
|
||||
DockNode ID=0x00000004 Parent=0x08BD597D SizeRef=324,996 Split=Y Selected=0x5DB6FF37
|
||||
|
||||
@ -1,238 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
简单的拖拽导入工具
|
||||
直接运行此工具,然后将3D文件拖拽到窗口中
|
||||
工具会生成可在demo.py中使用的导入命令
|
||||
"""
|
||||
|
||||
import tkinter as tk
|
||||
from tkinter import filedialog, messagebox, scrolledtext
|
||||
import os
|
||||
import sys
|
||||
|
||||
class SimpleDragDrop:
|
||||
def __init__(self):
|
||||
self.root = tk.Tk()
|
||||
self.root.title("3D模型拖拽导入工具")
|
||||
self.root.geometry("600x400")
|
||||
|
||||
# 设置窗口始终在最前面
|
||||
self.root.attributes('-topmost', True)
|
||||
|
||||
# 支持的文件格式
|
||||
self.supported_formats = ['.gltf', '.glb', '.fbx', '.bam', '.egg', '.obj']
|
||||
|
||||
self.setup_ui()
|
||||
self.setup_drag_drop()
|
||||
|
||||
def setup_ui(self):
|
||||
"""设置用户界面"""
|
||||
# 主框架
|
||||
main_frame = tk.Frame(self.root)
|
||||
main_frame.pack(fill='both', expand=True, padx=10, pady=10)
|
||||
|
||||
# 标题
|
||||
title_label = tk.Label(
|
||||
main_frame,
|
||||
text="将3D模型文件拖拽到此处",
|
||||
font=('Arial', 16, 'bold')
|
||||
)
|
||||
title_label.pack(pady=(0, 10))
|
||||
|
||||
# 拖拽区域
|
||||
self.drop_area = tk.Frame(
|
||||
main_frame,
|
||||
bg='lightgray',
|
||||
relief='sunken',
|
||||
bd=2,
|
||||
height=150
|
||||
)
|
||||
self.drop_area.pack(fill='both', expand=True, pady=(0, 10))
|
||||
|
||||
# 提示文本
|
||||
hint_label = tk.Label(
|
||||
self.drop_area,
|
||||
text="支持格式: .gltf, .glb, .fbx, .bam, .egg, .obj\n\n拖拽文件到此处或点击下方按钮选择文件",
|
||||
bg='lightgray',
|
||||
font=('Arial', 11)
|
||||
)
|
||||
hint_label.pack(expand=True)
|
||||
|
||||
# 按钮框架
|
||||
button_frame = tk.Frame(main_frame)
|
||||
button_frame.pack(fill='x', pady=(0, 10))
|
||||
|
||||
# 选择文件按钮
|
||||
select_btn = tk.Button(
|
||||
button_frame,
|
||||
text="选择文件",
|
||||
command=self.select_files,
|
||||
width=15
|
||||
)
|
||||
select_btn.pack(side='left', padx=5)
|
||||
|
||||
# 清空按钮
|
||||
clear_btn = tk.Button(
|
||||
button_frame,
|
||||
text="清空",
|
||||
command=self.clear_files,
|
||||
width=15
|
||||
)
|
||||
clear_btn.pack(side='left', padx=5)
|
||||
|
||||
# 生成命令按钮
|
||||
generate_btn = tk.Button(
|
||||
button_frame,
|
||||
text="生成导入命令",
|
||||
command=self.generate_commands,
|
||||
bg='lightblue',
|
||||
font=('Arial', 10, 'bold'),
|
||||
width=15
|
||||
)
|
||||
generate_btn.pack(side='right', padx=5)
|
||||
|
||||
# 文件列表
|
||||
list_label = tk.Label(
|
||||
main_frame,
|
||||
text="文件列表:",
|
||||
font=('Arial', 10, 'bold')
|
||||
)
|
||||
list_label.pack(anchor='w', pady=(0, 5))
|
||||
|
||||
# 文件列表框
|
||||
self.file_listbox = tk.Listbox(
|
||||
main_frame,
|
||||
height=8,
|
||||
selectmode=tk.MULTIPLE
|
||||
)
|
||||
self.file_listbox.pack(fill='both', expand=True, pady=(0, 10))
|
||||
|
||||
# 命令输出区域
|
||||
output_label = tk.Label(
|
||||
main_frame,
|
||||
text="导入命令:",
|
||||
font=('Arial', 10, 'bold')
|
||||
)
|
||||
output_label.pack(anchor='w', pady=(0, 5))
|
||||
|
||||
self.command_text = scrolledtext.ScrolledText(
|
||||
main_frame,
|
||||
height=6,
|
||||
width=70
|
||||
)
|
||||
self.command_text.pack(fill='both', expand=True)
|
||||
|
||||
def setup_drag_drop(self):
|
||||
"""设置拖拽功能"""
|
||||
try:
|
||||
# 设置拖拽目标
|
||||
self.drop_area.drop_target_register('DND_Files')
|
||||
self.drop_area.dnd_bind('<<Drop>>', self.on_drop)
|
||||
self.drop_area.dnd_bind('<<DragEnter>>', self.on_drag_enter)
|
||||
self.drop_area.dnd_bind('<<DragLeave>>', self.on_drag_leave)
|
||||
except:
|
||||
# 如果拖拽不支持,只使用文件选择器
|
||||
print("拖拽功能不可用,请使用文件选择器")
|
||||
|
||||
def on_drag_enter(self, event):
|
||||
"""拖拽进入事件"""
|
||||
self.drop_area.configure(bg='lightblue')
|
||||
|
||||
def on_drag_leave(self, event):
|
||||
"""拖拽离开事件"""
|
||||
self.drop_area.configure(bg='lightgray')
|
||||
|
||||
def on_drop(self, event):
|
||||
"""拖拽释放事件"""
|
||||
try:
|
||||
self.drop_area.configure(bg='lightgray')
|
||||
|
||||
# 获取拖拽的文件
|
||||
files = self.root.tk.splitlist(event.data)
|
||||
|
||||
for file_path in files:
|
||||
self.add_file(file_path)
|
||||
|
||||
except Exception as e:
|
||||
messagebox.showerror("错误", f"拖拽处理失败: {e}")
|
||||
|
||||
def add_file(self, file_path):
|
||||
"""添加文件到列表"""
|
||||
if os.path.exists(file_path):
|
||||
file_ext = os.path.splitext(file_path)[1].lower()
|
||||
if file_ext in self.supported_formats:
|
||||
if file_path not in self.file_listbox.get(0, tk.END):
|
||||
self.file_listbox.insert(tk.END, file_path)
|
||||
else:
|
||||
messagebox.showwarning("格式错误", f"不支持的文件格式: {file_ext}")
|
||||
else:
|
||||
messagebox.showerror("文件错误", f"文件不存在: {file_path}")
|
||||
|
||||
def select_files(self):
|
||||
"""选择文件"""
|
||||
files = filedialog.askopenfilenames(
|
||||
title="选择3D模型文件",
|
||||
filetypes=[
|
||||
("所有支持的格式", "*.gltf *.glb *.fbx *.bam *.egg *.obj"),
|
||||
("glTF文件", "*.gltf *.glb"),
|
||||
("FBX文件", "*.fbx"),
|
||||
("BAM文件", "*.bam"),
|
||||
("EGG文件", "*.egg"),
|
||||
("OBJ文件", "*.obj"),
|
||||
("所有文件", "*.*")
|
||||
]
|
||||
)
|
||||
|
||||
for file_path in files:
|
||||
self.add_file(file_path)
|
||||
|
||||
def clear_files(self):
|
||||
"""清空文件列表"""
|
||||
self.file_listbox.delete(0, tk.END)
|
||||
self.command_text.delete(1.0, tk.END)
|
||||
|
||||
def generate_commands(self):
|
||||
"""生成导入命令"""
|
||||
files = list(self.file_listbox.get(0, tk.END))
|
||||
|
||||
if not files:
|
||||
messagebox.showwarning("没有文件", "请先选择或拖拽文件")
|
||||
return
|
||||
|
||||
# 生成Python导入代码
|
||||
commands = "# 在demo.py的控制台中执行以下命令来导入文件:\n\n"
|
||||
|
||||
for i, file_path in enumerate(files, 1):
|
||||
filename = os.path.basename(file_path)
|
||||
commands += f"# 导入文件 {i}: {filename}\n"
|
||||
commands += f"world._import_model_from_path('{file_path}')\n\n"
|
||||
|
||||
commands += "# 或者使用批量导入:\n"
|
||||
commands += "files = [\n"
|
||||
for file_path in files:
|
||||
commands += f" '{file_path}',\n"
|
||||
commands += "]\n"
|
||||
commands += "for file_path in files:\n"
|
||||
commands += " world._import_model_from_path(file_path)\n"
|
||||
|
||||
# 显示命令
|
||||
self.command_text.delete(1.0, tk.END)
|
||||
self.command_text.insert(1.0, commands)
|
||||
|
||||
messagebox.showinfo("完成", "导入命令已生成,请复制到demo.py控制台中执行")
|
||||
|
||||
def run(self):
|
||||
"""运行应用"""
|
||||
self.root.mainloop()
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
try:
|
||||
app = SimpleDragDrop()
|
||||
app.run()
|
||||
except Exception as e:
|
||||
print(f"启动拖拽工具失败: {e}")
|
||||
input("按回车键退出...")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||