修复拖动按钮闪退,保存和加载LUI信息

This commit is contained in:
Hector 2026-03-18 15:13:18 +08:00
parent 74b6a3307c
commit bdd4d0f1b5
4 changed files with 139 additions and 32 deletions

View File

@ -25,25 +25,25 @@ Collapsed=0
[Window][工具栏]
Pos=285,20
Size=1190,62
Size=1221,62
Collapsed=0
DockId=0x00000009,0
[Window][场景树]
Pos=0,20
Size=283,506
Size=283,494
Collapsed=0
DockId=0x00000007,0
[Window][属性面板]
Pos=1477,20
Size=443,1012
Pos=1508,20
Size=412,360
Collapsed=0
DockId=0x0000000B,0
[Window][控制台]
Pos=0,528
Size=283,504
Pos=0,516
Size=283,493
Collapsed=0
DockId=0x00000008,0
@ -60,7 +60,7 @@ Collapsed=0
[Window][WindowOverViewport_11111111]
Pos=0,20
Size=1920,1012
Size=1920,989
Collapsed=0
[Window][测试窗口1]
@ -84,12 +84,12 @@ Size=400,300
Collapsed=0
[Window][选择路径]
Pos=660,266
Pos=660,254
Size=600,500
Collapsed=0
[Window][打开项目]
Pos=710,316
Pos=715,312
Size=500,400
Collapsed=0
@ -99,8 +99,8 @@ Size=600,500
Collapsed=0
[Window][资源管理器]
Pos=285,727
Size=1190,305
Pos=285,788
Size=1221,221
Collapsed=0
DockId=0x00000004,0
@ -150,8 +150,8 @@ Size=101,226
Collapsed=0
[Window][LUI编辑器]
Pos=1477,749
Size=443,283
Pos=1508,382
Size=412,627
Collapsed=0
DockId=0x0000000C,0
@ -212,17 +212,17 @@ Size=460,240
Collapsed=0
[Docking][Data]
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,20 Size=1920,1012 Split=X
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,20 Size=1920,989 Split=X
DockNode ID=0x00000005 Parent=0x08BD597D SizeRef=283,1012 Split=Y Selected=0xE0015051
DockNode ID=0x00000007 Parent=0x00000005 SizeRef=283,506 Selected=0xE0015051
DockNode ID=0x00000008 Parent=0x00000005 SizeRef=283,504 Selected=0x5428E753
DockNode ID=0x00000006 Parent=0x08BD597D SizeRef=1635,1012 Split=X
DockNode ID=0x00000001 Parent=0x00000006 SizeRef=1190,989 Split=Y
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=1321,666 Split=Y
DockNode ID=0x00000001 Parent=0x00000006 SizeRef=1221,989 Split=Y
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=1321,789 Split=Y
DockNode ID=0x00000009 Parent=0x00000003 SizeRef=1304,62 Selected=0x43A39006
DockNode ID=0x0000000A Parent=0x00000003 SizeRef=1304,618 CentralNode=1
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=1321,305 Selected=0x3A2E05C3
DockNode ID=0x00000002 Parent=0x00000006 SizeRef=443,989 Split=Y Selected=0x5DB6FF37
DockNode ID=0x0000000B Parent=0x00000002 SizeRef=443,727 Selected=0x5DB6FF37
DockNode ID=0x0000000C Parent=0x00000002 SizeRef=443,283 Selected=0x1EB923B7
DockNode ID=0x0000000A Parent=0x00000003 SizeRef=1304,725 CentralNode=1
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=1321,221 Selected=0x3A2E05C3
DockNode ID=0x00000002 Parent=0x00000006 SizeRef=412,989 Split=Y Selected=0x5DB6FF37
DockNode ID=0x0000000B Parent=0x00000002 SizeRef=443,368 Selected=0x5DB6FF37
DockNode ID=0x0000000C Parent=0x00000002 SizeRef=443,642 Selected=0x1EB923B7

View File

