This commit is contained in:
Jeffreytsai1004 2025-02-09 23:22:48 +08:00
parent 02bc3822af
commit 4fb8654dfc
8 changed files with 1538 additions and 60 deletions

View File

@ -1,81 +1,128 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os from PySide2 import QtCore, QtGui, QtWidgets
import sys
from maya import cmds
import maya.OpenMayaUI as omui import maya.OpenMayaUI as omui
from PySide2 import QtWidgets, QtCore
from shiboken2 import wrapInstance from shiboken2 import wrapInstance
from scripts import config
from . import config from scripts.ui import menu, toolshelf, meshes, rigging, behaviour, definition
from .ui import menu, toolshelf
from .utils import maya_utils
class MetaFusion(QtWidgets.QMainWindow): class MetaFusion(QtWidgets.QMainWindow):
def __init__(self, parent=None): def __init__(self, parent=None):
super(MetaFusion, self).__init__(parent) super(MetaFusion, self).__init__(parent)
self._setup_ui()
self._create_connections()
self._load_style()
# 设置窗口标题和基本属性 def _setup_ui(self):
self.setWindowTitle("MetaFusion") """设置UI布局"""
self.setMinimumSize(1200, 800) # === 设置窗口属性 ===
self.setWindowTitle(f"{config.TOOL_NAME} {config.TOOL_VERSION}")
# 设置初始大小和最小大小
self.resize(550, 700) # 初始大小
self.setMinimumSize(550, 700) # 最小大小限制
# === 创建中心部件 ===
self.central_widget = QtWidgets.QWidget()
self.setCentralWidget(self.central_widget)
# 加载样式表 # === Main Layout ===
style_file = os.path.join(os.path.dirname(__file__), "ui", "style.qss") self.main_layout = QtWidgets.QVBoxLayout(self.central_widget)
with open(style_file, "r", encoding="utf-8") as f: self.main_layout.setContentsMargins(0, 0, 0, 0)
self.setStyleSheet(f.read()) self.main_layout.setSpacing(0)
# 创建主窗口部件
self.main_widget = QtWidgets.QWidget()
self.setCentralWidget(self.main_widget)
# 创建主布局 # === 创建菜单栏 ===
self.main_layout = QtWidgets.QVBoxLayout(self.main_widget) self.menu_bar = menu.MenuBar(self)
self.setMenuBar(self.menu_bar)
# 初始化UI组件 # === 创建工具架 ===
self._init_ui()
def _init_ui(self):
"""初始化UI组件"""
# 创建工具架
self.tool_shelf = toolshelf.ToolShelf(self) self.tool_shelf = toolshelf.ToolShelf(self)
self.main_layout.addWidget(self.tool_shelf) self.main_layout.addWidget(self.tool_shelf)
# 创建标签页 # === 创建标签页 ===
self.tab_widget = QtWidgets.QTabWidget() self.tab_widget = QtWidgets.QTabWidget()
self.main_layout.addWidget(self.tab_widget) self.main_layout.addWidget(self.tab_widget)
# 添加各个功能页面 # 添加各个功能页面
self._add_meshes_tab() self._create_tabs()
self._add_rigging_tab()
self._add_behaviour_tab()
self._add_definition_tab()
def _add_meshes_tab(self): # === 创建状态栏 ===
"""添加模型页面""" self.status_bar = self._create_status_bar()
from .ui.meshes import MeshesTab self.setStatusBar(self.status_bar)
meshes_widget = MeshesTab()
self.tab_widget.addTab(meshes_widget, "模型")
def _add_rigging_tab(self): # 设置窗口位置居中
"""添加绑定页面""" self._center_window()
from .ui.rigging import RiggingTab
rigging_widget = RiggingTab()
self.tab_widget.addTab(rigging_widget, "绑定")
def _center_window(self):
"""将窗口居中显示"""
# 获取主屏幕几何信息
screen = QtWidgets.QApplication.primaryScreen().geometry()
# 计算窗口居中位置
x = (screen.width() - self.width()) // 2
y = (screen.height() - self.height()) // 2
# 移动窗口
self.move(x, y)
def _add_behaviour_tab(self): def _create_tabs(self):
"""添加行为页面""" """创建标签页"""
from .ui.behaviour import BehaviourTab # Meshes标签页
behaviour_widget = BehaviourTab() self.meshes_tab = meshes.MeshesTab()
self.tab_widget.addTab(behaviour_widget, "行为") self.tab_widget.addTab(self.meshes_tab, "模型")
# Rigging标签页
def _add_definition_tab(self): self.rigging_tab = rigging.RiggingTab()
"""添加定义页面""" self.tab_widget.addTab(self.rigging_tab, "绑定")
from .ui.definition import DefinitionTab
definition_widget = DefinitionTab() # Behaviour标签页
self.tab_widget.addTab(definition_widget, "定义") self.behaviour_tab = behaviour.BehaviourTab()
self.tab_widget.addTab(self.behaviour_tab, "行为")
# Definition标签页
self.definition_tab = definition.DefinitionTab()
self.tab_widget.addTab(self.definition_tab, "定义")
def _create_status_bar(self):
"""创建状态栏"""
# === Widget ===
status_bar = QtWidgets.QStatusBar()
# 添加版本信息
version_label = QtWidgets.QLabel(f"{config.TOOL_VERSION}")
status_bar.addPermanentWidget(version_label)
# 添加Maya版本信息
maya_version_label = QtWidgets.QLabel(f"Maya {config.MAYA_VERSION}")
status_bar.addPermanentWidget(maya_version_label)
# 添加Python版本信息
python_version_label = QtWidgets.QLabel(f"Python {config.PYTHON_VERSION}")
status_bar.addPermanentWidget(python_version_label)
return status_bar
def _load_style(self):
"""加载样式表"""
try:
with open(config.TOOL_STYLE_FILE, "r", encoding="utf-8") as f:
style = f.read()
self.setStyleSheet(style)
except Exception as e:
print(f"加载样式表失败: {str(e)}")
def _create_connections(self):
"""创建信号连接"""
# 标签页切换
self.tab_widget.currentChanged.connect(self._on_tab_changed)
def _on_tab_changed(self, index):
"""标签页切换事件"""
tab_name = self.tab_widget.tabText(index)
self.status_bar.showMessage(f"当前页面: {tab_name}")
def closeEvent(self, event):
"""关闭窗口事件"""
# TODO: 实现关闭前的保存提示等逻辑
event.accept()
def show(): def show():
@ -87,3 +134,7 @@ def show():
global meta_fusion_window global meta_fusion_window
meta_fusion_window = MetaFusion(parent=maya_main_window) meta_fusion_window = MetaFusion(parent=maya_main_window)
meta_fusion_window.show() meta_fusion_window.show()
if __name__ == "__main__":
show()

View File

@ -32,7 +32,170 @@ except ImportError:
wrapInstance = None wrapInstance = None
class BehaviourTab(QtWidgets.QWidget): class BehaviourTab(QtWidgets.QWidget):
pass def __init__(self, parent=None):
super(BehaviourTab, self).__init__(parent)
self._setup_ui()
self._create_connections()
def _setup_ui(self):
"""设置UI布局"""
# 创建主布局
self.main_layout = QtWidgets.QHBoxLayout(self)
self.main_layout.setContentsMargins(2, 2, 2, 2)
self.main_layout.setSpacing(2)
# 创建左右两个主面板
self.raw_control_widget = self._create_raw_control()
self.blend_shapes_widget = self._create_blend_shapes()
self.main_layout.addWidget(self.raw_control_widget)
self.main_layout.addWidget(self.blend_shapes_widget)
def _create_raw_control(self):
"""创建Raw Control部分"""
# === Widget ===
widget = QtWidgets.QWidget()
# === Layout ===
layout = QtWidgets.QVBoxLayout(widget)
layout.setContentsMargins(5, 5, 5, 5)
layout.setSpacing(5)
# 标题
title_layout = QtWidgets.QHBoxLayout()
title_label = QtWidgets.QLabel("Raw Control [814/814]")
title_layout.addWidget(title_label)
layout.addLayout(title_layout)
# 搜索框
self.search_edit = QtWidgets.QLineEdit()
self.search_edit.setPlaceholderText("搜索...")
layout.addWidget(self.search_edit)
# BlendShape列表
self.blend_list = QtWidgets.QListWidget()
layout.addWidget(self.blend_list)
# 数值调节
value_layout = QtWidgets.QHBoxLayout()
self.value_spin = QtWidgets.QDoubleSpinBox()
self.value_spin.setValue(0.010)
self.value_spin.setDecimals(3)
value_layout.addWidget(self.value_spin)
layout.addLayout(value_layout)
# 过滤按钮组
filter_layout = QtWidgets.QHBoxLayout()
self.filter_buttons = []
for text in ["全部", "2", "3", "4", "5", "6"]:
btn = QtWidgets.QPushButton(text)
btn.setCheckable(True)
filter_layout.addWidget(btn)
self.filter_buttons.append(btn)
layout.addLayout(filter_layout)
return widget
def _create_blend_shapes(self):
"""创建Related Blend Shapes部分"""
# === Widget ===
widget = QtWidgets.QWidget()
# === Layout ===
layout = QtWidgets.QVBoxLayout(widget)
layout.setContentsMargins(5, 5, 5, 5)
layout.setSpacing(5)
# 标题
title_layout = QtWidgets.QHBoxLayout()
title_label = QtWidgets.QLabel("Related Blend Shapes [858/858]")
title_layout.addWidget(title_label)
layout.addLayout(title_layout)
# BlendShape列表
self.related_list = QtWidgets.QListWidget()
layout.addWidget(self.related_list)
# 数值调节
value_layout = QtWidgets.QHBoxLayout()
self.related_value_spin = QtWidgets.QDoubleSpinBox()
self.related_value_spin.setValue(0.000)
self.related_value_spin.setDecimals(3)
value_layout.addWidget(self.related_value_spin)
layout.addLayout(value_layout)
# 操作按钮组 - 第一行
op_layout1 = QtWidgets.QHBoxLayout()
op_buttons1 = [
("翻转目标", "flip.png"),
("镜像目标", "mirror.png"),
("查找翻转目标", "find_flip.png")
]
for text, icon in op_buttons1:
btn = QtWidgets.QPushButton(text)
if icon:
btn.setIcon(QtGui.QIcon(f"{config.ICONS_PATH}/{icon}"))
op_layout1.addWidget(btn)
layout.addLayout(op_layout1)
# 操作按钮组 - 第二行
op_layout2 = QtWidgets.QHBoxLayout()
op_buttons2 = [
("添加混合目标", "add.png"),
("删除混合目标", "delete.png"),
("批量混合目标", "batch.png")
]
for text, icon in op_buttons2:
btn = QtWidgets.QPushButton(text)
if icon:
btn.setIcon(QtGui.QIcon(f"{config.ICONS_PATH}/{icon}"))
op_layout2.addWidget(btn)
layout.addLayout(op_layout2)
# 功能按钮组
func_layout = QtWidgets.QHBoxLayout()
func_buttons = ["PSD", "BSE", "KEY", "MIR", "ARK", "CTR"]
for text in func_buttons:
btn = QtWidgets.QPushButton(text)
btn.setFixedWidth(40)
func_layout.addWidget(btn)
layout.addLayout(func_layout)
return widget
def _create_connections(self):
"""创建信号连接"""
# 搜索框
self.search_edit.textChanged.connect(self._filter_blend_shapes)
# 数值调节
self.value_spin.valueChanged.connect(self._update_raw_value)
self.related_value_spin.valueChanged.connect(self._update_related_value)
# 过滤按钮组
for btn in self.filter_buttons:
btn.clicked.connect(self._apply_filter)
# ================================ 事件函数 ================================
def _filter_blend_shapes(self, text):
"""过滤BlendShape列表"""
# TODO: 实现过滤逻辑
pass
def _update_raw_value(self, value):
"""更新Raw Control值"""
# TODO: 实现更新逻辑
pass
def _update_related_value(self, value):
"""更新Related Blend Shapes值"""
# TODO: 实现更新逻辑
pass
def _apply_filter(self):
"""应用过滤器"""
# TODO: 实现过滤逻辑
pass
if __name__ == "__main__": if __name__ == "__main__":
pass pass

View File

@ -32,7 +32,261 @@ except ImportError:
wrapInstance = None wrapInstance = None
class DefinitionTab(QtWidgets.QWidget): class DefinitionTab(QtWidgets.QWidget):
pass def __init__(self, parent=None):
super(DefinitionTab, self).__init__(parent)
self._setup_ui()
self._create_connections()
def _setup_ui(self):
"""设置UI布局"""
# === Main Layout ===
self.main_layout = QtWidgets.QHBoxLayout(self)
self.main_layout.setContentsMargins(2, 2, 2, 2)
self.main_layout.setSpacing(2)
# 创建左侧和右侧面板
self.left_panel = self._create_left_panel()
self.right_panel = self._create_right_panel()
self.main_layout.addWidget(self.left_panel, 1) # 1:3比例
self.main_layout.addWidget(self.right_panel, 3)
def _create_left_panel(self):
"""创建左侧面板"""
# === Widget ===
widget = QtWidgets.QWidget()
# === Layout ===
layout = QtWidgets.QVBoxLayout(widget)
layout.setContentsMargins(5, 5, 5, 5)
layout.setSpacing(5)
# LODs列表
lod_group = QtWidgets.QGroupBox("LODs")
lod_layout = QtWidgets.QVBoxLayout()
self.lod_list = QtWidgets.QListWidget()
for i in range(8):
self.lod_list.addItem(f"LOD {i}")
lod_layout.addWidget(self.lod_list)
# Define Joint按钮
self.define_joint_btn = QtWidgets.QPushButton("Define Joint For LOD")
lod_layout.addWidget(self.define_joint_btn)
lod_group.setLayout(lod_layout)
layout.addWidget(lod_group)
return widget
def _create_right_panel(self):
"""创建右侧面板"""
# === Widget ===
widget = QtWidgets.QWidget()
# === Layout ===
layout = QtWidgets.QVBoxLayout(widget)
layout.setContentsMargins(5, 5, 5, 5)
layout.setSpacing(5)
# 创建各个部分
self.meshes_widget = self._create_meshes_section()
self.joints_widget = self._create_joints_section()
self.blend_shapes_widget = self._create_blend_shapes_section()
self.animated_map_widget = self._create_animated_map_section()
# 添加到布局
layout.addWidget(self.meshes_widget)
layout.addWidget(self.joints_widget)
layout.addWidget(self.blend_shapes_widget)
layout.addWidget(self.animated_map_widget)
# 添加操作按钮区域
self.operation_widget = self._create_operation_section()
layout.addWidget(self.operation_widget)
return widget
def _create_meshes_section(self):
"""创建Meshes部分"""
# === Widget ===
group = QtWidgets.QGroupBox("Meshes [010/54]")
# === Layout ===
layout = QtWidgets.QVBoxLayout(group)
# 列表
self.meshes_list = QtWidgets.QListWidget()
meshes = [
"head_lod0_mesh (000)",
"teeth_lod0_mesh (001)",
"saliva_lod0_mesh (002)",
"eyeLeft_lod0_mesh (003)",
"eyeRight_lod0_mesh (004)",
"eyeshell_lod0_mesh (005)",
"eyelashes_lod0_mesh (006)",
"eyeEdge_lod0_mesh (007)"
]
for mesh in meshes:
self.meshes_list.addItem(mesh)
layout.addWidget(self.meshes_list)
# Create Geometry按钮
self.create_geo_btn = QtWidgets.QPushButton("Create Geometry")
layout.addWidget(self.create_geo_btn)
return group
def _create_joints_section(self):
"""创建Joints部分"""
# === Widget ===
group = QtWidgets.QGroupBox("Joints [840/870]")
# === Layout ===
layout = QtWidgets.QVBoxLayout(group)
# 列表
self.joints_list = QtWidgets.QListWidget()
joints = [
"FACIAL_C_NeckB Group: 001",
"FACIAL_L_NeckB1 Group: 002",
"FACIAL_R_NeckB1 Group: 003",
"FACIAL_L_NeckB2 Group: 002",
"FACIAL_R_NeckB2 Group: 003",
"FACIAL_C12PV_NeckB1 Group: 001"
]
for joint in joints:
self.joints_list.addItem(joint)
layout.addWidget(self.joints_list)
return group
def _create_blend_shapes_section(self):
"""创建Blend Shapes部分"""
# === Widget ===
group = QtWidgets.QGroupBox("Blend Shapes [782/782]")
# === Layout ===
layout = QtWidgets.QVBoxLayout(group)
# 列表
self.blend_shapes_list = QtWidgets.QListWidget()
shapes = [
"brow_down_L 000",
"brow_down_R 001",
"brow_lateral_L 002",
"brow_lateral_R 003",
"brow_raiseIn_L 004",
"brow_raiseIn_R 005"
]
for shape in shapes:
self.blend_shapes_list.addItem(shape)
layout.addWidget(self.blend_shapes_list)
return group
def _create_animated_map_section(self):
"""创建AnimatedMap部分"""
# === Widget ===
group = QtWidgets.QGroupBox("AnimatedMap [082/82]")
# === Layout ===
layout = QtWidgets.QVBoxLayout(group)
# 列表
self.animated_map_list = QtWidgets.QListWidget()
maps = [
"head_cm2_color.head_wm2.browsDown 000",
"head_cm2_color.head_wm2.browsDown 001",
"head_cm2_color.head_wm2.browsLate 002",
"head_cm2_color.head_wm2.browsLate 003",
"head_cm1_color.head_wm1.browsRais 004",
"head_cm1_color.head_wm1.browsRais 005"
]
for map_item in maps:
self.animated_map_list.addItem(map_item)
layout.addWidget(self.animated_map_list)
return group
def _create_operation_section(self):
"""创建操作区域"""
# === Widget ===
widget = QtWidgets.QWidget()
# === Layout ===
layout = QtWidgets.QVBoxLayout(widget)
# Write部分
write_group = QtWidgets.QGroupBox("Write")
write_layout = QtWidgets.QVBoxLayout()
write_buttons = [
"Write Neutral Pose Joint Position",
"Write Geometry",
"Write Skin Weights",
"Write Blend Shapes Targets"
]
for text in write_buttons:
btn = QtWidgets.QPushButton(text)
write_layout.addWidget(btn)
write_group.setLayout(write_layout)
layout.addWidget(write_group)
# Create部分
create_group = QtWidgets.QGroupBox("Create")
create_layout = QtWidgets.QVBoxLayout()
create_buttons = [
"Create Blend Shapes For Mesh",
"Bind Skin For Mesh",
"Unbind Skin For Mesh"
]
for text in create_buttons:
btn = QtWidgets.QPushButton(text)
create_layout.addWidget(btn)
create_group.setLayout(create_layout)
layout.addWidget(create_group)
# Tools部分
tools_group = QtWidgets.QGroupBox("Tools")
tools_layout = QtWidgets.QVBoxLayout()
tools_buttons = [
"New Neutral Joint Transform",
"Quickly Create Presets"
]
for text in tools_buttons:
btn = QtWidgets.QPushButton(text)
tools_layout.addWidget(btn)
tools_group.setLayout(tools_layout)
layout.addWidget(tools_group)
return widget
def _create_connections(self):
"""创建信号连接"""
# LOD列表选择
self.lod_list.currentItemChanged.connect(self._on_lod_changed)
# Define Joint按钮
self.define_joint_btn.clicked.connect(self._on_define_joint)
# Create Geometry按钮
self.create_geo_btn.clicked.connect(self._on_create_geometry)
# ================================ 事件函数 ================================
def _on_lod_changed(self, current, previous):
"""LOD选择改变"""
# TODO: 实现LOD切换逻辑
pass
def _on_define_joint(self):
"""Define Joint按钮点击"""
# TODO: 实现Define Joint逻辑
pass
def _on_create_geometry(self):
"""Create Geometry按钮点击"""
# TODO: 实现Create Geometry逻辑
pass
if __name__ == "__main__": if __name__ == "__main__":
pass pass

View File

@ -31,9 +31,233 @@ except ImportError:
QtCore = QtGui = QtWidgets = None QtCore = QtGui = QtWidgets = None
wrapInstance = None wrapInstance = None
class Menu(QtWidgets.QWidget): class MenuBar(QtWidgets.QMenuBar):
pass def __init__(self, parent=None):
super(MenuBar, self).__init__(parent)
self._setup_ui()
self._create_connections()
def _setup_ui(self):
"""设置UI布局"""
# 创建主菜单
self.file_menu = self._create_file_menu()
self.edit_menu = self._create_edit_menu()
self.tools_menu = self._create_tools_menu()
self.language_menu = self._create_language_menu()
self.help_menu = self._create_help_menu()
# 添加到菜单栏
self.addMenu(self.file_menu)
self.addMenu(self.edit_menu)
self.addMenu(self.tools_menu)
self.addMenu(self.language_menu)
self.addMenu(self.help_menu)
def _create_file_menu(self):
"""创建文件菜单"""
# === Widget ===
menu = QtWidgets.QMenu("文件")
# === Actions ===
self.file_actions = {}
actions = [
("open_dna", "打开 DNA", "open.png", "Ctrl+O"),
("save_dna", "保存 DNA", "save.png", "Ctrl+S"),
("load_current", "加载当前项目 DNA", "open.png", ""),
None, # 分隔符
("rename_blend", "修改混合目标名称", "rename.png", ""),
("reset_blend", "重置混合目标名称", "resetname.png", ""),
None,
("export_fbx", "导出 FBX", "export.png", ""),
None,
("exit", "退出", "exit.png", "Alt+F4")
]
self._add_menu_actions(menu, actions, self.file_actions)
return menu
def _create_edit_menu(self):
"""创建编辑菜单"""
# === Widget ===
menu = QtWidgets.QMenu("编辑")
# === Actions ===
self.edit_actions = {}
actions = [
("create_rl4", "创建 RL4 节点", "connect.png", ""),
("delete_rl4", "删除 RL4 节点", "disconnect.png", ""),
None,
("mirror_l2r", "镜像左至右", "mirrorL.png", ""),
("mirror_r2l", "镜像右至左", "mirrorR.png", ""),
None,
("pose_a2t", "姿势由 A 型转 T 型", "pose_A_To_T.png", ""),
("pose_t2a", "姿势由 T 型转 A 型", "pose_T_To_A.png", ""),
None,
("transfer_lod", "传输 LOD 贴图", "locator.png", ""),
("set_joint_color", "设置关节颜色", "color.png", ""),
None,
("unmark_all", "取消全部标记", "unmark_all.png", ""),
("rebuild_targets", "重建所有目标", "rebuildTargets.png", ""),
None,
("set_expression_keys", "为所有表情设置关键帧", "bakeAnimation.png", ""),
("bake_expression_keys", "烘焙所有表情的关键帧", "centerCurrentTime.png", "")
]
self._add_menu_actions(menu, actions, self.edit_actions)
return menu
def _create_tools_menu(self):
"""创建工具菜单"""
# === Widget ===
menu = QtWidgets.QMenu("工具")
# === Actions ===
self.tools_actions = {}
actions = [
("export_skin", "导出蒙皮", "export_skin.png", ""),
("import_skin", "导入蒙皮", "import_skin.png", ""),
("copy_skin", "拷贝装皮", "copy_skin.png", ""),
None,
("rbf_deformer", "RBF变形器", "blendShape.png", ""),
("quick_bind", "快速绑定服装", "clothing_weight.png", ""),
("clone_blend", "克隆混合变形", "blendShape.png", ""),
None,
("transfer_uv", "UV传递点序", "repair_vertex_order.png", ""),
("create_face_ctrl", "面部生成控制器", "controller.png", ""),
("extract_52bs", "提取52BS", "ARKit52.png", ""),
None,
("fix_joint_axis", "关节轴向修复", "joint.png", ""),
("create_body_ctrl", "生成身体控制器", "create_body_ctrl.png", ""),
None,
("import_face_anim", "导入面部动画", "import_face_anim.png", ""),
("import_body_anim", "导入身体动画", "import_body_anim.png", "")
]
self._add_menu_actions(menu, actions, self.tools_actions)
return menu
def _create_language_menu(self):
"""创建语言菜单"""
# === Widget ===
menu = QtWidgets.QMenu("语言")
# === Actions ===
self.language_actions = {}
actions = [
("chinese", "中文", "chinese.png", ""),
("english", "English", "english.png", "")
]
self._add_menu_actions(menu, actions, self.language_actions)
return menu
def _create_help_menu(self):
"""创建帮助菜单"""
# === Widget ===
menu = QtWidgets.QMenu("帮助")
# === Actions ===
self.help_actions = {}
actions = [
("check_update", "检查更新", "update.png", ""),
("help", "帮助文档", "help.png", "F1"),
("about", "关于", "about.png", "")
]
self._add_menu_actions(menu, actions, self.help_actions)
return menu
def _add_menu_actions(self, menu, actions, action_dict):
"""添加菜单项"""
for action in actions:
if action is None:
menu.addSeparator()
else:
action_id, name, icon, shortcut = action
act = QtWidgets.QAction(name, self)
if icon:
act.setIcon(QtGui.QIcon(f"{config.ICONS_PATH}/{icon}"))
if shortcut:
act.setShortcut(shortcut)
menu.addAction(act)
action_dict[action_id] = act
def _create_connections(self):
"""创建信号连接"""
# 文件菜单
self.file_actions["open_dna"].triggered.connect(self._on_open_dna)
self.file_actions["save_dna"].triggered.connect(self._on_save_dna)
self.file_actions["exit"].triggered.connect(self._on_exit)
# 编辑菜单
self.edit_actions["create_rl4"].triggered.connect(self._on_create_rl4)
self.edit_actions["delete_rl4"].triggered.connect(self._on_delete_rl4)
# 工具菜单
self.tools_actions["export_skin"].triggered.connect(self._on_export_skin)
self.tools_actions["import_skin"].triggered.connect(self._on_import_skin)
# 语言菜单
self.language_actions["chinese"].triggered.connect(lambda: self._on_language_change("zh"))
self.language_actions["english"].triggered.connect(lambda: self._on_language_change("en"))
# 帮助菜单
self.help_actions["help"].triggered.connect(self._on_help)
self.help_actions["about"].triggered.connect(self._on_about)
# ================================ 事件函数 ================================
def _on_open_dna(self):
"""打开DNA"""
# TODO: 实现打开DNA逻辑
pass
def _on_save_dna(self):
"""保存DNA"""
# TODO: 实现保存DNA逻辑
pass
def _on_exit(self):
"""退出程序"""
# TODO: 实现退出逻辑
pass
def _on_create_rl4(self):
"""创建RL4节点"""
# TODO: 实现创建RL4节点逻辑
pass
def _on_delete_rl4(self):
"""删除RL4节点"""
# TODO: 实现删除RL4节点逻辑
pass
def _on_export_skin(self):
"""导出蒙皮"""
# TODO: 实现导出蒙皮逻辑
pass
def _on_import_skin(self):
"""导入蒙皮"""
# TODO: 实现导入蒙皮逻辑
pass
def _on_language_change(self, lang):
"""切换语言"""
# TODO: 实现语言切换逻辑
pass
def _on_help(self):
"""显示帮助文档"""
# TODO: 实现显示帮助逻辑
pass
def _on_about(self):
"""显示关于对话框"""
# TODO: 实现显示关于对话框逻辑
pass
if __name__ == "__main__": if __name__ == "__main__":
pass pass

View File

@ -32,7 +32,221 @@ except ImportError:
wrapInstance = None wrapInstance = None
class MeshesTab(QtWidgets.QWidget): class MeshesTab(QtWidgets.QWidget):
pass def __init__(self, parent=None):
super(MeshesTab, self).__init__(parent)
self._setup_ui()
self._create_connections()
def _setup_ui(self):
"""设置UI布局"""
# === Main Layout ===
self.main_layout = QtWidgets.QVBoxLayout(self)
self.main_layout.setContentsMargins(2, 2, 2, 2)
self.main_layout.setSpacing(2)
# 创建各个部件
self.toolbar_widget = self._create_toolbar()
self.lod_tab_widget = self._create_lod_tabs()
self.bottom_toolbar_widget = self._create_bottom_toolbar()
# 添加到主布局
self.main_layout.addWidget(self.toolbar_widget)
self.main_layout.addWidget(self.lod_tab_widget)
self.main_layout.addWidget(self.bottom_toolbar_widget)
def _create_toolbar(self):
"""创建顶部工具栏"""
# === Widget ===
widget = QtWidgets.QWidget()
# === Layout ===
layout = QtWidgets.QHBoxLayout(widget)
layout.setContentsMargins(2, 2, 2, 2)
layout.setSpacing(2)
# 预设下拉框
self.preset_combo = QtWidgets.QComboBox()
self.preset_combo.addItem("MetaHuman")
layout.addWidget(self.preset_combo)
# 垃圾桶按钮
self.trash_btn = QtWidgets.QPushButton()
self.trash_btn.setIcon(QtGui.QIcon(f"{config.ICONS_PATH}/trash.png"))
self.trash_btn.setFixedSize(24, 24)
layout.addWidget(self.trash_btn)
layout.addStretch()
return widget
def _create_lod_tabs(self):
"""创建LOD选项卡"""
# === Widget ===
self.lod_tab = QtWidgets.QTabWidget()
# 添加LOD页面
for i in range(8):
lod_widget = self._create_lod_page(i)
self.lod_tab.addTab(lod_widget, f"LOD{i}")
return self.lod_tab
def _create_lod_page(self, lod_index):
"""创建单个LOD页面"""
# === Widget ===
widget = QtWidgets.QWidget()
# === Layout ===
layout = QtWidgets.QVBoxLayout(widget)
layout.setContentsMargins(5, 5, 5, 5)
layout.setSpacing(2)
# 创建网格列表
scroll_area = QtWidgets.QScrollArea()
scroll_area.setWidgetResizable(True)
scroll_widget = QtWidgets.QWidget()
scroll_layout = QtWidgets.QVBoxLayout(scroll_widget)
# 获取当前LOD级别的网格列表
meshes = config.LOD_MESHES[f"LOD{lod_index}"]
self.mesh_items = [] # 存储所有mesh item的引用
for mesh in meshes:
mesh_item = self._create_mesh_item(mesh)
scroll_layout.addWidget(mesh_item)
self.mesh_items.append(mesh_item)
scroll_layout.addStretch()
scroll_area.setWidget(scroll_widget)
layout.addWidget(scroll_area)
# 添加底部按钮
button_widget = QtWidgets.QWidget()
button_layout = QtWidgets.QHBoxLayout(button_widget)
button_layout.setContentsMargins(0, 0, 0, 0)
self.auto_select_btn = QtWidgets.QPushButton("自动选择模型")
self.standardize_btn = QtWidgets.QPushButton("标准化命名")
self.auto_group_btn = QtWidgets.QPushButton("自动分组")
button_layout.addWidget(self.auto_select_btn)
button_layout.addWidget(self.standardize_btn)
button_layout.addWidget(self.auto_group_btn)
layout.addWidget(button_widget)
return widget
def _create_mesh_item(self, mesh_name):
"""创建单个网格项"""
# === Widget ===
widget = QtWidgets.QWidget()
# === Layout ===
layout = QtWidgets.QHBoxLayout(widget)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(2)
# 网格名称标签
label = QtWidgets.QLabel(mesh_name)
label.setFixedWidth(80)
layout.addWidget(label)
# 网格路径输入框
path_edit = QtWidgets.QLineEdit()
layout.addWidget(path_edit)
# 添加按钮
add_btn = QtWidgets.QPushButton("添加...")
add_btn.setFixedWidth(60)
layout.addWidget(add_btn)
return widget
def _create_bottom_toolbar(self):
"""创建底部工具栏"""
# === Widget ===
widget = QtWidgets.QWidget()
# === Layout ===
layout = QtWidgets.QHBoxLayout(widget)
layout.setContentsMargins(5, 5, 5, 5)
layout.setSpacing(5)
# 预设选择
preset_label = QtWidgets.QLabel("预设:")
self.preset_combo_bottom = QtWidgets.QComboBox()
self.preset_combo_bottom.addItem("MetaHuman")
# LOD选择
lod_label = QtWidgets.QLabel("选择LOD:")
self.lod_combo = QtWidgets.QComboBox()
self.lod_combo.addItem("全部")
# 功能按钮
self.create_lod_btn = QtWidgets.QPushButton("生成当前LOD")
self.modify_btn = QtWidgets.QPushButton("修复线性")
self.save_btn = QtWidgets.QPushButton("修复存储")
# 添加到布局
layout.addWidget(preset_label)
layout.addWidget(self.preset_combo_bottom)
layout.addStretch()
layout.addWidget(lod_label)
layout.addWidget(self.lod_combo)
layout.addWidget(self.create_lod_btn)
layout.addWidget(self.modify_btn)
layout.addWidget(self.save_btn)
return widget
def _create_connections(self):
"""创建信号连接"""
# 顶部工具栏
self.trash_btn.clicked.connect(self._on_trash_clicked)
self.preset_combo.currentIndexChanged.connect(self._on_preset_changed)
# LOD页面按钮
for i in range(self.lod_tab.count()):
page = self.lod_tab.widget(i)
auto_select_btn = page.findChild(QtWidgets.QPushButton, "自动选择模型")
if auto_select_btn:
auto_select_btn.clicked.connect(self._on_auto_select)
# 底部工具栏
self.create_lod_btn.clicked.connect(self._on_create_lod)
self.modify_btn.clicked.connect(self._on_modify)
self.save_btn.clicked.connect(self._on_save)
def _on_trash_clicked(self):
"""垃圾桶按钮点击"""
# TODO: 实现清除功能
pass
def _on_preset_changed(self, index):
"""预设改变"""
# TODO: 实现预设切换
pass
def _on_auto_select(self):
"""自动选择模型"""
# TODO: 实现自动选择
pass
def _on_create_lod(self):
"""生成当前LOD"""
# TODO: 实现LOD生成
pass
def _on_modify(self):
"""修复线性"""
# TODO: 实现修复
pass
def _on_save(self):
"""修复存储"""
# TODO: 实现存储
pass
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -32,8 +32,235 @@ except ImportError:
wrapInstance = None wrapInstance = None
class RiggingTab(QtWidgets.QWidget): class RiggingTab(QtWidgets.QWidget):
pass def __init__(self, parent=None):
super(RiggingTab, self).__init__(parent)
self._setup_ui()
self._create_connections()
def _setup_ui(self):
"""设置UI布局"""
# === Main Layout ===
self.main_layout = QtWidgets.QHBoxLayout(self)
self.main_layout.setContentsMargins(2, 2, 2, 2)
self.main_layout.setSpacing(2)
# 创建左右两个主面板
self.dna_browser_widget = self._create_dna_browser()
self.asset_settings_widget = self._create_asset_settings()
# 设置左右比例为1:1
self.main_layout.addWidget(self.dna_browser_widget, 1)
self.main_layout.addWidget(self.asset_settings_widget, 1)
def _create_dna_browser(self):
"""创建DNA预设浏览器"""
# === Widget ===
widget = QtWidgets.QWidget()
# === Layout ===
layout = QtWidgets.QVBoxLayout(widget)
layout.setContentsMargins(5, 5, 5, 5)
layout.setSpacing(5)
# 预设网格视图
self.preset_view = QtWidgets.QListWidget()
self.preset_view.setViewMode(QtWidgets.QListView.IconMode)
self.preset_view.setIconSize(QtCore.QSize(90, 90))
self.preset_view.setSpacing(10)
self.preset_view.setResizeMode(QtWidgets.QListView.Adjust)
self.preset_view.setMovement(QtWidgets.QListView.Static)
self.preset_view.setWrapping(True)
layout.addWidget(self.preset_view)
# 缩放控制
zoom_widget = QtWidgets.QWidget()
zoom_layout = QtWidgets.QHBoxLayout(zoom_widget)
zoom_layout.setContentsMargins(0, 0, 0, 0)
zoom_label = QtWidgets.QLabel("缩放:")
self.zoom_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.zoom_slider.setMinimum(60)
self.zoom_slider.setMaximum(200)
self.zoom_slider.setValue(90)
self.zoom_value_label = QtWidgets.QLabel("90")
zoom_layout.addWidget(zoom_label)
zoom_layout.addWidget(self.zoom_slider)
zoom_layout.addWidget(self.zoom_value_label)
layout.addWidget(zoom_widget)
# 导入/导出按钮
button_widget = QtWidgets.QWidget()
button_layout = QtWidgets.QHBoxLayout(button_widget)
button_layout.setContentsMargins(0, 0, 0, 0)
self.import_btn = QtWidgets.QPushButton("导入设置")
self.export_btn = QtWidgets.QPushButton("导出设置")
button_layout.addWidget(self.import_btn)
button_layout.addWidget(self.export_btn)
layout.addWidget(button_widget)
return widget
def _create_asset_settings(self):
"""创建资产设置面板"""
# === Widget ===
widget = QtWidgets.QWidget()
# === Layout ===
layout = QtWidgets.QVBoxLayout(widget)
layout.setContentsMargins(5, 5, 5, 5)
layout.setSpacing(5)
# 项目路径设置
self.project_group = self._create_project_settings()
layout.addWidget(self.project_group)
# 数据分层选项
self.layer_group = self._create_layer_settings()
layout.addWidget(self.layer_group)
# 描述设置
self.desc_group = self._create_description_settings()
layout.addWidget(self.desc_group)
layout.addStretch()
return widget
def _create_project_settings(self):
"""创建项目设置组"""
# === Widget ===
group = QtWidgets.QGroupBox("项目设置")
# === Layout ===
layout = QtWidgets.QFormLayout(group)
layout.setContentsMargins(5, 5, 5, 5)
layout.setSpacing(5)
# 项目路径
path_widget = QtWidgets.QWidget()
path_layout = QtWidgets.QHBoxLayout(path_widget)
path_layout.setContentsMargins(0, 0, 0, 0)
self.project_path = QtWidgets.QLineEdit()
self.browse_project_btn = QtWidgets.QPushButton("浏览...")
self.browse_project_btn.setFixedWidth(60)
path_layout.addWidget(self.project_path)
path_layout.addWidget(self.browse_project_btn)
layout.addRow("项目路径:", path_widget)
# 预设文件
preset_widget = QtWidgets.QWidget()
preset_layout = QtWidgets.QHBoxLayout(preset_widget)
preset_layout.setContentsMargins(0, 0, 0, 0)
self.preset_path = QtWidgets.QLineEdit()
self.browse_preset_btn = QtWidgets.QPushButton("浏览...")
self.browse_preset_btn.setFixedWidth(60)
preset_layout.addWidget(self.preset_path)
preset_layout.addWidget(self.browse_preset_btn)
layout.addRow("预设文件:", preset_widget)
return group
def _create_layer_settings(self):
"""创建数据分层设置组"""
# === Widget ===
group = QtWidgets.QGroupBox("数据分层")
# === Layout ===
layout = QtWidgets.QVBoxLayout(group)
layout.setContentsMargins(5, 5, 5, 5)
layout.setSpacing(5)
self.behavior_check = QtWidgets.QCheckBox("行为(包括描述和定义)")
self.expression_check = QtWidgets.QCheckBox("覆盖表情选项")
layout.addWidget(self.behavior_check)
layout.addWidget(self.expression_check)
return group
def _create_description_settings(self):
"""创建描述设置组"""
# === Widget ===
group = QtWidgets.QGroupBox("描述设置")
# === Layout ===
layout = QtWidgets.QFormLayout(group)
layout.setContentsMargins(5, 5, 5, 5)
layout.setSpacing(5)
# 创建设置项
self.name_edit = QtWidgets.QLineEdit("Archetype")
self.type_combo = QtWidgets.QComboBox()
self.type_combo.addItem("其他")
self.gender_combo = QtWidgets.QComboBox()
self.gender_combo.addItem("女性")
self.age_spin = QtWidgets.QSpinBox()
self.age_spin.setValue(24)
self.transform_combo = QtWidgets.QComboBox()
self.transform_combo.addItem("厘米")
self.rotation_combo = QtWidgets.QComboBox()
self.rotation_combo.addItem("角度")
self.up_axis_combo = QtWidgets.QComboBox()
self.up_axis_combo.addItem("Z轴向上")
self.lod_spin = QtWidgets.QSpinBox()
self.lod_spin.setValue(8)
# 添加到布局
layout.addRow("名称:", self.name_edit)
layout.addRow("原型:", self.type_combo)
layout.addRow("性别:", self.gender_combo)
layout.addRow("年龄:", self.age_spin)
layout.addRow("变换单位:", self.transform_combo)
layout.addRow("旋转单位:", self.rotation_combo)
layout.addRow("向上轴标:", self.up_axis_combo)
layout.addRow("LOD计数:", self.lod_spin)
return group
def _create_connections(self):
"""创建信号连接"""
# 缩放滑块
self.zoom_slider.valueChanged.connect(self._on_zoom_changed)
# 导入导出按钮
self.import_btn.clicked.connect(self._on_import)
self.export_btn.clicked.connect(self._on_export)
# 浏览按钮
self.browse_project_btn.clicked.connect(self._on_browse_project)
self.browse_preset_btn.clicked.connect(self._on_browse_preset)
def _on_zoom_changed(self, value):
"""缩放值改变"""
self.zoom_value_label.setText(str(value))
self.preset_view.setIconSize(QtCore.QSize(value, value))
def _on_import(self):
"""导入设置"""
# TODO: 实现导入逻辑
pass
def _on_export(self):
"""导出设置"""
# TODO: 实现导出逻辑
pass
def _on_browse_project(self):
"""浏览项目路径"""
# TODO: 实现浏览逻辑
pass
def _on_browse_preset(self):
"""浏览预设文件"""
# TODO: 实现浏览逻辑
pass
if __name__ == "__main__": if __name__ == "__main__":
pass pass

139
scripts/ui/toolshelf.py Normal file
View File

@ -0,0 +1,139 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from PySide2 import QtCore, QtGui, QtWidgets
from scripts import config
class ToolShelf(QtWidgets.QWidget):
def __init__(self, parent=None):
super(ToolShelf, self).__init__(parent)
self._setup_ui()
self._create_connections()
def _setup_ui(self):
"""设置UI布局"""
# === Main Layout ===
self.main_layout = QtWidgets.QHBoxLayout(self)
self.main_layout.setContentsMargins(2, 2, 2, 2)
self.main_layout.setSpacing(4)
# 创建工具按钮
self.tool_buttons = {}
self._create_tool_buttons()
def _create_tool_buttons(self):
"""创建工具按钮"""
# === 工具按钮配置 ===
tools = [
("save_dna", "保存 DNA", "save.png"),
("load_dna", "加载当前项目 DNA", "open.png"),
None, # 分隔符
("create_rl4", "创建RL4节点", "connect.png"),
("delete_rl4", "删除RL4节点", "disconnect.png"),
None,
("export_skin", "导出蒙皮", "export_skin.png"),
("import_skin", "导入蒙皮", "import_skin.png"),
("copy_skin", "复制蒙皮", "copy_skin.png"),
None,
("help", "帮助文档", "help.png"),
("about", "关于", "about.png")
]
# === 创建按钮 ===
for tool in tools:
if tool is None:
# 添加分隔符
separator = QtWidgets.QFrame()
separator.setFrameShape(QtWidgets.QFrame.VLine)
separator.setFrameShadow(QtWidgets.QFrame.Sunken)
self.main_layout.addWidget(separator)
else:
tool_id, name, icon = tool
button = self._create_tool_button(name, icon)
self.tool_buttons[tool_id] = button
self.main_layout.addWidget(button)
# 添加弹性空间
self.main_layout.addStretch()
def _create_tool_button(self, name, icon):
"""创建单个工具按钮"""
# === Widget ===
button = QtWidgets.QToolButton()
# === 设置按钮属性 ===
button.setIcon(QtGui.QIcon(f"{config.ICONS_PATH}/{icon}"))
button.setIconSize(QtCore.QSize(24, 24))
button.setToolTip(name)
button.setStatusTip(name)
# 设置按钮样式
button.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly)
button.setAutoRaise(True) # 鼠标悬停时才显示边框
return button
def _create_connections(self):
"""创建信号连接"""
# 保存/加载DNA
self.tool_buttons["save_dna"].clicked.connect(self._on_save_dna)
self.tool_buttons["load_dna"].clicked.connect(self._on_load_dna)
# RL4节点
self.tool_buttons["create_rl4"].clicked.connect(self._on_create_rl4)
self.tool_buttons["delete_rl4"].clicked.connect(self._on_delete_rl4)
# 蒙皮操作
self.tool_buttons["export_skin"].clicked.connect(self._on_export_skin)
self.tool_buttons["import_skin"].clicked.connect(self._on_import_skin)
self.tool_buttons["copy_skin"].clicked.connect(self._on_copy_skin)
# 帮助/关于
self.tool_buttons["help"].clicked.connect(self._on_help)
self.tool_buttons["about"].clicked.connect(self._on_about)
# ================================ 事件函数 ================================
def _on_save_dna(self):
"""保存DNA"""
# TODO: 实现保存DNA逻辑
pass
def _on_load_dna(self):
"""加载DNA"""
# TODO: 实现加载DNA逻辑
pass
def _on_create_rl4(self):
"""创建RL4节点"""
# TODO: 实现创建RL4节点逻辑
pass
def _on_delete_rl4(self):
"""删除RL4节点"""
# TODO: 实现删除RL4节点逻辑
pass
def _on_export_skin(self):
"""导出蒙皮"""
# TODO: 实现导出蒙皮逻辑
pass
def _on_import_skin(self):
"""导入蒙皮"""
# TODO: 实现导入蒙皮逻辑
pass
def _on_copy_skin(self):
"""复制蒙皮"""
# TODO: 实现复制蒙皮逻辑
pass
def _on_help(self):
"""显示帮助文档"""
# TODO: 实现显示帮助逻辑
pass
def _on_about(self):
"""显示关于对话框"""
# TODO: 实现显示关于对话框逻辑
pass

View File

@ -9,3 +9,209 @@ import maya.mel as mel
import webbrowser import webbrowser
import sys import sys
import os import os
from PySide2 import QtCore, QtGui, QtWidgets
class ToolShelf(QtWidgets.QToolBar):
def __init__(self, parent=None):
super(ToolShelf, self).__init__(parent)
self.setIconSize(QtCore.QSize(24, 24))
self.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly)
# 添加工具按钮
self._add_tools()
def _add_tools(self):
"""添加工具按钮"""
tools = [
("保存 DNA", "save.png"),
("加载当前项目 DNA", "open.png"),
None, # 分隔符
("创建RL4节点", "connect.png"),
("删除RL4节点", "disconnect.png"),
None,
("导出蒙皮", "export_skin.png"),
("导入蒙皮", "import_skin.png"),
("复制蒙皮", "copy_skin.png"),
None,
("帮助文档", "help.png"),
("关于", "about.png")
]
for tool in tools:
if tool is None:
self.addSeparator()
else:
name, icon = tool
action = QtWidgets.QAction(QtGui.QIcon(f"{config.ICONS_PATH}/{icon}"), name, self)
self.addAction(action)
class IconButton(QtWidgets.QPushButton):
"""带图标的按钮"""
def __init__(self, icon_name, tooltip="", size=24, parent=None):
super(IconButton, self).__init__(parent)
self._setup_ui(icon_name, tooltip, size)
def _setup_ui(self, icon_name, tooltip, size):
# 设置图标
icon = QtGui.QIcon(f"{config.ICONS_PATH}/{icon_name}")
self.setIcon(icon)
self.setIconSize(QtCore.QSize(size, size))
# 设置样式
self.setFixedSize(size, size)
self.setToolTip(tooltip)
self.setStyleSheet("""
QPushButton {
border: none;
background: transparent;
}
QPushButton:hover {
background: rgba(255, 255, 255, 0.1);
}
QPushButton:pressed {
background: rgba(255, 255, 255, 0.2);
}
""")
class SearchLineEdit(QtWidgets.QLineEdit):
"""带搜索图标的输入框"""
def __init__(self, parent=None):
super(SearchLineEdit, self).__init__(parent)
self._setup_ui()
def _setup_ui(self):
# 设置样式
self.setPlaceholderText("搜索...")
# 添加搜索图标
search_action = QtWidgets.QAction(self)
search_action.setIcon(QtGui.QIcon(f"{config.ICONS_PATH}/search.png"))
self.addAction(search_action, QtWidgets.QLineEdit.LeadingPosition)
# 添加清除按钮
self.setClearButtonEnabled(True)
class CollapsibleWidget(QtWidgets.QWidget):
"""可折叠的控件组"""
def __init__(self, title="", parent=None):
super(CollapsibleWidget, self).__init__(parent)
self._setup_ui(title)
self._create_connections()
def _setup_ui(self, title):
# === Main Layout ===
self.main_layout = QtWidgets.QVBoxLayout(self)
self.main_layout.setContentsMargins(0, 0, 0, 0)
self.main_layout.setSpacing(0)
# === Header ===
self.header_widget = QtWidgets.QWidget()
header_layout = QtWidgets.QHBoxLayout(self.header_widget)
header_layout.setContentsMargins(5, 5, 5, 5)
# 展开/折叠按钮
self.toggle_btn = IconButton("arrow_down.png", size=16)
header_layout.addWidget(self.toggle_btn)
# 标题
title_label = QtWidgets.QLabel(title)
header_layout.addWidget(title_label)
header_layout.addStretch()
self.main_layout.addWidget(self.header_widget)
# === Content ===
self.content_widget = QtWidgets.QWidget()
self.content_layout = QtWidgets.QVBoxLayout(self.content_widget)
self.content_layout.setContentsMargins(20, 5, 5, 5)
self.main_layout.addWidget(self.content_widget)
def _create_connections(self):
self.toggle_btn.clicked.connect(self._toggle_content)
def _toggle_content(self):
"""切换内容显示/隐藏"""
if self.content_widget.isVisible():
self.content_widget.hide()
self.toggle_btn.setIcon(QtGui.QIcon(f"{config.ICONS_PATH}/arrow_right.png"))
else:
self.content_widget.show()
self.toggle_btn.setIcon(QtGui.QIcon(f"{config.ICONS_PATH}/arrow_down.png"))
def add_widget(self, widget):
"""添加控件到内容区域"""
self.content_layout.addWidget(widget)
class LabeledSpinBox(QtWidgets.QWidget):
"""带标签的数值输入框"""
valueChanged = QtCore.Signal(float) # 值改变信号
def __init__(self, label="", min_value=0.0, max_value=1.0, decimals=3, parent=None):
super(LabeledSpinBox, self).__init__(parent)
self._setup_ui(label, min_value, max_value, decimals)
self._create_connections()
def _setup_ui(self, label, min_value, max_value, decimals):
# === Layout ===
layout = QtWidgets.QHBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
# 标签
self.label = QtWidgets.QLabel(label)
layout.addWidget(self.label)
# 数值输入框
self.spin_box = QtWidgets.QDoubleSpinBox()
self.spin_box.setMinimum(min_value)
self.spin_box.setMaximum(max_value)
self.spin_box.setDecimals(decimals)
layout.addWidget(self.spin_box)
def _create_connections(self):
self.spin_box.valueChanged.connect(self.valueChanged.emit)
def value(self):
return self.spin_box.value()
def setValue(self, value):
self.spin_box.setValue(value)
class StatusWidget(QtWidgets.QWidget):
"""状态显示控件"""
def __init__(self, parent=None):
super(StatusWidget, self).__init__(parent)
self._setup_ui()
def _setup_ui(self):
# === Layout ===
layout = QtWidgets.QHBoxLayout(self)
layout.setContentsMargins(5, 2, 5, 2)
# 状态图标
self.icon_label = QtWidgets.QLabel()
self.icon_label.setFixedSize(16, 16)
layout.addWidget(self.icon_label)
# 状态文本
self.text_label = QtWidgets.QLabel()
layout.addWidget(self.text_label)
layout.addStretch()
def set_status(self, status, message):
"""设置状态
status: 'normal', 'warning', 'error'
"""
icon_map = {
'normal': 'status_normal.png',
'warning': 'status_warning.png',
'error': 'status_error.png'
}
if status in icon_map:
self.icon_label.setPixmap(
QtGui.QPixmap(f"{config.ICONS_PATH}/{icon_map[status]}")
)
self.text_label.setText(message)