@ -299,8 +299,10 @@ class ProjectManager:
temp_scene_file = os.path.join(scene_dir, f"{scene_name}.tmp{scene_ext}")
final_gui_info_file = scene_file.replace('.bam', '_gui.json')
temp_gui_info_file = temp_scene_file.replace('.bam', '_gui.json')
final_lui_info_file = scene_file.replace('.bam', '_lui.json')
temp_lui_info_file = temp_scene_file.replace('.bam', '_lui.json')
for temp_path in (temp_scene_file, temp_gui_info_file):
for temp_path in (temp_scene_file, temp_gui_info_file, temp_lui_info_file):
if os.path.exists(temp_path):
try:
os.remove(temp_path)
@ -316,12 +318,15 @@ class ProjectManager:
if os.path.exists(temp_gui_info_file):
os.replace(temp_gui_info_file, final_gui_info_file)
if os.path.exists(temp_lui_info_file):
os.replace(temp_lui_info_file, final_lui_info_file)
return True
except Exception as e:
print(f"原子保存场景失败: {e}")
return False
finally:
for temp_path in (temp_scene_file, temp_gui_info_file):
for temp_path in (temp_scene_file, temp_gui_info_file, temp_lui_info_file):
if os.path.exists(temp_path):
try:
os.remove(temp_path)

View File

@ -62,6 +62,104 @@ class SceneManagerIOMixin:
except Exception as e:
print(f"清理残留场景节点失败: {e}")
def _get_scene_sidecar_path(self, filename, suffix):
scene_root, _ = os.path.splitext(os.path.normpath(filename))
return f"{scene_root}{suffix}"
def _reset_lui_runtime_state(self, lui_manager):
if lui_manager is None or getattr(lui_manager, "lui_enabled", True) is False:
return True
apply_fn = getattr(lui_manager, "_apply_lui_structure_snapshot", None)
if callable(apply_fn):
try:
apply_fn({})
return True
except Exception as e:
print(f"清空LUI结构失败: {e}")
return False
clear_fn = getattr(lui_manager, "_clear_lui_structure_runtime", None)
if callable(clear_fn):
try:
clear_fn()
return True
except Exception as e:
print(f"清空LUI结构失败: {e}")
return False
return True
def _save_lui_snapshot_file(self, filename):
lui_manager = getattr(self.world, "lui_manager", None)
if lui_manager is None or getattr(lui_manager, "lui_enabled", True) is False:
print(" 当前会话未启用LUI跳过LUI结构保存")
return True
capture_fn = getattr(lui_manager, "_capture_lui_structure_snapshot", None)
if not callable(capture_fn):
print(" 未找到LUI结构快照接口跳过LUI结构保存")
return True
try:
snapshot = capture_fn() or {}
lui_info_file = self._get_scene_sidecar_path(filename, "_lui.json")
with open(lui_info_file, "w", encoding="utf-8") as f:
json.dump(snapshot, f, ensure_ascii=False, indent=4)
canvas_count = len(snapshot.get("canvases", []) or [])
component_count = len(snapshot.get("components", []) or [])
print(
f"✓ LUI信息已保存: {lui_info_file} "
f"(Canvas: {canvas_count}, Components: {component_count})"
)
return True
except Exception as e:
print(f"✗ 保存LUI信息失败: {e}")
import traceback
traceback.print_exc()
return False
def _load_lui_snapshot_file(self, filename):
lui_manager = getattr(self.world, "lui_manager", None)
if lui_manager is None or getattr(lui_manager, "lui_enabled", True) is False:
print(" 当前会话未启用LUI跳过LUI结构恢复")
return True
lui_info_file = self._get_scene_sidecar_path(filename, "_lui.json")
apply_fn = getattr(lui_manager, "_apply_lui_structure_snapshot", None)
if not os.path.exists(lui_info_file):
print(" 未找到LUI信息文件清空当前LUI结构")
return self._reset_lui_runtime_state(lui_manager)
if not callable(apply_fn):
print(" 未找到LUI结构恢复接口跳过LUI结构恢复")
return True
try:
with open(lui_info_file, "r", encoding="utf-8") as f:
content = f.read().strip()
snapshot = json.loads(content) if content else {}
apply_fn(snapshot)
canvas_count = len(snapshot.get("canvases", []) or [])
component_count = len(snapshot.get("components", []) or [])
print(
f"✓ LUI信息已恢复: {lui_info_file} "
f"(Canvas: {canvas_count}, Components: {component_count})"
)
return True
except json.JSONDecodeError as e:
print(f"✗ LUI信息文件格式错误: {e}")
except Exception as e:
print(f"✗ 加载LUI信息失败: {e}")
import traceback
traceback.print_exc()
return self._reset_lui_runtime_state(lui_manager)
def _collectGUIElementInfo(self, gui_node):
"""收集GUI元素的信息用于保存"""
try:
@ -594,6 +692,8 @@ class SceneManagerIOMixin:
if success:
print(f"✓ 场景保存成功: {filename}")
if not self._save_lui_snapshot_file(filename):
return False
else:
print("✗ 场景保存失败")
@ -1529,6 +1629,8 @@ class SceneManagerIOMixin:
else:
print(" 未找到GUI信息文件")
self._load_lui_snapshot_file(filename)
# 移除临时场景节点
if not scene.isEmpty():
scene.removeNode()

View File

@ -324,7 +324,7 @@ class LUIFunctionPropertiesMixin:
manager.luiFunction._draw_text_font_size_editor(manager, index, comp_data, comp_obj)
draw_color = manager.luiFunction._draw_text_color_editor(manager, index, comp_data)
manager.luiFunction._sync_text_or_button_color(comp_obj, comp_type, draw_color)
manager.luiFunction._sync_text_or_button_color(manager, comp_obj, comp_type, draw_color)
if comp_type == 'Button':
manager.luiFunction._draw_button_texture_controls(manager, index, comp_data, comp_obj)
@ -387,9 +387,9 @@ class LUIFunctionPropertiesMixin:
def _sync_text_or_button_color(manager, comp_obj, comp_type, draw_color):
# 保留原行为:每帧同步当前编辑色,避免按钮状态贴图切换后颜色不同步。
if comp_type == 'Button':
manager.luiFunction._apply_button_layout_color(comp_obj, draw_color)
manager.luiFunction._apply_button_layout_color(manager, comp_obj, draw_color)
else:
manager.luiFunction._apply_text_color(comp_obj, draw_color)
manager.luiFunction._apply_text_color(manager, comp_obj, draw_color)
imgui.separator()
imgui.spacing()
@ -436,7 +436,7 @@ class LUIFunctionPropertiesMixin:
lambda: manager.luiFunction._clear_button_custom_textures(manager, comp_data, comp_obj),
)
manager.luiFunction._draw_button_texture_path_labels(comp_data)
manager.luiFunction._draw_button_texture_path_labels(manager, comp_data)
def _draw_button_texture_pick_button(manager, index, comp_data, comp_obj, button_label, variant):
if not imgui.button(button_label, (160, 20)):
@ -1382,7 +1382,7 @@ class LUIFunctionPropertiesMixin:
if changed:
manager._apply_component_change_with_history(
index,
lambda: manager.luiFunction._set_checkbox_checked(comp_data, comp_obj, new_checked),
lambda: manager.luiFunction._set_checkbox_checked(manager, comp_data, comp_obj, new_checked),
)
def _set_checkbox_checked(manager, comp_data, comp_obj, new_checked):
@ -1497,7 +1497,7 @@ class LUIFunctionPropertiesMixin:
sel_id = opt_ids[new_index]
manager._apply_component_change_with_history(
index,
lambda: manager.luiFunction._set_selectbox_selected_option(comp_data, comp_obj, sel_id),
lambda: manager.luiFunction._set_selectbox_selected_option(manager, comp_data, comp_obj, sel_id),
)
imgui.spacing()
@ -1526,7 +1526,7 @@ class LUIFunctionPropertiesMixin:
if dirty:
manager._apply_component_change_with_history(
index,
lambda: manager.luiFunction._apply_selectbox_options(comp_data, comp_obj, new_labels, current_selected),
lambda: manager.luiFunction._apply_selectbox_options(manager, comp_data, comp_obj, new_labels, current_selected),
)
def _set_selectbox_selected_option(manager, comp_data, comp_obj, selected_option_id):
@ -1682,7 +1682,7 @@ class LUIFunctionPropertiesMixin:
if imgui.button("清除音频", (90, 24)):
manager._apply_component_change_with_history(
index,
lambda: manager.luiFunction._clear_audio_for_component(comp_data),
lambda: manager.luiFunction._clear_audio_for_component(manager, comp_data),
)
if imgui.button("Select Audio", (110, 24)):
@ -1928,7 +1928,7 @@ class LUIFunctionPropertiesMixin:
def _clear_video_source(manager, comp_data, comp_obj=None):
if comp_data.get("audio_from_video"):
manager.luiFunction._clear_audio_for_component(comp_data)
manager.luiFunction._clear_audio_for_component(manager, comp_data)
manager.luiFunction._clear_video_audio_runtime(manager, comp_data)
target_obj = comp_obj or comp_data.get("object")
if target_obj is not None: