This commit is contained in:
2025-05-07 01:31:21 +08:00
parent d27ef50341
commit 27240530b6
18 changed files with 2033 additions and 1160 deletions

View File

@@ -66,11 +66,21 @@ Make a Metahuman custom plugin for Maya, language: Python-based, Maya version: 2
### Important Variables ### Important Variables
- Config File: `config.py` - Config File: `config.py`
- Main File: `scripts\Main.py` - Main File: `scripts\Main.py`
- UI Modules: `scripts\ui\`
- Ot Module: `scripts\ui\Qt.py` - Ot Module: `scripts\ui\Qt.py`
- Style File: `scripts\ui\style.qss` - Style File: `scripts\ui\style.qss`
- Utilities Path: `scripts\utils\` - Utilities Path: `scripts\utils\`
- Reload Module `scripts\ReloadModules.py` - Reload Module `scripts\ReloadModules.py`
- UI Modules: `scripts\ui\`
- UI 通用功能模块: `scripts\ui\ui_utils.py`
- 几何体UI模块 `scripts\ui\geometry.py`
- 几何体功能模块: `scripts\utils\utils_geometry.py`
- 绑定UI模块 `scripts\ui\rigging.py`
- 绑定功能模块: `scripts\utils\utils_rigging.py`
- 行为面板UI模块 `scripts\ui\behavior.py`
- 行为面板功能模块: `scripts\utils\utils_behavior.py`
- 定义面板UI模块 `scripts\ui\definition.py`
- 定义面板功能模块: `scripts\utils\utils_definition.py`
### Doc reference Do not modify ### Doc reference Do not modify
- [DNA_Calibration在线文档](https://epicgames.github.io/MetaHuman-DNA-Calibration), use context7; - [DNA_Calibration在线文档](https://epicgames.github.io/MetaHuman-DNA-Calibration), use context7;
@@ -91,6 +101,43 @@ Make a Metahuman custom plugin for Maya, language: Python-based, Maya version: 2
- [Metahuman 蒙皮、骨骼、驱动](https://blog.csdn.net/qq_28976599/article/details/130849821), use context7; - [Metahuman 蒙皮、骨骼、驱动](https://blog.csdn.net/qq_28976599/article/details/130849821), use context7;
- [MetaHuman DNA Calibration Deep Dive](https://dev.epicgames.com/community/learning/tutorials/EoPj/metahuman-dna-calibration-deep-dive), use context7; - [MetaHuman DNA Calibration Deep Dive](https://dev.epicgames.com/community/learning/tutorials/EoPj/metahuman-dna-calibration-deep-dive), use context7;
### 1.1. SuperRigging功能
#### 1.1.1. Minimalist binding process
- One-button installation, one-button standardized model, one-button binding, one-button output FBX, use context7;
- https://pointart.oss-cn-hangzhou.aliyuncs.com/wp-content/uploads/2024/05/cut_1.mp4, use context7;
- Through our project experience and technical accumulation, we can shorten the technical process to the maximum, without complicated configuration process, and the technology is automatic and one-click processing, so that your energy can focus more on role modeling, expression and performance.
#### 1.1.2. Quickly adjust the expression
- The expression range is adjusted in batches, and the expression is quickly mirrored, use context7;
- https://pointart.oss-cn-hangzhou.aliyuncs.com/wp-content/uploads/2024/06/%E5%BF%AB%E9%80%9F%E8%B0%83%E6%95%B4.mp4, use context7;
- If the volume of some parts of your character (eyes/mouth/chin) is different from that of the standard Metahuman, you need to adjust the expression range of this area in batches to adapt to the correct expression, and you may need to adjust it one by one in the traditional process.
Now you can select these expressions in batches, and adjust the range with one button to quickly mirror the expressions, which will help you achieve the required facial expression quality faster and better.
#### 1.1.3. External expression import
- Support other expressions made by DCC to be imported into this role, use context7;
- https://pointart.oss-cn-hangzhou.aliyuncs.com/wp-content/uploads/2024/06/%E5%A4%96%E9%83%A8%E8%A1%A8%E6%83%85%E5%AF%BC%E5%85%A5.mp4, use context7;
- In the actual production process, expressions need to be carved with Zbrush or other software, or many people collaborate. We support exporting and carving some expressions in the form of model files, and then importing them into the role after carving is completed, which solves the problem of collaboration in the expression production process, and all this can be achieved without you studying the underlying expression architecture of Metahuman, just by exporting and importing with one key.
#### 1.1.4. Create a template preset
- One-click creation for the current role is a template preset, and similar roles can be quickly bound without repeated work! use context7;
- https://pointart.oss-cn-hangzhou.aliyuncs.com/wp-content/uploads/2024/06/%E5%88%9B%E5%BB%BA%E6%A8%A1%E6%9D%BF%E9%A2%84%E8%AE%BE.mp4, use context7;
- In the production process, we will encounter a batch of characters with similar faces, but if each one needs to be rebound and repaired, the workload of repetition is huge, so we support converting the current character into a template with one click, and similar characters can be directly and perfectly bound with one click.
Can also be used for your binding file needs to fine-tune the face, change the body posture and other needs, first create the role as a template, and then re-bind, saving work.
#### 1.1.5. Automatic role correction
- Automatic correction of joint axis-&Pose of role, use context7;
- https://pointart.oss-cn-hangzhou.aliyuncs.com/wp-content/uploads/2024/06/%E8%87%AA%E5%8A%A8%E6%A0%A1%E6%AD%A3%E8%A7%92%E8%89%B2.mp4, use context7;
- The role pose has different effects on the action capture and body modification of Metahuman. According to our experience, the role needs to fully conform to the pose of Metahuman, which can achieve higher data quality of dynamic capture and the correctness of finger axis.
We support one-click to correct the pose of the character to the correct pose, and set the axial direction of the joint correctly.
#### 1.1.6. Facial controller
- Face and panel controllers can be seamlessly switched, use context7;
- https://pointart.oss-cn-hangzhou.aliyuncs.com/wp-content/uploads/2024/06/%E9%9D%A2%E9%83%A8%E6%8E%A7%E5%88%B6%E5%99%A8.mp4, use context7;
- Aiming at the problem that Metahuman's face control panel is not intuitive, we support the generation of a brand-new face controller, which can directly adjust the expression and muscles of the character on the face. If you contact Metahuman controller soon, it will greatly reduce your learning cost.
#### 1.1.7. Different topological binding
- Arbitrary model, without changing topology and UV, one-click binding, use context7;
- https://pointart.oss-cn-hangzhou.aliyuncs.com/wp-content/uploads/2024/06/%E4%BB%BB%E6%84%8F%E6%8B%93%E6%89%91%E4%B8%80%E9%94%AE%E7%BB%91%E5%AE%9A.mp4, use context7;
- In the asset production process of most companies, the model is a set of fixed topological structure. If you want to bind it into Metahuman, the traditional process needs to be wrapped, and changing the topological structure also needs to change UV and Wrap. This link is easy to cause asset migration problems (model deformation, inaccurate topology, unable to Wrap the mapping format, UV layout does not meet asset characteristics and many other problems).
#### 1.1.8. Adapting UE specification
- The animation assets of UE, Animator and hair & clothing can be used seamlessly, use context7;
- https://pointart.oss-cn-hangzhou.aliyuncs.com/wp-content/uploads/2024/06/%E5%AE%8C%E7%BE%8E%E9%80%82%E9%85%8DUE%E8%A7%84%E8%8C%83.mp4, use context7;
- Using the official code as the underlying framework, it is perfectly compatible with UE, and can seamlessly use all kinds of assets created by UE for Metahuman, so that your role can perform easily or perfectly.
### Code reference (do not modify) ### Code reference (do not modify)
- `Reference\DNA_Calibration` - `Reference\DNA_Calibration`
- `Reference\MSLiveLink` - `Reference\MSLiveLink`

View File

@@ -477,6 +477,9 @@ endif
def _get_reload_modules_command(self): def _get_reload_modules_command(self):
"""Get the command string for the reload modules button""" """Get the command string for the reload modules button"""
return f""" return f"""
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys import sys
import os import os
import importlib import importlib
@@ -524,6 +527,9 @@ except Exception as error:
def _get_shelf_button_command(self): def _get_shelf_button_command(self):
"""Get the command string for the shelf button""" """Get the command string for the shelf button"""
return f""" return f"""
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys import sys
import os import os
import importlib import importlib
@@ -595,6 +601,9 @@ except Exception as e:
def _get_reload_modules_command(self): def _get_reload_modules_command(self):
"""Get the command string for the reload modules button""" """Get the command string for the reload modules button"""
return f""" return f"""
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys import sys
import os import os
import importlib import importlib

98
Plan.md
View File

@@ -15,33 +15,63 @@
- `scripts/ui/`所有UI模块含主界面、子工具面板 - `scripts/ui/`所有UI模块含主界面、子工具面板
- `scripts/ui/Qt.py`Qt封装 - `scripts/ui/Qt.py`Qt封装
- `scripts/ui/style.qss`(统一样式) - `scripts/ui/style.qss`(统一样式)
- `scripts/ui/ui_utils.py`UI通用功能模块
- `scripts/ui/geometry.py`几何体UI模块
- `scripts/ui/rigging.py`绑定UI模块
- `scripts/ui/behaviour.py`行为面板UI模块
- `scripts/ui/definition.py`定义面板UI模块
3. **功能实现** 3. **功能实现**
- `scripts/utils/utils_geometry.py`(几何体功能模块)
- `scripts/utils/utils_rigging.py`(绑定功能模块)
- `scripts/utils/utils_behaviour.py`(行为面板功能模块)
- `scripts/utils/utils_definition.py`(定义面板功能模块)
- DNA文件处理加载/保存/编辑) - DNA文件处理加载/保存/编辑)
- 绑定与骨骼校准 - 绑定与骨骼校准
- BlendShape与控制器映射 - BlendShape与控制器映射
- FBX导出 - FBX导出
- 兼容MetaHuman DNA标准 - 兼容MetaHuman DNA标准
4. **工具与通用模块** 4. **DNA处理核心**
- `scripts/utils/`(通用工具函数 - `scripts/dnalib/`DNA文件处理核心库
- `scripts/builder/`构建系统负责从DNA文件创建Maya场景
## 3. 阶段性开发计划 ## 3. 阶段性开发计划
### 第一阶段基础框架与UI ### 第一阶段基础框架与UI
- [x] 搭建插件基础结构,主入口、配置加载 - [x] 搭建插件基础结构,主入口、配置加载
- [x] 主界面UI搭建参考UI图与.qss风格 - [x] 主界面UI搭建参考UI图与.qss风格
- [x] 完善主界面各功能区分栏(模型、绑定、调整、定义) - [x] 完善主界面各功能区分栏(模型、绑定、调整、定义)
- [x] 优化 `main_window.py`确保Tab加载各Panel统一风格和注释 - [x] 优化 `Main.py`确保Tab加载各Panel统一风格和注释
- [x] 完善 `model_editor_panel.py`(模型编辑面板 - [x] 完善 `geometry.py`几何体UI模块
- [x] 完善 `dna_browser_panel.py`DNA浏览面板 - [x] 完善 `rigging.py`绑定UI模块
- [x] 创建 `joint_calibration_panel.py`(骨骼校准面板 - [x] 完善 `behaviour.py`行为面板UI模块
- [x] 创建 `blendshape_edit_panel.py`(混合变形编辑面板 - [x] 完善 `definition.py`定义面板UI模块
- [x] 完善 `animation_system_panel.py`(动画系统面板) - [ ] UI微调与优化
- [ ] 统一按钮样式与图标
- [ ] 优化面板布局与响应式设计
- [ ] 完善多语言支持
- [ ] 增强UI与Maya交互的稳定性
- [ ] 优化UI性能减少重绘和刷新操作
### 第二阶段:核心功能开发 ### 第二阶段:核心功能开发
- [ ] DNA文件读写与MetaHuman兼容 - [ ] DNA文件读写与MetaHuman兼容
- [ ] 完善dnalib模块实现DNA文件的读取与解析
- [ ] 实现DNA文件的保存与导出
- [ ] 确保与MetaHuman DNA标准兼容
- [ ] 绑定流程实现支持MetaHuman与自定义模型 - [ ] 绑定流程实现支持MetaHuman与自定义模型
- [ ] 实现MetaHuman同拓扑模型的自动绑定
- [ ] 支持自定义模型的绑定流程
- [ ] 实现骨骼映射与权重传递
- [ ] 骨骼校准与自动分组 - [ ] 骨骼校准与自动分组
- [ ] 实现骨骼自动分组算法
- [ ] 支持骨骼位置、旋转的微调与校准
- [ ] 实现关节轴向自动校正
- [ ] BlendShape与控制器映射 - [ ] BlendShape与控制器映射
- [ ] 实现BlendShape的创建、编辑与管理
- [ ] 支持控制器与BlendShape的映射关系设置
- [ ] 实现表情范围调整与镜像功能
- [ ] FBX导出与DNA文件导出 - [ ] FBX导出与DNA文件导出
- [ ] 实现模型与骨骼的FBX导出
- [ ] 支持DNA文件的导出与版本控制
- [ ] 确保导出文件与MetaHuman兼容
### 第三阶段:高级功能与优化 ### 第三阶段:高级功能与优化
- [ ] 批量处理与自动化工具 - [ ] 批量处理与自动化工具
@@ -58,41 +88,33 @@
## 进度记录与后续安排 ## 进度记录与后续安排
### 2025-05-06 更新
- 基础框架与UI阶段第一阶段已完成95%
- 完成了主要UI模块的创建和基本功能实现
- 优化了项目结构,明确了各模块的职责和接口
- 需要进行UI微调与优化包括统一样式、完善交互等
- DNA处理核心模块已初步实现需要进一步完善与测试
### 2025-04-30 更新 ### 2025-04-30 更新
- 完成了基础框架与UI阶段的全部任务(第一阶段 100% - 完成了基础框架与UI阶段的主要任务(第一阶段 90%
- 优化了主窗口与各功能面板的UI结构确保风格统一 - 优化了主窗口与各功能面板的UI结构确保风格统一
- 创建了所有必要的面板类,包括: - 创建了所有必要的面板类,包括:
- ModelEditorPanel模型编辑面板 - 几何体UI模块geometry.py
- DNABrowserPanelDNA浏览面板 - 绑定UI模块rigging.py
- JointCalibrationPanel骨骼校准面板 - 行为面板UI模块behaviour.py
- BlendshapeEditPanel混合变形编辑面板 - 定义面板UI模块definition.py
- AnimationSystemPanel动画系统面板
### 下一步开发计划(第二阶段) ### 下一步开发计划
1. **DNA文件读写与MetaHuman兼容** 1. **完成UI微调与优化**
- 实现DNA文件的读取、解析与保存功能 - 统一按钮样式与图标
- 优化面板布局与响应式设计
- 完善多语言支持
- 增强UI与Maya交互的稳定性
2. **开始DNA文件读写与MetaHuman兼容功能开发**
- 完善dnalib模块实现DNA文件的读取与解析
- 实现DNA文件的保存与导出
- 确保与MetaHuman DNA标准兼容 - 确保与MetaHuman DNA标准兼容
- 完善DNAManager类提供DNA文件操作API
2. **绑定流程实现**
- 支持MetaHuman同拓扑模型的自动绑定
- 支持自定义模型的绑定流程
- 实现骨骼映射与权重传递
3. **骨骼校准与自动分组**
- 完善JointCalibrationPanel的功能实现
- 实现骨骼自动分组算法
- 支持骨骼位置、旋转的微调与校准
4. **BlendShape与控制器映射**
- 完善BlendshapeEditPanel的功能实现
- 实现BlendShape的创建、编辑与管理
- 支持控制器与BlendShape的映射关系设置
5. **FBX导出与DNA文件导出**
- 实现模型与骨骼的FBX导出
- 支持DNA文件的导出与版本控制
- 确保导出文件与MetaHuman兼容
- 每完成一个功能模块,及时更新开发计划文档与进度百分比。 - 每完成一个功能模块,及时更新开发计划文档与进度百分比。
- 代码变更后,自动检查代码规范、插件完整性,并给出下一步开发建议。 - 代码变更后,自动检查代码规范、插件完整性,并给出下一步开发建议。

View File

@@ -29,6 +29,10 @@ DNA_FILE_PATH = os.path.join(TOOL_PATH, "assets", "dna").replace("\\", "/")
DNA_IMG_PATH = os.path.join(TOOL_PATH, "assets", "img").replace("\\", "/") DNA_IMG_PATH = os.path.join(TOOL_PATH, "assets", "img").replace("\\", "/")
TOOL_COMMAND_ICON = TOOL_ICON TOOL_COMMAND_ICON = TOOL_ICON
# UI 默认尺寸
TOOL_HEIGHT = 800
TOOL_WIDTH = 600
# DNA Config # DNA Config
# GUI相关常量 # GUI相关常量
GUI_HOLDER = "GUI_Holder" GUI_HOLDER = "GUI_Holder"

View File

@@ -53,13 +53,15 @@ ASSETS_PATH = config.ASSETS_PATH
DNA_FILE_PATH = config.DNA_FILE_PATH DNA_FILE_PATH = config.DNA_FILE_PATH
DNA_IMG_PATH = config.DNA_IMG_PATH DNA_IMG_PATH = config.DNA_IMG_PATH
TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON
TOOL_WIDTH = config.TOOL_WIDTH
TOOL_HEIGHT = config.TOOL_HEIGHT
#======================================= MAIN FUNCTION ===================================== #======================================= MAIN FUNCTION =====================================
class MainWindow(QtWidgets.QWidget): class MainWindow(QtWidgets.QWidget):
def __init__(self, parent=ui_utils.get_maya_main_window()): def __init__(self, parent=ui_utils.get_maya_main_window()):
super(MainWindow, self).__init__(parent) super(MainWindow, self).__init__(parent)
self.setWindowTitle(TOOL_NAME) self.setWindowTitle(TOOL_NAME)
self.setObjectName(f"{TOOL_NAME}MainWindow") self.setObjectName(f"{TOOL_NAME}MainWindow")
self.setMinimumSize(1200, 800) self.setMinimumSize(TOOL_WIDTH, TOOL_HEIGHT)
self.setStyleSheet("") self.setStyleSheet("")
self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
@@ -249,9 +251,25 @@ class MainWindow(QtWidgets.QWidget):
def center_window(self): def center_window(self):
"""将窗口居中显示在屏幕上""" """将窗口居中显示在屏幕上"""
# 获取屏幕几何信息 # 获取屏幕几何信息 - 兼容PySide2和PySide6
try:
# 使用Qt.py兼容层方式获取屏幕信息
app = QtWidgets.QApplication.instance()
screen = app.primaryScreen() if hasattr(app, 'primaryScreen') else None
if screen:
screen_rect = screen.availableGeometry()
else:
# 尝试使用desktop方法PySide2兼容
if hasattr(QtWidgets.QApplication, 'desktop'):
desktop = QtWidgets.QApplication.desktop() desktop = QtWidgets.QApplication.desktop()
screen_rect = desktop.availableGeometry(desktop.primaryScreen()) screen_rect = desktop.availableGeometry(desktop.primaryScreen())
else:
# 如果都失败,使用默认值
screen_rect = QtCore.QRect(0, 0, 1920, 1080)
except Exception as e:
print(f"获取屏幕信息时出错: {str(e)}")
# 使用默认值
screen_rect = QtCore.QRect(0, 0, 1920, 1080)
# 计算窗口居中位置 # 计算窗口居中位置
window_rect = self.frameGeometry() window_rect = self.frameGeometry()

File diff suppressed because it is too large Load Diff

View File

@@ -53,6 +53,8 @@ ASSETS_PATH = config.ASSETS_PATH
DNA_FILE_PATH = config.DNA_FILE_PATH DNA_FILE_PATH = config.DNA_FILE_PATH
DNA_IMG_PATH = config.DNA_IMG_PATH DNA_IMG_PATH = config.DNA_IMG_PATH
TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON
TOOL_WIDTH = config.TOOL_WIDTH
TOOL_HEIGHT = config.TOOL_HEIGHT
#========================================= LOCATION ======================================= #========================================= LOCATION =======================================
from scripts.ui import localization from scripts.ui import localization
LANG = localization.LANG LANG = localization.LANG
@@ -62,47 +64,83 @@ class BehaviourUI(ui_utils.BaseUI):
行为系统UI类 - 负责显示角色行为编辑界面和基础操作 行为系统UI类 - 负责显示角色行为编辑界面和基础操作
继承自BaseUI类实现行为系统相关的UI功能 继承自BaseUI类实现行为系统相关的UI功能
""" """
#========================================== INIT ======================================== # 类变量,存储单例实例
def __init__(self): _instance = None
@classmethod
def get_instance(cls):
"""
获取BehaviourUI的单例实例
Returns:
BehaviourUI: 单例实例如果不存在则返回None
"""
return cls._instance
def __init__(self, parent=None):
""" """
初始化行为系统UI 初始化行为系统UI
创建主控件和布局,并连接信号和槽
""" """
super(BehaviourUI, self).__init__() super(BehaviourUI, self).__init__() # 不传递parent参数给BaseUI
self.main_widget = QtWidgets.QWidget(parent) # 在创建main_widget时传递parent
# 创建主控件
self.main_widget = QtWidgets.QWidget()
self.main_widget.setObjectName("behaviourMainWidget") self.main_widget.setObjectName("behaviourMainWidget")
# 初始化UI # 设置单例实例
BehaviourUI._instance = self
# 初始化控件、布局和按钮字典
self.controls = {}
self.layouts = {}
self.buttons = {}
self.splitters = {}
# 创建UI组件
self.create_widgets() self.create_widgets()
self.create_layouts() self.create_layouts()
self.create_connections() self.create_connections()
#========================================= WIDGET ======================================= # 在创建完所有布局后,设置分割器的初始大小和属性
# 使用ui_utils中的函数来设置分割器大小
ui_utils.setup_splitter(self, "main_splitter", [1, 1])
# 设置分割器所有子元素的最小尺寸为0确保可以自由调整
ui_utils.set_splitter_children_minimum_size(self, recursive=True)
# 设置所有控件的最小尺寸为0确保分割器可以自由移动
ui_utils.set_all_controls_minimum_size(self)
# 使用ui_utils中的函数强制设置均等大小
ui_utils.force_equal_splitter_sizes(self)
def create_widgets(self): def create_widgets(self):
""" """
创建行为系统UI控件 创建行为系统UI控件
包括按钮、标签、列表 包括标题标签、搜索框、控制列表、滑块
""" """
# 标题标签 - 使用HTML格式化标题 # 标题标签
title_text = f"<h4 style='margin:0;padding:5px;'>{LANG.get('behaviour_title', '行为')}</h4>" self.controls["title_label"] = QtWidgets.QLabel(LANG.get("behaviour_title", "行为系统"))
self.controls["title_label"] = QtWidgets.QLabel(title_text)
self.controls["title_label"].setObjectName("behaviourTitleLabel") self.controls["title_label"].setObjectName("behaviourTitleLabel")
self.controls["title_label"].setAlignment(QtCore.Qt.AlignCenter) self.controls["title_label"].setAlignment(QtCore.Qt.AlignCenter)
self.controls["title_label"].setMaximumHeight(30) # 限制标题高度 self.controls["title_label"].setStyleSheet("font-size: 14px; font-weight: bold; padding: 5px;")
# 左侧面板 # 主分割器
self.splitters["main_splitter"] = QtWidgets.QSplitter(QtCore.Qt.Horizontal)
self.splitters["main_splitter"].setObjectName("behaviourMainSplitter")
self.splitters["main_splitter"].setHandleWidth(6) # 设置分割器手柄宽度
self.splitters["main_splitter"].setChildrenCollapsible(False) # 禁止子部件折叠
self.splitters["main_splitter"].setOpaqueResize(True) # 实时显示调整效果,更丝滑
# 左侧面板 - Raw Control
self.controls["left_panel"] = QtWidgets.QWidget() self.controls["left_panel"] = QtWidgets.QWidget()
self.controls["left_panel"].setObjectName("behaviourLeftPanel") self.controls["left_panel"].setObjectName("behaviourLeftPanel")
self.controls["left_panel"].setMinimumWidth(0)
self.controls["left_panel"].setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
# 右侧面板 # 右侧面板 - BlendShapes
self.controls["right_panel"] = QtWidgets.QWidget() self.controls["right_panel"] = QtWidgets.QWidget()
self.controls["right_panel"].setObjectName("behaviourRightPanel") self.controls["right_panel"].setObjectName("behaviourRightPanel")
self.controls["right_panel"].setMinimumWidth(0)
# 左侧面板控件 - Raw Control self.controls["right_panel"].setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
self.controls["raw_control_group"] = QtWidgets.QGroupBox("Raw Control [000]")
self.controls["raw_control_group"].setObjectName("rawControlGroup")
# 搜索框 # 搜索框
self.controls["search_input"] = QtWidgets.QLineEdit() self.controls["search_input"] = QtWidgets.QLineEdit()
@@ -112,15 +150,45 @@ class BehaviourUI(ui_utils.BaseUI):
# 控制列表 # 控制列表
self.controls["control_list"] = QtWidgets.QListWidget() self.controls["control_list"] = QtWidgets.QListWidget()
self.controls["control_list"].setObjectName("controlList") self.controls["control_list"].setObjectName("controlList")
self.controls["control_list"].setMinimumWidth(0) # 设置最小宽度为零
# 底部滑块和按钮 # 底部滑块
self.controls["bottom_slider"] = QtWidgets.QSlider(QtCore.Qt.Horizontal) self.controls["raw_slider"] = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.controls["bottom_slider"].setObjectName("bottomSlider") self.controls["raw_slider"].setObjectName("rawSlider")
self.controls["bottom_slider"].setMinimum(0) self.controls["raw_slider"].setMinimum(0)
self.controls["bottom_slider"].setMaximum(100) self.controls["raw_slider"].setMaximum(100)
self.controls["bottom_slider"].setValue(10) self.controls["raw_slider"].setValue(0)
self.controls["raw_slider"].setFixedHeight(20)
# 创建滑块布局,包含数值显示和滑块
self.layouts["raw_slider_layout"] = QtWidgets.QHBoxLayout()
self.layouts["raw_slider_layout"].setContentsMargins(0, 0, 0, 0)
self.layouts["raw_slider_layout"].setSpacing(2)
# 添加数值显示标签
self.controls["raw_slider_value"] = QtWidgets.QLabel("0.000")
self.controls["raw_slider_value"].setObjectName("rawSliderValue")
self.controls["raw_slider_value"].setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.controls["raw_slider_value"].setFixedWidth(40)
self.layouts["raw_slider_layout"].addWidget(self.controls["raw_slider_value"])
# 添加滑块
self.layouts["raw_slider_layout"].addWidget(self.controls["raw_slider"])
# 添加"All"勾选框和标签
self.controls["raw_slider_all_check"] = QtWidgets.QCheckBox("All")
self.controls["raw_slider_all_check"].setObjectName("rawSliderAllCheck")
self.layouts["raw_slider_layout"].addWidget(self.controls["raw_slider_all_check"])
# 页码按钮 # 页码按钮
self.buttons["prev_page"] = QtWidgets.QPushButton("上一页")
self.buttons["prev_page"].setObjectName("prevPageButton")
self.buttons["prev_page"].setIcon(ui_utils.get_icon("arrowLeft.png"))
self.buttons["next_page"] = QtWidgets.QPushButton("下一页")
self.buttons["next_page"].setObjectName("nextPageButton")
self.buttons["next_page"].setIcon(ui_utils.get_icon("arrowRight.png"))
self.buttons["page_all"] = QtWidgets.QPushButton("All") self.buttons["page_all"] = QtWidgets.QPushButton("All")
self.buttons["page_all"].setObjectName("pageAllButton") self.buttons["page_all"].setObjectName("pageAllButton")
self.buttons["page_all"].setCheckable(True) self.buttons["page_all"].setCheckable(True)
@@ -146,119 +214,204 @@ class BehaviourUI(ui_utils.BaseUI):
self.buttons["page_6"].setObjectName("page6Button") self.buttons["page_6"].setObjectName("page6Button")
self.buttons["page_6"].setCheckable(True) self.buttons["page_6"].setCheckable(True)
# 左下角按钮 # 左下角Range按钮
self.buttons["range_minus"] = QtWidgets.QPushButton("-") self.buttons["range_plus"] = QtWidgets.QPushButton("Range +")
self.buttons["range_minus"].setObjectName("rangeMinusButton")
self.buttons["range_plus"] = QtWidgets.QPushButton("+")
self.buttons["range_plus"].setObjectName("rangePlusButton") self.buttons["range_plus"].setObjectName("rangePlusButton")
self.buttons["range_plus"].setIcon(ui_utils.get_icon("behaviour.png"))
self.buttons["range_minus"] = QtWidgets.QPushButton("Range -")
self.buttons["range_minus"].setObjectName("rangeMinusButton")
self.buttons["range_minus"].setIcon(ui_utils.get_icon("behaviour.png"))
# 右侧面板控件 - Related BlendShapes # 右侧面板控件 - Related BlendShapes
self.controls["blendshapes_group"] = QtWidgets.QGroupBox("Related BlendShapes [000]") self.controls["blendshapes_group"] = QtWidgets.QGroupBox("Related BlendShapes [000]")
self.controls["blendshapes_group"].setObjectName("blendshapesGroup") self.controls["blendshapes_group"].setObjectName("blendshapesGroup")
self.controls["blendshapes_group"].setMinimumWidth(0) # 设置最小宽度为零
self.controls["blendshapes_group"].setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
# BlendShapes列表 # BlendShapes列表
self.controls["blendshapes_list"] = QtWidgets.QListWidget() self.controls["blendshapes_list"] = QtWidgets.QListWidget()
self.controls["blendshapes_list"].setObjectName("blendshapesList") self.controls["blendshapes_list"].setObjectName("blendshapesList")
self.controls["blendshapes_list"].setMinimumWidth(0) # 设置最小宽度为零
self.controls["blendshapes_list"].setMinimumHeight(100) # 设置最小高度,确保列表可见
self.controls["blendshapes_list"].setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
# 底部滑块和按钮 # 底部滑块
self.controls["bs_slider"] = QtWidgets.QSlider(QtCore.Qt.Horizontal) self.controls["bs_slider"] = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.controls["bs_slider"].setObjectName("bsSlider") self.controls["bs_slider"].setObjectName("bsSlider")
self.controls["bs_slider"].setMinimum(0) self.controls["bs_slider"].setMinimum(0)
self.controls["bs_slider"].setMaximum(100) self.controls["bs_slider"].setMaximum(100)
self.controls["bs_slider"].setValue(0) self.controls["bs_slider"].setValue(0)
# 创建BS滑块布局包含数值显示和滑块
self.layouts["bs_slider_layout"] = QtWidgets.QHBoxLayout()
self.layouts["bs_slider_layout"].setContentsMargins(0, 0, 0, 0)
self.layouts["bs_slider_layout"].setSpacing(2)
# 添加数值显示标签
self.controls["bs_slider_value"] = QtWidgets.QLabel("0.000")
self.controls["bs_slider_value"].setObjectName("bsSliderValue")
self.controls["bs_slider_value"].setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.controls["bs_slider_value"].setFixedWidth(40)
self.layouts["bs_slider_layout"].addWidget(self.controls["bs_slider_value"])
# 添加滑块
self.layouts["bs_slider_layout"].addWidget(self.controls["bs_slider"])
# 添加"All"勾选框和标签
self.controls["bs_slider_all_check"] = QtWidgets.QCheckBox("All")
self.controls["bs_slider_all_check"].setObjectName("bsSliderAllCheck")
self.layouts["bs_slider_layout"].addWidget(self.controls["bs_slider_all_check"])
# BlendShape操作按钮 # BlendShape操作按钮
self.buttons["reset_blendshape"] = QtWidgets.QPushButton(LANG.get("reset_blendshape", "重置混合形状")) self.buttons["flip_target"] = QtWidgets.QPushButton("Flip Target")
self.buttons["reset_blendshape"].setObjectName("resetBlendshapeButton") self.buttons["flip_target"].setObjectName("flipTargetButton")
self.buttons["flip_target"].setIcon(ui_utils.get_icon("mirrorL.png"))
self.buttons["flip_target"].setMinimumHeight(25) # 设置最小高度,确保按钮可见
self.buttons["mirror_blendshape"] = QtWidgets.QPushButton(LANG.get("mirror_blendshape", "镜像混合形状")) self.buttons["mirror_target"] = QtWidgets.QPushButton("Mirror Target")
self.buttons["mirror_blendshape"].setObjectName("mirrorBlendshapeButton") self.buttons["mirror_target"].setObjectName("mirrorTargetButton")
self.buttons["mirror_target"].setIcon(ui_utils.get_icon("mirror.png"))
self.buttons["mirror_target"].setMinimumHeight(25) # 设置最小高度,确保按钮可见
self.buttons["add_blendshape"] = QtWidgets.QPushButton(LANG.get("add_blendshape", "添加混合形状")) self.buttons["find_flip_target"] = QtWidgets.QPushButton("Find Flip Target")
self.buttons["find_flip_target"].setObjectName("findFlipTargetButton")
self.buttons["find_flip_target"].setIcon(ui_utils.get_icon("mirrorR.png"))
self.buttons["find_flip_target"].setMinimumHeight(25) # 设置最小高度,确保按钮可见
self.buttons["add_blendshape"] = QtWidgets.QPushButton("Add BlendShape")
self.buttons["add_blendshape"].setObjectName("addBlendshapeButton") self.buttons["add_blendshape"].setObjectName("addBlendshapeButton")
self.buttons["add_blendshape"].setIcon(ui_utils.get_icon("blendShape.png"))
self.buttons["add_blendshape"].setMinimumHeight(25) # 设置最小高度,确保按钮可见
self.buttons["remove_blendshape"] = QtWidgets.QPushButton(LANG.get("remove_blendshape", "移除混合形状")) self.buttons["delete_blendshape"] = QtWidgets.QPushButton("Delete BlendShape")
self.buttons["remove_blendshape"].setObjectName("removeBlendshapeButton") self.buttons["delete_blendshape"].setObjectName("deleteBlendshapeButton")
self.buttons["delete_blendshape"].setIcon(ui_utils.get_icon("blendShape.png"))
self.buttons["delete_blendshape"].setMinimumHeight(25) # 设置最小高度,确保按钮可见
self.buttons["update_blendshape"] = QtWidgets.QPushButton(LANG.get("update_blendshape", "更新混合形状")) self.buttons["batch_blendshape"] = QtWidgets.QPushButton("Batch BlendShape")
self.buttons["update_blendshape"].setObjectName("updateBlendshapeButton") self.buttons["batch_blendshape"].setObjectName("batchBlendshapeButton")
self.buttons["batch_blendshape"].setIcon(ui_utils.get_icon("blendShape.png"))
self.buttons["batch_blendshape"].setMinimumHeight(25) # 设置最小高度,确保按钮可见
self.buttons["isolate_blendshape"] = QtWidgets.QPushButton(LANG.get("isolate_blendshape", "隔离混合形状")) self.buttons["rebuild_select"] = QtWidgets.QPushButton("Rebuild Select")
self.buttons["isolate_blendshape"].setObjectName("isolateBlendshapeButton") self.buttons["rebuild_select"].setObjectName("rebuildSelectButton")
self.buttons["rebuild_select"].setIcon(ui_utils.get_icon("loading.png"))
self.buttons["rebuild_select"].setMinimumHeight(25) # 设置最小高度,确保按钮可见
self.buttons["new_blendshape"] = QtWidgets.QPushButton(LANG.get("new_blendshape", "新建混合形状")) self.buttons["reposition_joints"] = QtWidgets.QPushButton("Reposition Joints")
self.buttons["new_blendshape"].setObjectName("newBlendshapeButton") self.buttons["reposition_joints"].setObjectName("repositionJointsButton")
self.buttons["reposition_joints"].setIcon(ui_utils.get_icon("loading.png"))
self.buttons["reposition_joints"].setMinimumHeight(25) # 设置最小高度,确保按钮可见
self.buttons["combine_blendshape"] = QtWidgets.QPushButton(LANG.get("combine_blendshape", "组合混合形状")) self.buttons["blend_select"] = QtWidgets.QPushButton("Blend Select")
self.buttons["combine_blendshape"].setObjectName("combineBlendshapeButton") self.buttons["blend_select"].setObjectName("blendSelectButton")
self.buttons["blend_select"].setIcon(ui_utils.get_icon("loading.png"))
self.buttons["blend_select"].setMinimumHeight(25) # 设置最小高度,确保按钮可见
# 右侧Range按钮
self.buttons["bs_range_plus"] = QtWidgets.QPushButton("Range +")
self.buttons["bs_range_plus"].setObjectName("bsRangePlusButton")
self.buttons["bs_range_plus"].setIcon(ui_utils.get_icon("behaviour.png"))
self.buttons["bs_range_minus"] = QtWidgets.QPushButton("Range -")
self.buttons["bs_range_minus"].setObjectName("bsRangeMinusButton")
self.buttons["bs_range_minus"].setIcon(ui_utils.get_icon("behaviour.png"))
# 底部标签页按钮 # 底部标签页按钮
self.buttons["tab_bs"] = QtWidgets.QPushButton("BS")
self.buttons["tab_bs"].setObjectName("tabBsButton")
self.buttons["tab_bs"].setCheckable(True)
self.buttons["tab_bs"].setChecked(True)
self.buttons["tab_psd"] = QtWidgets.QPushButton("PSD") self.buttons["tab_psd"] = QtWidgets.QPushButton("PSD")
self.buttons["tab_psd"].setIcon(ui_utils.get_icon("psd.png"))
self.buttons["tab_psd"].setObjectName("tabPsdButton") self.buttons["tab_psd"].setObjectName("tabPsdButton")
self.buttons["tab_psd"].setCheckable(True) self.buttons["tab_psd"].setCheckable(True)
self.buttons["tab_bse"] = QtWidgets.QPushButton("BSE")
self.buttons["tab_bse"].setIcon(ui_utils.get_icon("blendShape.png"))
self.buttons["tab_bse"].setObjectName("tabBseButton")
self.buttons["tab_bse"].setCheckable(True)
self.buttons["tab_key"] = QtWidgets.QPushButton("KEY") self.buttons["tab_key"] = QtWidgets.QPushButton("KEY")
self.buttons["tab_key"].setIcon(ui_utils.get_icon("setKeyOnAnim.png"))
self.buttons["tab_key"].setObjectName("tabKeyButton") self.buttons["tab_key"].setObjectName("tabKeyButton")
self.buttons["tab_key"].setCheckable(True) self.buttons["tab_key"].setCheckable(True)
self.buttons["tab_jnt"] = QtWidgets.QPushButton("JNT") self.buttons["tab_mir"] = QtWidgets.QPushButton("MIR")
self.buttons["tab_jnt"].setObjectName("tabJntButton") self.buttons["tab_mir"].setIcon(ui_utils.get_icon("mirrorR.png"))
self.buttons["tab_jnt"].setCheckable(True) self.buttons["tab_mir"].setObjectName("tabMirButton")
self.buttons["tab_mir"].setCheckable(True)
self.buttons["tab_ark"] = QtWidgets.QPushButton("ARK") self.buttons["tab_ark"] = QtWidgets.QPushButton("ARK")
self.buttons["tab_ark"].setIcon(ui_utils.get_icon("ARKit52.png"))
self.buttons["tab_ark"].setObjectName("tabArkButton") self.buttons["tab_ark"].setObjectName("tabArkButton")
self.buttons["tab_ark"].setCheckable(True) self.buttons["tab_ark"].setCheckable(True)
self.buttons["tab_ctr"] = QtWidgets.QPushButton("CTR") self.buttons["tab_ctr"] = QtWidgets.QPushButton("CTR")
self.buttons["tab_ctr"].setIcon(ui_utils.get_icon("ctrl_hide.png"))
self.buttons["tab_ctr"].setObjectName("tabCtrButton") self.buttons["tab_ctr"].setObjectName("tabCtrButton")
self.buttons["tab_ctr"].setCheckable(True) self.buttons["tab_ctr"].setCheckable(True)
# 底部滑块 # 底部滑块
self.controls["bottom_main_slider"] = QtWidgets.QSlider(QtCore.Qt.Horizontal) self.controls["bottom_main_slider"] = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.controls["bottom_main_slider"].setObjectName("bottomMainSlider") self.controls["bottom_main_slider"].setObjectName("bottomMainSlider")
self.controls["bottom_main_slider"].setMinimum(0) self.controls["bottom_main_slider"].setMinimum(0)
self.controls["bottom_main_slider"].setMaximum(100) self.controls["bottom_main_slider"].setMaximum(100)
self.controls["bottom_main_slider"].setValue(0) self.controls["bottom_main_slider"].setValue(0)
self.controls["bottom_main_slider"].setFixedHeight(20)
# 底部按钮 # 底部主滑块布局包含数值显示和All勾选框
self.buttons["exchange_from_expression"] = QtWidgets.QPushButton(LANG.get("exchange_from_expression", "交换从表情")) self.layouts["bottom_slider_layout"] = QtWidgets.QHBoxLayout()
self.buttons["exchange_from_expression"].setObjectName("exchangeFromExpressionButton") self.layouts["bottom_slider_layout"].setContentsMargins(0, 0, 0, 0)
self.layouts["bottom_slider_layout"].setSpacing(2)
self.buttons["reset_selected_expression"] = QtWidgets.QPushButton(LANG.get("reset_selected_expression", "重置选定表情")) # 添加数值显示标签
self.buttons["reset_selected_expression"].setObjectName("resetSelectedExpressionButton") self.controls["bottom_slider_value"] = QtWidgets.QLabel("0.000")
self.controls["bottom_slider_value"].setObjectName("bottomSliderValue")
self.controls["bottom_slider_value"].setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.controls["bottom_slider_value"].setFixedWidth(40)
self.layouts["bottom_slider_layout"].addWidget(self.controls["bottom_slider_value"])
self.buttons["import_expression"] = QtWidgets.QPushButton(LANG.get("import_expression", "导入表情")) # 添加滑块
self.buttons["import_expression"].setObjectName("importExpressionButton") self.layouts["bottom_slider_layout"].addWidget(self.controls["bottom_main_slider"])
self.buttons["control_panel_search"] = QtWidgets.QPushButton(LANG.get("control_panel_search", "控制面板搜索")) # 添加"All"勾选框
self.buttons["control_panel_search"].setObjectName("controlPanelSearchButton") self.controls["bottom_slider_all_check"] = QtWidgets.QCheckBox("All")
self.controls["bottom_slider_all_check"].setObjectName("bottomSliderAllCheck")
self.layouts["bottom_slider_layout"].addWidget(self.controls["bottom_slider_all_check"])
self.buttons["select_related_keys"] = QtWidgets.QPushButton(LANG.get("select_related_keys", "选择相关键值")) # 底部功能按钮
self.buttons["select_related_keys"].setObjectName("selectRelatedKeysButton") self.buttons["reset_default_expression"] = QtWidgets.QPushButton("Reset Default Expression")
self.buttons["reset_default_expression"].setIcon(ui_utils.get_icon("reset.png"))
self.buttons["reset_default_expression"].setObjectName("resetDefaultExpressionButton")
self.buttons["import_map_expression"] = QtWidgets.QPushButton(LANG.get("import_map_expression", "导入表情映射")) self.buttons["find_select_expression"] = QtWidgets.QPushButton("Find Select Expression")
self.buttons["import_map_expression"].setObjectName("importMapExpressionButton") self.buttons["find_select_expression"].setIcon(ui_utils.get_icon("expressions_current.png"))
# 表情控制按钮 self.buttons["find_select_expression"].setObjectName("findSelectExpressionButton")
self.buttons["reset_expression"] = QtWidgets.QPushButton(LANG.get("reset_expression", "还原默认表情"))
self.buttons["find_expression"] = QtWidgets.QPushButton(LANG.get("find_expression", "查找选择表情")) self.buttons["write_current_expressions"] = QtWidgets.QPushButton("Write Current Expressions")
self.buttons["find_control_panel"] = QtWidgets.QPushButton(LANG.get("find_control_panel", "控制面板查找")) self.buttons["write_current_expressions"].setIcon(ui_utils.get_icon("expression.png"))
self.buttons["write_current_expressions"].setObjectName("writeCurrentExpressionsButton")
self.buttons["controller_find"] = QtWidgets.QPushButton("Controller Find")
self.buttons["controller_find"].setIcon(ui_utils.get_icon("controller.png"))
self.buttons["controller_find"].setObjectName("controllerFindButton")
self.buttons["select_associated_joint"] = QtWidgets.QPushButton("Select Associated Joint")
self.buttons["select_associated_joint"].setIcon(ui_utils.get_icon("out_joint.png"))
self.buttons["select_associated_joint"].setObjectName("selectAssociatedJointButton")
self.buttons["write_find_mirror"] = QtWidgets.QPushButton("Write Find Mirror")
self.buttons["write_find_mirror"].setIcon(ui_utils.get_icon("mirror.png"))
self.buttons["write_find_mirror"].setObjectName("writeFindMirrorButton")
#========================================= LAYOUT ======================================= #========================================= LAYOUT =======================================
def create_layouts(self): def create_layouts(self):
""" """
创建行为系统UI布局 创建行为系统UI布局
安排控件的位置和尺寸 包括主布局、左右面板布局等
""" """
# 主布局 # 主布局
self.layouts["main_layout"] = QtWidgets.QVBoxLayout(self.main_widget) self.layouts["main_layout"] = QtWidgets.QVBoxLayout(self.main_widget)
self.layouts["main_layout"].setContentsMargins(2, 2, 2, 2) self.layouts["main_layout"].setContentsMargins(0, 0, 0, 0)
self.layouts["main_layout"].setSpacing(2) self.layouts["main_layout"].setSpacing(0)
# 添加标题标签 # 添加标题标签
self.layouts["main_layout"].addWidget(self.controls["title_label"]) self.layouts["main_layout"].addWidget(self.controls["title_label"])
@@ -266,70 +419,136 @@ class BehaviourUI(ui_utils.BaseUI):
# 创建主分割器 # 创建主分割器
self.splitters["main_splitter"] = QtWidgets.QSplitter(QtCore.Qt.Horizontal) self.splitters["main_splitter"] = QtWidgets.QSplitter(QtCore.Qt.Horizontal)
self.splitters["main_splitter"].setObjectName("behaviourMainSplitter") self.splitters["main_splitter"].setObjectName("behaviourMainSplitter")
self.layouts["main_layout"].addWidget(self.splitters["main_splitter"]) self.splitters["main_splitter"].setHandleWidth(6) # 设置分割器手柄宽度
self.splitters["main_splitter"].setChildrenCollapsible(False) # 禁止子部件折叠
self.splitters["main_splitter"].setOpaqueResize(True) # 实时显示调整效果,更丝滑
# 添加主分割器
self.layouts["main_layout"].addWidget(self.splitters["main_splitter"], 1) # 设置伸缩因子为1使其占据剩余空间
# 左侧面板
self.controls["left_panel"] = QtWidgets.QWidget()
self.controls["left_panel"].setObjectName("behaviourLeftPanel")
self.controls["left_panel"].setMinimumWidth(0)
self.controls["left_panel"].setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
# 右侧面板
self.controls["right_panel"] = QtWidgets.QWidget()
self.controls["right_panel"].setObjectName("behaviourRightPanel")
self.controls["right_panel"].setMinimumWidth(0)
self.controls["right_panel"].setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
# 将左右面板添加到分割器
self.splitters["main_splitter"].addWidget(self.controls["left_panel"])
self.splitters["main_splitter"].addWidget(self.controls["right_panel"])
# 左侧面板布局 # 左侧面板布局
self.layouts["left_layout"] = QtWidgets.QVBoxLayout(self.controls["left_panel"]) self.layouts["left_layout"] = QtWidgets.QVBoxLayout(self.controls["left_panel"])
self.layouts["left_layout"].setContentsMargins(5, 5, 5, 5) self.layouts["left_layout"].setContentsMargins(5, 5, 5, 5)
self.layouts["left_layout"].setSpacing(5) self.layouts["left_layout"].setSpacing(5)
# Raw Control组布局
self.layouts["raw_control_layout"] = QtWidgets.QVBoxLayout(self.controls["raw_control_group"])
self.layouts["raw_control_layout"].setContentsMargins(5, 10, 5, 5)
self.layouts["raw_control_layout"].setSpacing(5)
# 搜索框
self.layouts["raw_control_layout"].addWidget(self.controls["search_input"])
# 控制列表
self.layouts["raw_control_layout"].addWidget(self.controls["control_list"])
# 底部滑块
self.layouts["raw_control_layout"].addWidget(self.controls["bottom_slider"])
# 页码按钮行
self.layouts["page_buttons_layout"] = QtWidgets.QHBoxLayout()
self.layouts["page_buttons_layout"].setContentsMargins(0, 0, 0, 0)
self.layouts["page_buttons_layout"].setSpacing(2)
self.layouts["page_buttons_layout"].addWidget(self.buttons["page_all"])
self.layouts["page_buttons_layout"].addWidget(self.buttons["page_2"])
self.layouts["page_buttons_layout"].addWidget(self.buttons["page_3"])
self.layouts["page_buttons_layout"].addWidget(self.buttons["page_4"])
self.layouts["page_buttons_layout"].addWidget(self.buttons["page_5"])
self.layouts["page_buttons_layout"].addWidget(self.buttons["page_6"])
self.layouts["raw_control_layout"].addLayout(self.layouts["page_buttons_layout"])
# 左下角按钮行
self.layouts["range_buttons_layout"] = QtWidgets.QHBoxLayout()
self.layouts["range_buttons_layout"].setContentsMargins(0, 0, 0, 0)
self.layouts["range_buttons_layout"].setSpacing(2)
self.layouts["range_buttons_layout"].addWidget(self.buttons["range_minus"])
self.layouts["range_buttons_layout"].addWidget(self.buttons["range_plus"])
self.layouts["range_buttons_layout"].addStretch()
self.layouts["raw_control_layout"].addLayout(self.layouts["range_buttons_layout"])
# 添加Raw Control组到左侧面板
self.layouts["left_layout"].addWidget(self.controls["raw_control_group"])
# 右侧面板布局 # 右侧面板布局
self.layouts["right_layout"] = QtWidgets.QVBoxLayout(self.controls["right_panel"]) self.layouts["right_layout"] = QtWidgets.QVBoxLayout(self.controls["right_panel"])
self.layouts["right_layout"].setContentsMargins(5, 5, 5, 5) self.layouts["right_layout"].setContentsMargins(5, 5, 5, 5)
self.layouts["right_layout"].setSpacing(5) self.layouts["right_layout"].setSpacing(5)
# BlendShapes组布局 # 左侧面板控件 - Raw Control
self.layouts["blendshapes_layout"] = QtWidgets.QVBoxLayout(self.controls["blendshapes_group"]) self.controls["raw_control_group"] = QtWidgets.QGroupBox("Raw Control")
self.layouts["blendshapes_layout"].setContentsMargins(5, 10, 5, 5) self.controls["raw_control_group"].setObjectName("rawControlGroup")
self.layouts["blendshapes_layout"].setSpacing(5) self.controls["raw_control_group"].setMinimumWidth(0) # 设置最小宽度为零
self.controls["raw_control_group"].setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
# BlendShapes列表 # 左侧面板控件布局
self.layouts["blendshapes_layout"].addWidget(self.controls["blendshapes_list"]) self.layouts["raw_control_layout"] = QtWidgets.QVBoxLayout()
self.layouts["raw_control_layout"].setContentsMargins(2, 2, 2, 2)
self.layouts["raw_control_layout"].setSpacing(2)
self.controls["raw_control_group"].setLayout(self.layouts["raw_control_layout"])
# 滑块 # 添加控制列表到Raw Control布局
self.layouts["blendshapes_layout"].addWidget(self.controls["bs_slider"]) self.layouts["raw_control_layout"].addWidget(self.controls["control_list"])
# 左侧滑块布局
self.layouts["raw_control_layout"].addLayout(self.layouts["raw_slider_layout"])
# 范围调整按钮布局
self.layouts["range_buttons_layout"] = QtWidgets.QHBoxLayout()
self.layouts["range_buttons_layout"].setContentsMargins(0, 0, 0, 0)
self.layouts["range_buttons_layout"].setSpacing(2)
# 设置Range按钮的策略使其均等撑满一行
size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
self.buttons["range_plus"].setSizePolicy(size_policy)
self.buttons["range_minus"].setSizePolicy(size_policy)
self.layouts["range_buttons_layout"].addWidget(self.buttons["range_plus"])
self.layouts["range_buttons_layout"].addWidget(self.buttons["range_minus"])
# 添加范围按钮布局到Raw Control布局
self.layouts["raw_control_layout"].addLayout(self.layouts["range_buttons_layout"])
# 页码按钮布局
self.layouts["page_buttons_layout"] = QtWidgets.QHBoxLayout()
self.layouts["page_buttons_layout"].setContentsMargins(0, 0, 0, 0)
self.layouts["page_buttons_layout"].setSpacing(2)
# 设置页码按钮的策略,使其均等撑满一行
for button_name in ["prev_page", "next_page", "page_all", "page_2", "page_3", "page_4", "page_5", "page_6"]:
if button_name in self.buttons:
self.buttons[button_name].setSizePolicy(size_policy)
# 添加页码按钮到布局
for button_name in ["prev_page", "next_page", "page_all", "page_2", "page_3", "page_4", "page_5", "page_6"]:
if button_name in self.buttons:
self.layouts["page_buttons_layout"].addWidget(self.buttons[button_name])
# 添加页码按钮布局到Raw Control布局
self.layouts["raw_control_layout"].addLayout(self.layouts["page_buttons_layout"])
# 添加Raw Control组到左侧面板
self.layouts["left_layout"].addWidget(self.controls["raw_control_group"])
# 右侧面板控件 - Related BlendShapes
self.controls["blendshapes_group"] = QtWidgets.QGroupBox("Related BlendShapes")
self.controls["blendshapes_group"].setObjectName("blendshapesGroup")
self.controls["blendshapes_group"].setMinimumWidth(0) # 设置最小宽度为零
self.controls["blendshapes_group"].setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
# 右侧面板控件布局
self.layouts["blendshapes_layout"] = QtWidgets.QVBoxLayout()
self.layouts["blendshapes_layout"].setContentsMargins(2, 2, 2, 2)
self.layouts["blendshapes_layout"].setSpacing(2)
self.controls["blendshapes_group"].setLayout(self.layouts["blendshapes_layout"])
# 添加BlendShapes列表到BlendShapes布局
self.layouts["blendshapes_layout"].addWidget(self.controls["blendshapes_list"], 1) # 设置伸缩因子为1使其占据更多空间
# 右侧滑块布局
self.layouts["blendshapes_layout"].addLayout(self.layouts["bs_slider_layout"])
# BS范围调整按钮布局
self.layouts["bs_range_buttons_layout"] = QtWidgets.QHBoxLayout()
self.layouts["bs_range_buttons_layout"].setContentsMargins(0, 0, 0, 0)
self.layouts["bs_range_buttons_layout"].setSpacing(2)
# 设置BS Range按钮的策略使其均等撑满一行
size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
self.buttons["bs_range_plus"].setSizePolicy(size_policy)
self.buttons["bs_range_minus"].setSizePolicy(size_policy)
self.layouts["bs_range_buttons_layout"].addWidget(self.buttons["bs_range_plus"])
self.layouts["bs_range_buttons_layout"].addWidget(self.buttons["bs_range_minus"])
# 添加BS范围按钮布局到BlendShapes布局
self.layouts["blendshapes_layout"].addLayout(self.layouts["bs_range_buttons_layout"])
# 为所有按钮设置统一的大小策略
for button_name in ["flip_target", "mirror_target", "find_flip_target",
"add_blendshape", "delete_blendshape", "batch_blendshape",
"rebuild_select", "reposition_joints", "blend_select"]:
if button_name in self.buttons:
self.buttons[button_name].setMinimumWidth(0)
self.buttons[button_name].setMinimumHeight(25) # 设置最小高度,确保按钮可见
self.buttons[button_name].setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
# BlendShape操作按钮网格 # BlendShape操作按钮网格
self.layouts["bs_buttons_grid"] = QtWidgets.QGridLayout() self.layouts["bs_buttons_grid"] = QtWidgets.QGridLayout()
@@ -337,136 +556,123 @@ class BehaviourUI(ui_utils.BaseUI):
self.layouts["bs_buttons_grid"].setSpacing(2) self.layouts["bs_buttons_grid"].setSpacing(2)
# 添加按钮到网格 # 添加按钮到网格
self.layouts["bs_buttons_grid"].addWidget(self.buttons["reset_blendshape"], 0, 0) self.layouts["bs_buttons_grid"].addWidget(self.buttons["flip_target"], 0, 0)
self.layouts["bs_buttons_grid"].addWidget(self.buttons["mirror_blendshape"], 0, 1) self.layouts["bs_buttons_grid"].addWidget(self.buttons["mirror_target"], 0, 1)
self.layouts["bs_buttons_grid"].addWidget(self.buttons["find_flip_target"], 0, 2)
self.layouts["bs_buttons_grid"].addWidget(self.buttons["add_blendshape"], 1, 0) self.layouts["bs_buttons_grid"].addWidget(self.buttons["add_blendshape"], 1, 0)
self.layouts["bs_buttons_grid"].addWidget(self.buttons["remove_blendshape"], 1, 1) self.layouts["bs_buttons_grid"].addWidget(self.buttons["delete_blendshape"], 1, 1)
self.layouts["bs_buttons_grid"].addWidget(self.buttons["update_blendshape"], 2, 0) self.layouts["bs_buttons_grid"].addWidget(self.buttons["batch_blendshape"], 1, 2)
self.layouts["bs_buttons_grid"].addWidget(self.buttons["isolate_blendshape"], 2, 1) self.layouts["bs_buttons_grid"].addWidget(self.buttons["rebuild_select"], 2, 0)
self.layouts["bs_buttons_grid"].addWidget(self.buttons["new_blendshape"], 3, 0) self.layouts["bs_buttons_grid"].addWidget(self.buttons["reposition_joints"], 2, 1)
self.layouts["bs_buttons_grid"].addWidget(self.buttons["combine_blendshape"], 3, 1) self.layouts["bs_buttons_grid"].addWidget(self.buttons["blend_select"], 2, 2)
# 添加按钮网格到BlendShapes布局
self.layouts["blendshapes_layout"].addLayout(self.layouts["bs_buttons_grid"]) self.layouts["blendshapes_layout"].addLayout(self.layouts["bs_buttons_grid"])
# 标签页按钮行 # 添加BlendShapes组到右侧面板
self.layouts["right_layout"].addWidget(self.controls["blendshapes_group"], 1) # 设置伸缩因子为1使其占据更多空间
# 底部标签页按钮行
self.layouts["tab_buttons_layout"] = QtWidgets.QHBoxLayout() self.layouts["tab_buttons_layout"] = QtWidgets.QHBoxLayout()
self.layouts["tab_buttons_layout"].setContentsMargins(0, 0, 0, 0) self.layouts["tab_buttons_layout"].setContentsMargins(0, 0, 0, 0)
self.layouts["tab_buttons_layout"].setSpacing(2) self.layouts["tab_buttons_layout"].setSpacing(0)
self.layouts["tab_buttons_layout"].addWidget(self.buttons["tab_bs"]) # 设置标签页按钮的策略,使其均等撑满一行
self.layouts["tab_buttons_layout"].addWidget(self.buttons["tab_psd"]) for button_name in ["tab_psd", "tab_bse", "tab_key", "tab_mir", "tab_ark", "tab_ctr"]:
self.layouts["tab_buttons_layout"].addWidget(self.buttons["tab_key"]) if button_name in self.buttons:
self.layouts["tab_buttons_layout"].addWidget(self.buttons["tab_jnt"]) self.buttons[button_name].setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
self.layouts["tab_buttons_layout"].addWidget(self.buttons["tab_ark"]) self.layouts["tab_buttons_layout"].addWidget(self.buttons[button_name])
self.layouts["tab_buttons_layout"].addWidget(self.buttons["tab_ctr"])
self.layouts["blendshapes_layout"].addLayout(self.layouts["tab_buttons_layout"]) # 添加标签页按钮行到主布局
self.layouts["main_layout"].addLayout(self.layouts["tab_buttons_layout"])
# 添加BlendShapes组到右侧面板 # 底部按钮布局
self.layouts["right_layout"].addWidget(self.controls["blendshapes_group"]) self.layouts["bottom_buttons_layout"] = QtWidgets.QVBoxLayout()
self.layouts["bottom_buttons_layout"].setContentsMargins(0, 0, 0, 0)
self.layouts["bottom_buttons_layout"].setSpacing(5)
# 底部工具面板 # 第一行按钮
self.layouts["bottom_panel"] = QtWidgets.QVBoxLayout() self.layouts["bottom_buttons_row1"] = QtWidgets.QHBoxLayout()
self.layouts["bottom_panel"].setContentsMargins(2, 2, 2, 2) self.layouts["bottom_buttons_row1"].setContentsMargins(0, 0, 0, 0)
self.layouts["bottom_panel"].setSpacing(2) self.layouts["bottom_buttons_row1"].setSpacing(5)
# 底部滑块 self.layouts["bottom_buttons_row1"].addWidget(self.buttons["reset_default_expression"])
self.layouts["bottom_panel"].addWidget(self.controls["bottom_main_slider"]) self.layouts["bottom_buttons_row1"].addWidget(self.buttons["find_select_expression"])
self.layouts["bottom_buttons_row1"].addWidget(self.buttons["write_current_expressions"])
# 底部按钮网格 # 第二行按钮
self.layouts["bottom_buttons_grid"] = QtWidgets.QGridLayout() self.layouts["bottom_buttons_row2"] = QtWidgets.QHBoxLayout()
self.layouts["bottom_buttons_grid"].setContentsMargins(0, 0, 0, 0) self.layouts["bottom_buttons_row2"].setContentsMargins(0, 0, 0, 0)
self.layouts["bottom_buttons_grid"].setSpacing(2) self.layouts["bottom_buttons_row2"].setSpacing(5)
# 添加底部按钮 self.layouts["bottom_buttons_row2"].addWidget(self.buttons["controller_find"])
self.layouts["bottom_buttons_grid"].addWidget(self.buttons["exchange_from_expression"], 0, 0) self.layouts["bottom_buttons_row2"].addWidget(self.buttons["select_associated_joint"])
self.layouts["bottom_buttons_grid"].addWidget(self.buttons["reset_selected_expression"], 0, 1) self.layouts["bottom_buttons_row2"].addWidget(self.buttons["write_find_mirror"])
self.layouts["bottom_buttons_grid"].addWidget(self.buttons["import_expression"], 0, 2)
self.layouts["bottom_buttons_grid"].addWidget(self.buttons["control_panel_search"], 1, 0)
self.layouts["bottom_buttons_grid"].addWidget(self.buttons["select_related_keys"], 1, 1)
self.layouts["bottom_buttons_grid"].addWidget(self.buttons["import_map_expression"], 1, 2)
self.layouts["bottom_panel"].addLayout(self.layouts["bottom_buttons_grid"]) # 将两行按钮添加到底部按钮布局
self.layouts["bottom_buttons_layout"].addLayout(self.layouts["bottom_buttons_row1"])
self.layouts["bottom_buttons_layout"].addLayout(self.layouts["bottom_buttons_row2"])
# 添加底部面板到主布局 # 添加底部按钮布局到主布局
self.layouts["main_layout"].addLayout(self.layouts["bottom_panel"]) self.layouts["main_layout"].addLayout(self.layouts["bottom_buttons_layout"])
# 将左右面板添加到主分割器 # 底部主滑块布局
self.splitters["main_splitter"].addWidget(self.controls["left_panel"]) self.layouts["main_layout"].addLayout(self.layouts["bottom_slider_layout"])
self.splitters["main_splitter"].addWidget(self.controls["right_panel"])
# 设置分割器初始大小和伸缩因子
self.splitters["main_splitter"].setSizes([500, 500])
# 设置分割器的伸缩因子,确保左右栏能够自动调整宽度
for i in range(self.splitters["main_splitter"].count()):
self.splitters["main_splitter"].setStretchFactor(i, 1)
#======================================= CONNECTION ===================================== #======================================= CONNECTION =====================================
def create_connections(self): def create_connections(self):
""" """
连接信号和槽 创建信号连接
设置UI控件的交互行为 连接按钮点击事件和其他UI事件
""" """
# 导入行为工具函数 # 创建信号映射字典
from scripts.utils import utils_behaviour signal_mapping = {
'buttons': {
# 底部标签页按钮
'tab_psd': {'signal': 'clicked', 'handler': lambda: utils_behaviour.switch_tab(0)},
'tab_bse': {'signal': 'clicked', 'handler': lambda: utils_behaviour.switch_tab(1)},
'tab_key': {'signal': 'clicked', 'handler': lambda: utils_behaviour.switch_tab(2)},
'tab_mir': {'signal': 'clicked', 'handler': lambda: utils_behaviour.switch_tab(3)},
'tab_ark': {'signal': 'clicked', 'handler': lambda: utils_behaviour.switch_tab(4)},
'tab_ctr': {'signal': 'clicked', 'handler': lambda: utils_behaviour.switch_tab(5)},
# 左侧面板连接 # 功能按钮
# 搜索框连接 'flip_target': {'signal': 'clicked', 'handler': utils_behaviour.flip_target},
self.controls["search_input"].textChanged.connect(utils_behaviour.filter_controls) 'mirror_target': {'signal': 'clicked', 'handler': utils_behaviour.mirror_target},
'find_flip_target': {'signal': 'clicked', 'handler': utils_behaviour.find_flip_target},
'add_blendshape': {'signal': 'clicked', 'handler': utils_behaviour.add_blendshape},
'delete_blendshape': {'signal': 'clicked', 'handler': utils_behaviour.delete_blendshape},
'batch_blendshape': {'signal': 'clicked', 'handler': utils_behaviour.batch_blendshape},
'rebuild_select': {'signal': 'clicked', 'handler': utils_behaviour.rebuild_select},
'reposition_joints': {'signal': 'clicked', 'handler': utils_behaviour.reposition_joints},
'blend_select': {'signal': 'clicked', 'handler': utils_behaviour.blend_select},
'reset_default_expression': {'signal': 'clicked', 'handler': utils_behaviour.reset_default_expression},
'find_select_expression': {'signal': 'clicked', 'handler': utils_behaviour.find_select_expression},
'write_current_expressions': {'signal': 'clicked', 'handler': utils_behaviour.write_current_expressions},
'controller_find': {'signal': 'clicked', 'handler': utils_behaviour.controller_find},
'select_associated_joint': {'signal': 'clicked', 'handler': utils_behaviour.select_associated_joint},
'write_find_mirror': {'signal': 'clicked', 'handler': utils_behaviour.write_find_mirror},
# 控制列表连接 # 范围按钮
self.controls["control_list"].itemClicked.connect(utils_behaviour.control_selected) 'range_plus': {'signal': 'clicked', 'handler': lambda: utils_behaviour.adjust_range(0.1)},
self.controls["control_list"].itemSelectionChanged.connect(utils_behaviour.update_control_selection) 'range_minus': {'signal': 'clicked', 'handler': lambda: utils_behaviour.adjust_range(-0.1)},
'bs_range_plus': {'signal': 'clicked', 'handler': lambda: utils_behaviour.adjust_bs_range(0.1)},
'bs_range_minus': {'signal': 'clicked', 'handler': lambda: utils_behaviour.adjust_bs_range(-0.1)},
},
'controls': {
# 滑块控件
'raw_slider': {'signal': 'valueChanged', 'handler': utils_behaviour.on_raw_slider_changed},
'bs_slider': {'signal': 'valueChanged', 'handler': utils_behaviour.on_bs_slider_changed},
'bottom_main_slider': {'signal': 'valueChanged', 'handler': utils_behaviour.on_bottom_slider_changed},
# 底部滑块连接 # 列表控件
self.controls["bottom_slider"].valueChanged.connect(utils_behaviour.update_control_value) 'control_list': {'signal': 'itemSelectionChanged', 'handler': utils_behaviour.on_control_selection_changed},
'blendshapes_list': {'signal': 'itemSelectionChanged', 'handler': utils_behaviour.on_blendshape_selection_changed},
}
}
# 页码按钮连接 # 使用ui_utils中的函数连接信号
self.buttons["page_all"].clicked.connect(lambda: utils_behaviour.switch_page("all")) ui_utils.connect_ui_signals(self, signal_mapping)
self.buttons["page_2"].clicked.connect(lambda: utils_behaviour.switch_page("2"))
self.buttons["page_3"].clicked.connect(lambda: utils_behaviour.switch_page("3"))
self.buttons["page_4"].clicked.connect(lambda: utils_behaviour.switch_page("4"))
self.buttons["page_5"].clicked.connect(lambda: utils_behaviour.switch_page("5"))
self.buttons["page_6"].clicked.connect(lambda: utils_behaviour.switch_page("6"))
# 左下角按钮连接 # 连接Maya选择变化事件
self.buttons["range_minus"].clicked.connect(utils_behaviour.decrease_range) ui_utils.connect_maya_selection_changed(self, lambda: utils_behaviour.on_selection_changed(self), self.main_widget)
self.buttons["range_plus"].clicked.connect(utils_behaviour.increase_range)
# 右侧面板连接
# BlendShapes列表连接
self.controls["blendshapes_list"].itemClicked.connect(utils_behaviour.blendshape_selected)
self.controls["blendshapes_list"].itemSelectionChanged.connect(utils_behaviour.update_blendshape_selection)
# 滑块连接
self.controls["bs_slider"].valueChanged.connect(utils_behaviour.update_blendshape_value)
# BlendShape操作按钮连接
self.buttons["reset_blendshape"].clicked.connect(utils_behaviour.reset_blendshape)
self.buttons["mirror_blendshape"].clicked.connect(utils_behaviour.mirror_blendshape)
self.buttons["add_blendshape"].clicked.connect(utils_behaviour.add_blendshape)
self.buttons["remove_blendshape"].clicked.connect(utils_behaviour.remove_blendshape)
self.buttons["update_blendshape"].clicked.connect(utils_behaviour.update_blendshape)
self.buttons["isolate_blendshape"].clicked.connect(utils_behaviour.isolate_blendshape)
self.buttons["new_blendshape"].clicked.connect(utils_behaviour.new_blendshape)
self.buttons["combine_blendshape"].clicked.connect(utils_behaviour.combine_blendshape)
# 标签页按钮连接
self.buttons["tab_bs"].clicked.connect(lambda: utils_behaviour.switch_tab("bs"))
self.buttons["tab_psd"].clicked.connect(lambda: utils_behaviour.switch_tab("psd"))
self.buttons["tab_key"].clicked.connect(lambda: utils_behaviour.switch_tab("key"))
self.buttons["tab_jnt"].clicked.connect(lambda: utils_behaviour.switch_tab("jnt"))
self.buttons["tab_ark"].clicked.connect(lambda: utils_behaviour.switch_tab("ark"))
self.buttons["tab_ctr"].clicked.connect(lambda: utils_behaviour.switch_tab("ctr"))
# 底部滑块连接
self.controls["bottom_main_slider"].valueChanged.connect(utils_behaviour.update_main_value)
# 底部按钮连接
self.buttons["exchange_from_expression"].clicked.connect(utils_behaviour.exchange_from_expression)
self.buttons["reset_selected_expression"].clicked.connect(utils_behaviour.reset_selected_expression)
self.buttons["import_expression"].clicked.connect(utils_behaviour.import_expression)
self.buttons["control_panel_search"].clicked.connect(utils_behaviour.control_panel_search)
self.buttons["select_related_keys"].clicked.connect(utils_behaviour.select_related_keys)
self.buttons["import_map_expression"].clicked.connect(utils_behaviour.import_map_expression)

View File

@@ -47,6 +47,8 @@ ASSETS_PATH = config.ASSETS_PATH
DNA_FILE_PATH = config.DNA_FILE_PATH DNA_FILE_PATH = config.DNA_FILE_PATH
DNA_IMG_PATH = config.DNA_IMG_PATH DNA_IMG_PATH = config.DNA_IMG_PATH
TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON
TOOL_WIDTH = config.TOOL_WIDTH
TOOL_HEIGHT = config.TOOL_HEIGHT
#========================================= LOCATION ======================================= #========================================= LOCATION =======================================
from scripts.ui import localization from scripts.ui import localization
LANG = localization.LANG LANG = localization.LANG
@@ -64,15 +66,37 @@ class DefinitionUI(ui_utils.BaseUI):
""" """
super(DefinitionUI, self).__init__() super(DefinitionUI, self).__init__()
# 设置单例实例
DefinitionUI._instance = self
# 创建主控件 # 创建主控件
self.main_widget = QtWidgets.QWidget() self.main_widget = QtWidgets.QWidget()
self.main_widget.setObjectName("definitionMainWidget") self.main_widget.setObjectName("definitionMainWidget")
# 初始化字典
self.controls = {}
self.layouts = {}
self.splitters = {}
self.buttons = {}
# 初始化UI # 初始化UI
self.create_widgets() self.create_widgets()
self.create_layouts() self.create_layouts()
self.create_connections() self.create_connections()
# 添加左右面板到分割器
self.splitters["main_splitter"].addWidget(self.controls["left_panel"])
self.splitters["main_splitter"].addWidget(self.controls["right_panel"])
# 设置分割器所有子元素的最小尺寸为0确保可以自由调整
ui_utils.set_splitter_children_minimum_size(self, recursive=True)
# 设置所有控件的最小尺寸为0确保分割器可以自由移动
ui_utils.set_all_controls_minimum_size(self)
# 使用ui_utils中的函数强制设置均等大小
ui_utils.force_equal_splitter_sizes(self)
#========================================= WIDGET ======================================= #========================================= WIDGET =======================================
def create_widgets(self): def create_widgets(self):
""" """
@@ -89,6 +113,9 @@ class DefinitionUI(ui_utils.BaseUI):
# 主分割器 # 主分割器
self.splitters["main_splitter"] = QtWidgets.QSplitter(QtCore.Qt.Horizontal) self.splitters["main_splitter"] = QtWidgets.QSplitter(QtCore.Qt.Horizontal)
self.splitters["main_splitter"].setObjectName("definitionMainSplitter") self.splitters["main_splitter"].setObjectName("definitionMainSplitter")
self.splitters["main_splitter"].setHandleWidth(6) # 设置分割器手柄宽度
self.splitters["main_splitter"].setChildrenCollapsible(False) # 禁止子部件折叠
self.splitters["main_splitter"].setOpaqueResize(True) # 实时显示调整效果
# 左侧面板 # 左侧面板
self.controls["left_panel"] = QtWidgets.QWidget() self.controls["left_panel"] = QtWidgets.QWidget()
@@ -221,8 +248,14 @@ class DefinitionUI(ui_utils.BaseUI):
# 添加标题标签 # 添加标题标签
self.layouts["main_layout"].addWidget(self.controls["title_label"]) self.layouts["main_layout"].addWidget(self.controls["title_label"])
# 添加主分割器 # 主分割器布局
self.layouts["main_layout"].addWidget(self.splitters["main_splitter"]) self.layouts["main_splitter_layout"] = QtWidgets.QHBoxLayout()
self.layouts["main_splitter_layout"].setContentsMargins(0, 0, 0, 0)
self.layouts["main_splitter_layout"].setSpacing(0)
self.layouts["main_splitter_layout"].addWidget(self.splitters["main_splitter"])
# 添加主分割器布局到主布局
self.layouts["main_layout"].addLayout(self.layouts["main_splitter_layout"])
# 左侧面板布局 # 左侧面板布局
self.layouts["left_layout"] = QtWidgets.QVBoxLayout(self.controls["left_panel"]) self.layouts["left_layout"] = QtWidgets.QVBoxLayout(self.controls["left_panel"])
@@ -339,23 +372,6 @@ class DefinitionUI(ui_utils.BaseUI):
# 添加底部面板到主布局 # 添加底部面板到主布局
self.layouts["main_layout"].addLayout(self.layouts["bottom_panel"]) self.layouts["main_layout"].addLayout(self.layouts["bottom_panel"])
# 设置分割器初始大小
# 主分割器左右各半
self.splitters["main_splitter"].setSizes([500, 500])
# 左侧分割器上下各半
self.splitters["left_splitter"].setSizes([250, 250])
# 右侧分割器均匀分配
right_item_height = 1000 // self.splitters["right_splitter"].count()
self.splitters["right_splitter"].setSizes([right_item_height] * self.splitters["right_splitter"].count())
# 设置分割器的伸缩因子,确保左右栏能够自动调整宽度
for i in range(self.splitters["main_splitter"].count()):
self.splitters["main_splitter"].setStretchFactor(i, 1)
# 将左右面板添加到主分割器
self.splitters["main_splitter"].addWidget(self.controls["left_panel"])
self.splitters["main_splitter"].addWidget(self.controls["right_panel"])
#======================================= CONNECTION ===================================== #======================================= CONNECTION =====================================
def create_connections(self): def create_connections(self):
""" """

View File

@@ -52,6 +52,8 @@ ASSETS_PATH = config.ASSETS_PATH
DNA_FILE_PATH = config.DNA_FILE_PATH DNA_FILE_PATH = config.DNA_FILE_PATH
DNA_IMG_PATH = config.DNA_IMG_PATH DNA_IMG_PATH = config.DNA_IMG_PATH
TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON
TOOL_WIDTH = config.TOOL_WIDTH
TOOL_HEIGHT = config.TOOL_HEIGHT
#========================================= LOCATION ======================================= #========================================= LOCATION =======================================
from scripts.ui import localization from scripts.ui import localization
LANG = localization.LANG LANG = localization.LANG
@@ -265,10 +267,10 @@ class GeometryUI(ui_utils.BaseUI):
input_field.setPlaceholderText(LANG.get("enter_model_name", "输入模型名称")) input_field.setPlaceholderText(LANG.get("enter_model_name", "输入模型名称"))
# 创建加载按钮 # 创建加载按钮
load_button = QtWidgets.QPushButton(LANG.get("load", "加载")) load_button = QtWidgets.QPushButton(LANG.get(" load ", " 加 载 "))
load_button.setObjectName(f"{lod_name}_{part}_load_button") load_button.setObjectName(f"{lod_name}_{part}_load_button")
load_button.setIcon(QtGui.QIcon(os.path.join(ICONS_PATH, "loading.png"))) load_button.setIcon(QtGui.QIcon(os.path.join(ICONS_PATH, "loading.png")))
load_button.setFixedSize(60, 24) # 增加宽度以容纳文字 load_button.setFixedSize(120, 24)
load_button.setToolTip(LANG.get("load_model", "加载模型")) load_button.setToolTip(LANG.get("load_model", "加载模型"))
# 将控件添加到布局 # 将控件添加到布局
@@ -294,8 +296,8 @@ class GeometryUI(ui_utils.BaseUI):
trash_icon = QtGui.QIcon(os.path.join(ICONS_PATH, "delete.png")) trash_icon = QtGui.QIcon(os.path.join(ICONS_PATH, "delete.png"))
trash_button = QtWidgets.QPushButton() trash_button = QtWidgets.QPushButton()
trash_button.setIcon(trash_icon) trash_button.setIcon(trash_icon)
trash_button.setIconSize(QtCore.QSize(32, 32)) # 设置图标大小为32x32 trash_button.setIconSize(QtCore.QSize(28, 28))
trash_button.setFixedSize(32, 32) # 增大按钮尺寸 trash_button.setFixedSize(28, 28)
trash_button.setToolTip(LANG.get("delete", "删除")) trash_button.setToolTip(LANG.get("delete", "删除"))
trash_button.setStyleSheet("QPushButton { border: none; background-color: transparent; }") trash_button.setStyleSheet("QPushButton { border: none; background-color: transparent; }")
self.controls["tab_widget"].setCornerWidget(trash_button, QtCore.Qt.TopRightCorner) self.controls["tab_widget"].setCornerWidget(trash_button, QtCore.Qt.TopRightCorner)

View File

@@ -50,6 +50,8 @@ ASSETS_PATH = config.ASSETS_PATH
DNA_FILE_PATH = config.DNA_FILE_PATH DNA_FILE_PATH = config.DNA_FILE_PATH
DNA_IMG_PATH = config.DNA_IMG_PATH DNA_IMG_PATH = config.DNA_IMG_PATH
TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON
TOOL_WIDTH = config.TOOL_WIDTH
TOOL_HEIGHT = config.TOOL_HEIGHT
#========================================= LOCATION ======================================= #========================================= LOCATION =======================================
from scripts.ui import localization from scripts.ui import localization
LANG = localization.LANG LANG = localization.LANG
@@ -643,12 +645,12 @@ class RiggingUI(ui_utils.BaseUI):
#======================================= FUNCTIONS ====================================== #======================================= FUNCTIONS ======================================
def create_connections(self): def create_connections(self):
""" """
连接UI信号和槽 创建信号连接
设置UI控件的事件处理函数
""" """
# 创建信号映射字典 # 创建信号映射字典
signal_mapping = { signal_mapping = {
'buttons': { 'buttons': {
# 绑定所有功能按钮
'add_joint_btn': {'signal': 'clicked', 'handler': utils_rigging.add_joint}, 'add_joint_btn': {'signal': 'clicked', 'handler': utils_rigging.add_joint},
'remove_joint_btn': {'signal': 'clicked', 'handler': utils_rigging.remove_joint}, 'remove_joint_btn': {'signal': 'clicked', 'handler': utils_rigging.remove_joint},
'duplicate_joint_btn': {'signal': 'clicked', 'handler': utils_rigging.duplicate_joint}, 'duplicate_joint_btn': {'signal': 'clicked', 'handler': utils_rigging.duplicate_joint},
@@ -658,6 +660,12 @@ class RiggingUI(ui_utils.BaseUI):
'import_dna_btn': {'signal': 'clicked', 'handler': utils_rigging.import_dna}, 'import_dna_btn': {'signal': 'clicked', 'handler': utils_rigging.import_dna},
'export_dna_btn': {'signal': 'clicked', 'handler': utils_rigging.export_dna}, 'export_dna_btn': {'signal': 'clicked', 'handler': utils_rigging.export_dna},
'calibrate_dna_btn': {'signal': 'clicked', 'handler': utils_rigging.calibrate_dna}, 'calibrate_dna_btn': {'signal': 'clicked', 'handler': utils_rigging.calibrate_dna},
# 已创建的按钮
'browse_path': {'signal': 'clicked', 'handler': lambda: utils_rigging.browse_file(self, "项目路径", self.controls["project_path_input"])},
'browse_dna': {'signal': 'clicked', 'handler': lambda: utils_rigging.browse_file(self, "DNA文件", self.controls["presets_dna_input"], "dna")},
'export_presets': {'signal': 'clicked', 'handler': utils_rigging.export_presets},
'import_presets': {'signal': 'clicked', 'handler': utils_rigging.import_presets},
}, },
'splitters': { 'splitters': {
'main_splitter': {'signal': 'splitterMoved', 'handler': lambda pos, index: ui_utils.on_splitter_moved(self, pos, index)}, 'main_splitter': {'signal': 'splitterMoved', 'handler': lambda pos, index: ui_utils.on_splitter_moved(self, pos, index)},
@@ -667,46 +675,5 @@ class RiggingUI(ui_utils.BaseUI):
# 使用ui_utils中的通用函数连接信号 # 使用ui_utils中的通用函数连接信号
ui_utils.connect_ui_signals(self, signal_mapping) ui_utils.connect_ui_signals(self, signal_mapping)
# 连接关节属性编辑控 # 连接Maya选择变化事
if hasattr(self, 'joint_name_edit'): ui_utils.connect_maya_selection_changed(self, lambda: utils_rigging.on_selection_changed(self), self.main_widget)
self.joint_name_edit.editingFinished.connect(lambda: utils_rigging.handle_joint_name_changed(self.joint_name_edit))
# 关节位置编辑
if hasattr(self, 'joint_pos_x_spin'):
self.joint_pos_x_spin.valueChanged.connect(lambda val: utils_rigging.handle_joint_position_changed(0, [self.joint_pos_x_spin, self.joint_pos_y_spin, self.joint_pos_z_spin]))
if hasattr(self, 'joint_pos_y_spin'):
self.joint_pos_y_spin.valueChanged.connect(lambda val: utils_rigging.handle_joint_position_changed(1, [self.joint_pos_x_spin, self.joint_pos_y_spin, self.joint_pos_z_spin]))
if hasattr(self, 'joint_pos_z_spin'):
self.joint_pos_z_spin.valueChanged.connect(lambda val: utils_rigging.handle_joint_position_changed(2, [self.joint_pos_x_spin, self.joint_pos_y_spin, self.joint_pos_z_spin]))
# 关节旋转编辑
if hasattr(self, 'joint_rot_x_spin'):
self.joint_rot_x_spin.valueChanged.connect(lambda val: utils_rigging.handle_joint_rotation_changed(0, [self.joint_rot_x_spin, self.joint_rot_y_spin, self.joint_rot_z_spin]))
if hasattr(self, 'joint_rot_y_spin'):
self.joint_rot_y_spin.valueChanged.connect(lambda val: utils_rigging.handle_joint_rotation_changed(1, [self.joint_rot_x_spin, self.joint_rot_y_spin, self.joint_rot_z_spin]))
if hasattr(self, 'joint_rot_z_spin'):
self.joint_rot_z_spin.valueChanged.connect(lambda val: utils_rigging.handle_joint_rotation_changed(2, [self.joint_rot_x_spin, self.joint_rot_y_spin, self.joint_rot_z_spin]))
# 关节缩放编辑
if hasattr(self, 'joint_scale_x_spin'):
self.joint_scale_x_spin.valueChanged.connect(lambda val: utils_rigging.handle_joint_scale_changed(0, [self.joint_scale_x_spin, self.joint_scale_y_spin, self.joint_scale_z_spin]))
if hasattr(self, 'joint_scale_y_spin'):
self.joint_scale_y_spin.valueChanged.connect(lambda val: utils_rigging.handle_joint_scale_changed(1, [self.joint_scale_x_spin, self.joint_scale_y_spin, self.joint_scale_z_spin]))
if hasattr(self, 'joint_scale_z_spin'):
self.joint_scale_z_spin.valueChanged.connect(lambda val: utils_rigging.handle_joint_scale_changed(2, [self.joint_scale_x_spin, self.joint_scale_y_spin, self.joint_scale_z_spin]))
# 关节属性应用和重置按钮
if "apply_joint_props_btn" in self.buttons:
pos_widgets = [self.joint_pos_x_spin, self.joint_pos_y_spin, self.joint_pos_z_spin] if all(hasattr(self, attr) for attr in ['joint_pos_x_spin', 'joint_pos_y_spin', 'joint_pos_z_spin']) else None
rot_widgets = [self.joint_rot_x_spin, self.joint_rot_y_spin, self.joint_rot_z_spin] if all(hasattr(self, attr) for attr in ['joint_rot_x_spin', 'joint_rot_y_spin', 'joint_rot_z_spin']) else None
scale_widgets = [self.joint_scale_x_spin, self.joint_scale_y_spin, self.joint_scale_z_spin] if all(hasattr(self, attr) for attr in ['joint_scale_x_spin', 'joint_scale_y_spin', 'joint_scale_z_spin']) else None
self.buttons["apply_joint_props_btn"].clicked.connect(lambda: utils_rigging.apply_joint_properties_from_ui(pos_widgets, rot_widgets, scale_widgets))
if "reset_joint_props_btn" in self.buttons:
pos_widgets = [self.joint_pos_x_spin, self.joint_pos_y_spin, self.joint_pos_z_spin] if all(hasattr(self, attr) for attr in ['joint_pos_x_spin', 'joint_pos_y_spin', 'joint_pos_z_spin']) else None
rot_widgets = [self.joint_rot_x_spin, self.joint_rot_y_spin, self.joint_rot_z_spin] if all(hasattr(self, attr) for attr in ['joint_rot_x_spin', 'joint_rot_y_spin', 'joint_rot_z_spin']) else None
scale_widgets = [self.joint_scale_x_spin, self.joint_scale_y_spin, self.joint_scale_z_spin] if all(hasattr(self, attr) for attr in ['joint_scale_x_spin', 'joint_scale_y_spin', 'joint_scale_z_spin']) else None
self.buttons["reset_joint_props_btn"].clicked.connect(lambda: utils_rigging.reset_joint_properties_ui(pos_widgets, rot_widgets, scale_widgets))
# 使用Maya的脚本任务来监听选择变化
ui_utils.connect_maya_selection_changed(self, utils_rigging.on_selection_changed)

View File

@@ -1,4 +1,4 @@
/* MetaFusion 插件样式表 */ /* 插件样式表 */
/* 作者: CGNICO */ /* 作者: CGNICO */
/* 版本: Alpha v1.0.0 */ /* 版本: Alpha v1.0.0 */
@@ -276,17 +276,26 @@ QSplitter::handle {
} }
QSplitter::handle:horizontal { QSplitter::handle:horizontal {
width: 2px; width: 6px;
} }
QSplitter::handle:vertical { QSplitter::handle:vertical {
height: 2px; height: 6px;
} }
QSplitter::handle:hover { QSplitter::handle:hover {
background-color: #007ACC; background-color: #007ACC;
} }
QSplitter {
border: none;
}
QSplitter > QWidget {
min-width: 0px;
min-height: 0px;
}
/* ==================== 滑块样式 ==================== */ /* ==================== 滑块样式 ==================== */
QSlider::groove:horizontal { QSlider::groove:horizontal {
border: 1px solid #1E1E1E; border: 1px solid #1E1E1E;

View File

@@ -49,6 +49,8 @@ ASSETS_PATH = config.ASSETS_PATH
DNA_FILE_PATH = config.DNA_FILE_PATH DNA_FILE_PATH = config.DNA_FILE_PATH
DNA_IMG_PATH = config.DNA_IMG_PATH DNA_IMG_PATH = config.DNA_IMG_PATH
TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON
TOOL_WIDTH = config.TOOL_WIDTH
TOOL_HEIGHT = config.TOOL_HEIGHT
#========================================= LOCATION ======================================= #========================================= LOCATION =======================================
from scripts.ui import localization from scripts.ui import localization
LANG = localization.LANG LANG = localization.LANG

View File

@@ -19,6 +19,8 @@ import traceback
import locale import locale
import sys import sys
import os import os
import weakref
#========================================== CONFIG ======================================== #========================================== CONFIG ========================================
import config import config
TOOL_NAME = config.TOOL_NAME TOOL_NAME = config.TOOL_NAME
@@ -40,81 +42,21 @@ ASSETS_PATH = config.ASSETS_PATH
DNA_FILE_PATH = config.DNA_FILE_PATH DNA_FILE_PATH = config.DNA_FILE_PATH
DNA_IMG_PATH = config.DNA_IMG_PATH DNA_IMG_PATH = config.DNA_IMG_PATH
TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON
TOOL_WIDTH = config.TOOL_WIDTH
TOOL_HEIGHT = config.TOOL_HEIGHT
#========================================= LOCATION ======================================= #========================================= LOCATION =======================================
from scripts.ui import localization from scripts.ui import localization
LANG = localization.LANG LANG = localization.LANG
#============================================ INIT ========================================== #============================================ UI BASE ==========================================
# 定义窗口大小变化事件处理类
# 用于监听窗口大小变化并调整分割器宽度
class SplitterResizeHandler(QtCore.QObject):
def __init__(self, parent_tab, main_splitter, is_horizontal=True, panel_count=2, panel_ratio=None):
super(SplitterResizeHandler, self).__init__(parent_tab)
self.parent_tab = parent_tab
self.main_splitter = main_splitter
self.is_horizontal = is_horizontal # 是否为水平分割器
self.panel_count = panel_count # 面板数量默认为2左右面板
# 面板比例默认为None则使用默认比例
# 如果是2面板默认比例为[1, 1](左右均等)
# 如果是3面板默认比例为[4, 1, 2]Presets面板较大Assets面板较小Descriptor面板中等
self.panel_ratio = panel_ratio if panel_ratio else ([1, 1] if panel_count == 2 else [4, 1, 2])
# 安装事件过滤器到父标签页
self.parent_tab.installEventFilter(self)
def eventFilter(self, obj, event):
# 监听大小变化事件
if obj == self.parent_tab and event.type() == QtCore.QEvent.Resize:
# 当窗口大小变化时,调整分割器宽度
self.adjustSplitterSizes()
return super(SplitterResizeHandler, self).eventFilter(obj, event)
def adjustSplitterSizes(self):
# 确保分割器存在且父标签页可见
if self.main_splitter and self.parent_tab.isVisible():
# 计算比例总和
ratio_sum = sum(self.panel_ratio)
if self.is_horizontal:
# 获取父标签页当前宽度
width = self.parent_tab.width()
# 根据面板数量设置分割器大小
if self.panel_count == 2:
# 设置分割器左右宽度按比例分配
sizes = [int(width * ratio / ratio_sum) for ratio in self.panel_ratio]
self.main_splitter.setSizes(sizes)
print(f"分割器 - 窗口大小变化,调整水平分割器宽度为: {sizes}")
elif self.panel_count == 3:
# 设置三面板分割器宽度按比例分配
sizes = [int(width * ratio / ratio_sum) for ratio in self.panel_ratio]
self.main_splitter.setSizes(sizes)
print(f"分割器 - 窗口大小变化,调整三面板水平分割器宽度为: {sizes}")
else:
# 获取父标签页当前高度
height = self.parent_tab.height()
# 根据面板数量设置分割器大小
if self.panel_count == 2:
# 设置分割器上下高度按比例分配
sizes = [int(height * ratio / ratio_sum) for ratio in self.panel_ratio]
self.main_splitter.setSizes(sizes)
print(f"分割器 - 窗口大小变化,调整垂直分割器高度为: {sizes}")
elif self.panel_count == 3:
# 设置三面板分割器高度按比例分配
sizes = [int(height * ratio / ratio_sum) for ratio in self.panel_ratio]
self.main_splitter.setSizes(sizes)
print(f"分割器 - 窗口大小变化,调整三面板垂直分割器高度为: {sizes}")
# 使用类来管理UI控件避免全局变量
class BaseUI(object): class BaseUI(object):
""" """
UI基类 UI基类
所有UI面板的基类提供通用的UI功能 所有UI面板的基类提供通用的UI功能
""" """
def __init__(self): def __init__(self):
""" """初始化UI基类"""
初始化UI基类
"""
# 初始化字典 # 初始化字典
self.controls = {} self.controls = {}
self.layouts = {} self.layouts = {}
@@ -126,214 +68,333 @@ class BaseUI(object):
# 创建主控件 # 创建主控件
self.main_widget = None self.main_widget = None
def on_show_event(self, event):
"""
显示事件处理
当面板显示时重置分割器大小
Args:
event: 显示事件对象
"""
# 重置分割器大小
self.reset_splitter_sizes()
# 调用父类的showEvent方法
super(BaseUI, self).showEvent(event)
def reset_splitter_sizes(self):
"""
重置分割器大小,设置三个面板的初始大小
Presets面板较大显示预设图片Assets面板较小Descriptor面板中等
"""
if hasattr(self, 'splitters') and 'main_splitter' in self.splitters and self.splitters["main_splitter"]:
# 尝试从设置中读取分割器大小
preset_size = cmds.optionVar(query="MetaFusionRiggingPresetsPanelSize") if cmds.optionVar(exists="MetaFusionRiggingPresetsPanelSize") else 400
assets_size = cmds.optionVar(query="MetaFusionRiggingAssetsPanelSize") if cmds.optionVar(exists="MetaFusionRiggingAssetsPanelSize") else 100
descriptor_size = cmds.optionVar(query="MetaFusionRiggingDescriptorPanelSize") if cmds.optionVar(exists="MetaFusionRiggingDescriptorPanelSize") else 200
# 设置分割器大小
self.splitters["main_splitter"].setSizes([preset_size, assets_size, descriptor_size])
def create_widgets(self): def create_widgets(self):
""" """创建UI控件"""
创建UI控件
创建所有UI控件并存储到字典中
"""
# 子类实现
pass pass
def create_layouts(self): def create_layouts(self):
""" """创建UI布局"""
创建UI布局
创建所有UI布局并存储到字典中
"""
# 子类实现
pass pass
def connect_ui_signals(self): def create_connections(self):
""" """连接UI信号和槽"""
连接UI信号和槽
设置UI控件的事件处理函数
"""
# 子类实现
pass pass
def showEvent(self, event): def showEvent(self, event):
""" """显示事件处理函数"""
显示事件 # 调用父类的showEvent方法
当面板显示时调用on_show_event方法 super(BaseUI, self).showEvent(event)
Args: # 强制设置分割器均等大小
event: 显示事件对象 if hasattr(self, 'splitters') and 'main_splitter' in self.splitters:
""" setup_splitter(self, 'main_splitter', equal_sizes=True)
# 调用自定义的显示事件处理方法
self.on_show_event(event)
# 分割器相关的工具函数
def update_splitter_position(splitter, sizes=None):
"""
更新分割器位置
Args:
splitter: 分割器对象
sizes: 分割器大小列表如果为None则使用当前分割器大小
"""
if not splitter:
return
# 记录当前分割器位置
if sizes is None:
sizes = splitter.sizes()
# 将分割器位置保存到设置中
# 如果是三面板分割器,则保存三个大小值
if len(sizes) == 3:
# 保存三面板分割器位置Presets、Assets、Descriptor
cmds.optionVar(intValue=["MetaFusionRiggingPresetsPanelSize", sizes[0]])
cmds.optionVar(intValue=["MetaFusionRiggingAssetsPanelSize", sizes[1]])
cmds.optionVar(intValue=["MetaFusionRiggingDescriptorPanelSize", sizes[2]])
else:
# 保存左右分割器位置
cmds.optionVar(intValue=["MetaFusionRiggingLeftPanelSize", sizes[0]])
cmds.optionVar(intValue=["MetaFusionRiggingRightPanelSize", sizes[1]])
print(f"Splitter sizes updated: {sizes}")
# 通用UI事件处理函数
def on_splitter_moved(ui_instance, pos, index):
"""
分割器移动事件处理
记录当前分割器位置
Args:
ui_instance: UI实例包含splitters字典
pos: 分割器位置
index: 分割器索引
"""
if hasattr(ui_instance, 'splitters') and 'main_splitter' in ui_instance.splitters:
update_splitter_position(ui_instance.splitters["main_splitter"])
#============================================ UI HELPERS ==========================================
def connect_ui_signals(ui_instance, signal_mapping): def connect_ui_signals(ui_instance, signal_mapping):
""" """连接UI信号和槽"""
连接UI信号和槽
设置UI控件的事件处理函数
Args:
ui_instance: UI实例包含控件和布局
signal_mapping: 信号映射字典,格式为:
{
'widget_type': { # 'buttons', 'inputs', 'splitters'
'widget_name': { # 控件名称
'signal': 'signal_name', # 信号名称,如'clicked', 'valueChanged'
'handler': handler_function, # 处理函数
'args': [arg1, arg2, ...] # 可选,处理函数的参数
}
}
}
"""
# 遍历信号映射字典 # 遍历信号映射字典
for widget_type, widgets in signal_mapping.items(): for widget_type, widgets in signal_mapping.items():
# 获取控件字典 # 获取控件字典
widget_dict = getattr(ui_instance, widget_type, {}) widget_dict = getattr(ui_instance, widget_type, {})
# 遍历控件 # 遍历控件字典
for widget_name, signal_info in widgets.items(): for widget_name, signal_info in widgets.items():
# 检查控件是否存在 # 获取控件
if widget_name in widget_dict: widget = widget_dict.get(widget_name)
widget = widget_dict[widget_name] if not widget:
# 静默处理未找到的控件,不显示警告
# print(f"警告: 未找到控件 {widget_name}")
continue
# 获取信号名称和处理函数
signal_name = signal_info.get('signal') signal_name = signal_info.get('signal')
handler = signal_info.get('handler') handler = signal_info.get('handler')
args = signal_info.get('args', [])
if not signal_name or not handler:
print(f"警告: 信号名称或处理函数未指定 {widget_name}")
continue
# 获取信号对象 # 获取信号对象
signal = getattr(widget, signal_name, None) signal = getattr(widget, signal_name, None)
if not signal:
print(f"警告: 未找到信号 {signal_name} 在控件 {widget_name}")
continue
# 获取可选参数
args = signal_info.get('args', [])
# 连接信号和槽 # 连接信号和槽
if signal and handler:
if args: if args:
signal.connect(lambda *extra, h=handler, a=args: h(*a)) signal.connect(lambda *_, handler=handler, args=args: handler(*args))
else: else:
signal.connect(handler) signal.connect(handler)
def connect_maya_selection_changed(ui_instance, handler, parent_widget=None): def connect_maya_selection_changed(ui_instance, handler, parent_widget=None):
""" """连接Maya选择变化事件"""
连接Maya选择变化事件 # 如果已经有scriptJob先删除
if hasattr(ui_instance, 'selection_job') and ui_instance.selection_job > 0:
try:
cmds.scriptJob(kill=ui_instance.selection_job, force=True)
except:
pass
Args: # 创建新的scriptJob
ui_instance: UI实例 parent_arg = {}
handler: 选择变化事件处理函数 if parent_widget and parent_widget.objectName():
parent_widget: 父控件用于设置scriptJob的parent参数 parent_arg = {"parent": parent_widget.objectName()}
"""
# 如果没有指定父控件则使用UI实例的main_widget
if parent_widget is None and hasattr(ui_instance, 'main_widget'):
parent_widget = ui_instance.main_widget
# 创建scriptJob ui_instance.selection_job = cmds.scriptJob(
if parent_widget: event=["SelectionChanged", handler],
job_id = cmds.scriptJob(event=["SelectionChanged", handler], parent=parent_widget.objectName()) protected=True,
return job_id **parent_arg
return None )
print(f"已连接选择变化事件, scriptJob ID: {ui_instance.selection_job}")
# 获取Maya主窗口 #============================================ MAYA HELPERS ==========================================
def get_maya_main_window(): def get_maya_main_window():
""" """获取Maya主窗口"""
获取Maya主窗口 ptr = omui.MQtUtil.mainWindow()
if ptr is not None:
Returns: return wrapInstance(int(ptr), QtWidgets.QWidget)
QWidget: Maya主窗口控件
"""
main_window_ptr = omui.MQtUtil.mainWindow()
return wrapInstance(int(main_window_ptr), QtWidgets.QWidget)
def get_parent_widget(widget_name): def get_parent_widget(widget_name):
""" """根据控件名称查找父容器控件"""
根据控件名称查找父容器控件 # 获取Maya主窗口
main_window = get_maya_main_window()
Args:
widget_name (str): 控件名称
Returns:
QWidget: 找到的父容器控件如果未找到则返回None
"""
# 查找主窗口中的所有控件
main_window = None
for widget in QtWidgets.QApplication.topLevelWidgets():
if widget.objectName() == f"{TOOL_NAME}MainWindow" or widget.objectName().endswith("MainWindow"):
main_window = widget
break
if not main_window: if not main_window:
print(f"无法找到主窗口,无法获取父容器: {widget_name}")
return None return None
# 在主窗口中查找指定名称的控件 # 查找所有子控件
found_widget = main_window.findChild(QtWidgets.QWidget, widget_name) def find_widget(parent, name):
if found_widget: # 检查当前控件
return found_widget if parent.objectName() == name:
return parent
# 如果未找到精确匹配,尝试模糊匹配 # 递归查找子控件
for child in main_window.findChildren(QtWidgets.QWidget): for child in parent.children():
if widget_name.lower() in child.objectName().lower(): if isinstance(child, QtWidgets.QWidget):
# 检查子控件
if child.objectName() == name:
return child return child
print(f"无法找到控件: {widget_name}") # 递归查找
result = find_widget(child, name)
if result:
return result
return None return None
# 从主窗口开始查找
return find_widget(main_window, widget_name)
def load_icon(icon_name):
"""
加载图标,支持多种来源
Args:
icon_name (str): 图标名称
Returns:
QIcon: 加载的图标对象
"""
if not icon_name:
return QtGui.QIcon()
# 尝试从插件图标路径加载
if ICONS_PATH and os.path.exists(ICONS_PATH):
# 检查不同的文件扩展名
extensions = ['', '.png', '.jpg', '.svg', '.ico']
for ext in extensions:
path = os.path.join(ICONS_PATH, icon_name + ext)
if os.path.exists(path):
return QtGui.QIcon(path)
# 尝试从Maya内置图标加载
for prefix in [':', ':/']:
try:
icon = QtGui.QIcon(QtGui.QPixmap(f"{prefix}{icon_name}"))
if not icon.isNull():
return icon
except:
continue
# 如果都失败,返回一个空图标
return QtGui.QIcon()
#============================================ SPLITTER ==========================================
def setup_splitter(ui_instance, splitter_name="main_splitter", equal_sizes=True):
"""
设置分割器的属性和大小,确保完全自由调整
Args:
ui_instance: UI实例对象
splitter_name (str): 分割器名称
equal_sizes (bool): 是否设置均等大小
Returns:
bool: 是否成功设置
"""
# 检查分割器是否存在
if not hasattr(ui_instance, 'splitters') or splitter_name not in ui_instance.splitters:
return False
# 获取分割器
splitter = ui_instance.splitters[splitter_name]
# 设置分割器属性
splitter.setOpaqueResize(True)
splitter.setChildrenCollapsible(False)
# 设置伸缩因子和子部件属性
for i in range(splitter.count()):
# 设置伸缩因子
splitter.setStretchFactor(i, 1)
# 设置子部件属性
widget = splitter.widget(i)
if widget:
widget.setMinimumWidth(0)
widget.setMinimumHeight(0)
widget.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
# 设置均等大小
if equal_sizes and splitter.count() > 0:
sizes = [1] * splitter.count()
splitter.setSizes(sizes)
splitter.update()
# 延迟设置确保生效
QtCore.QTimer.singleShot(500, lambda: splitter.setSizes(sizes))
return True
def force_equal_splitter_sizes(ui_instance, splitter_name="main_splitter"):
"""
强制设置分割器大小为均等,并确保完全自由调整
Args:
ui_instance: UI实例对象
splitter_name (str): 分割器名称
"""
# 调用通用的分割器设置函数,设置均等大小
return setup_splitter(ui_instance, splitter_name, equal_sizes=True)
def set_splitter_children_minimum_size(ui_instance, splitter_name="main_splitter", recursive=True):
"""
设置分割器所有子元素的最小宽度和高度为0允许完全自由调整
Args:
ui_instance: UI实例对象
splitter_name (str): 分割器名称
recursive (bool): 是否递归设置所有子元素
"""
# 检查分割器是否存在
if not hasattr(ui_instance, 'splitters') or splitter_name not in ui_instance.splitters:
return
# 获取分割器
splitter = ui_instance.splitters[splitter_name]
# 设置分割器属性
splitter.setOpaqueResize(True)
splitter.setChildrenCollapsible(False)
# 设置所有子部件的最小尺寸为0
for i in range(splitter.count()):
widget = splitter.widget(i)
if widget and recursive:
# 设置最小尺寸为0
widget.setMinimumWidth(0)
widget.setMinimumHeight(0)
widget.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
# 递归设置子控件
_set_widget_children_minimum_size(widget)
def _set_widget_children_minimum_size(widget):
"""
递归设置控件及其所有子控件的最小尺寸为0
Args:
widget: 要设置的控件
"""
# 递归设置所有子部件
for child in widget.findChildren(QtWidgets.QWidget):
# 设置每个子控件的最小宽度为0
child.setMinimumWidth(0)
# 对于按钮、标签等控件不应该设置最小高度为0否则会导致界面异常
if isinstance(child, (QtWidgets.QPushButton, QtWidgets.QToolButton,
QtWidgets.QLabel, QtWidgets.QLineEdit,
QtWidgets.QComboBox, QtWidgets.QCheckBox,
QtWidgets.QRadioButton)):
# 这些控件应该保持其默认高度只设置水平方向的策略为Expanding
policy = child.sizePolicy()
policy.setHorizontalPolicy(QtWidgets.QSizePolicy.Expanding)
child.setSizePolicy(policy)
else:
# 其他控件可以设置最小高度为0
child.setMinimumHeight(0)
child.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
# 特别处理容器控件
if isinstance(child, (QtWidgets.QSplitter, QtWidgets.QScrollArea,
QtWidgets.QGroupBox, QtWidgets.QFrame,
QtWidgets.QTabWidget, QtWidgets.QStackedWidget)) and hasattr(child, 'layout') and child.layout():
child.layout().setContentsMargins(0, 0, 0, 0)
child.layout().setSpacing(0)
# 特别处理列表、树和表格控件
elif isinstance(child, (QtWidgets.QListWidget, QtWidgets.QTreeWidget,
QtWidgets.QTableWidget, QtWidgets.QListView,
QtWidgets.QTreeView, QtWidgets.QTableView)):
child.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
child.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
def set_all_controls_minimum_size(ui_instance):
"""
设置UI实例中所有控件的最小尺寸为0确保分割器可以自由移动
Args:
ui_instance: UI实例对象必须包含controls和buttons字典
"""
# 设置所有按钮的最小尺寸
if hasattr(ui_instance, 'buttons'):
for button in ui_instance.buttons.values():
_set_control_minimum_size(button)
# 设置所有控件的最小尺寸
if hasattr(ui_instance, 'controls'):
for control in ui_instance.controls.values():
_set_control_minimum_size(control)
def _set_control_minimum_size(control):
"""
设置单个控件的最小尺寸为0
Args:
control: 要设置的控件
"""
control.setMinimumWidth(0)
# 对于按钮、标签等控件不应该设置最小高度为0否则会导致界面异常
if isinstance(control, (QtWidgets.QPushButton, QtWidgets.QToolButton,
QtWidgets.QLabel, QtWidgets.QLineEdit,
QtWidgets.QComboBox, QtWidgets.QCheckBox,
QtWidgets.QRadioButton)):
# 这些控件应该保持其默认高度只设置水平方向的策略为Expanding
policy = control.sizePolicy()
policy.setHorizontalPolicy(QtWidgets.QSizePolicy.Expanding)
control.setSizePolicy(policy)
else:
# 其他控件可以设置最小高度为0
control.setMinimumHeight(0)
control.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
# 特别处理列表、树和表格控件
if isinstance(control, (QtWidgets.QListWidget, QtWidgets.QTreeWidget,
QtWidgets.QTableWidget, QtWidgets.QListView,
QtWidgets.QTreeView, QtWidgets.QTableView)):
# 确保这些控件可以自由调整大小
control.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)

View File

@@ -3,7 +3,7 @@
""" """
Behaviour function module Behaviour function module
行为系统工具函数模块 - 提供行为系统UI所需的所有功能函数 行为系统功能模块 - 提供行为系统相关的功能函数
""" """
#===================================== IMPORT MODULES ===================================== #===================================== IMPORT MODULES =====================================
@@ -19,9 +19,30 @@ import importlib
import traceback import traceback
import sys import sys
import os import os
# 导入配置 # 导入配置
import config import config
TOOL_NAME = config.TOOL_NAME
TOOL_VERSION = config.TOOL_VERSION
TOOL_AUTHOR = config.TOOL_AUTHOR
TOOL_YEAR = config.TOOL_YEAR
TOOL_MOD_FILENAME = config.TOOL_MOD_FILENAME
TOOL_LANG = config.TOOL_LANG
TOOL_WSCL_NAME = config.TOOL_WSCL_NAME
TOOL_HELP_URL = config.TOOL_HELP_URL
TOOL_PATH = config.TOOL_PATH
SCRIPTS_PATH = config.SCRIPTS_PATH
TOOL_MAIN_SCRIPT = config.TOOL_MAIN_SCRIPT
UI_PATH = config.UI_PATH
STYLE_FILE = config.STYLE_FILE
ICONS_PATH = config.ICONS_PATH
TOOL_ICON = config.TOOL_ICON
ASSETS_PATH = config.ASSETS_PATH
DNA_FILE_PATH = config.DNA_FILE_PATH
DNA_IMG_PATH = config.DNA_IMG_PATH
TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON
TOOL_WIDTH = config.TOOL_WIDTH
TOOL_HEIGHT = config.TOOL_HEIGHT
# Localization
from scripts.ui import localization from scripts.ui import localization
LANG = localization.LANG LANG = localization.LANG
@@ -47,31 +68,27 @@ def control_selected(item):
return True return True
def update_control_selection(): def update_control_selection():
""" """更新控制器选择"""
更新控制选择 try:
""" print("更新控制器选择")
print("更新控制选择") except Exception as e:
# 这里实现更新控制选择的功能 print(f"更新控制选择时出错: {str(e)}")
return True
def update_control_value(value): def update_control_value(value):
""" """更新控制器值"""
更新控制值 try:
""" print(f"更新控制器值: {value}")
print(f"更新控制值: {value}") except Exception as e:
# 这里实现更新控制值的功能 print(f"更新控制器值时出错: {str(e)}")
return True
def switch_page(page):
"""切换页面"""
try:
print(f"切换到页面: {page}")
except Exception as e:
print(f"切换页面时出错: {str(e)}")
# 页码相关功能 # 页码相关功能
def switch_page(page):
"""
切换页面
"""
print(f"切换页面: {page}")
# 这里实现切换页面的功能
return True
# 范围相关功能
def decrease_range(): def decrease_range():
""" """
减小范围 减小范围
@@ -99,20 +116,81 @@ def blendshape_selected(item):
return True return True
def update_blendshape_selection(): def update_blendshape_selection():
""" """更新BlendShape选择"""
更新混合形状选择 try:
""" print("更新BlendShape选择")
print("更新混合形状选择") except Exception as e:
# 这里实现更新混合形状选择的功能 print(f"更新BlendShape选择时出错: {str(e)}")
return True
def update_blendshape_value(value): def update_blendshape_value(value):
""" """更新BlendShape值"""
更新混合形状值 try:
""" print(f"更新BlendShape值: {value}")
print(f"更新混合形状值: {value}") except Exception as e:
# 这里实现更新混合形状值的功能 print(f"更新BlendShape值时出错: {str(e)}")
return True
def flip_target():
"""翻转目标"""
try:
print("翻转目标")
except Exception as e:
print(f"翻转目标时出错: {str(e)}")
def mirror_target():
"""镜像目标"""
try:
print("镜像目标")
except Exception as e:
print(f"镜像目标时出错: {str(e)}")
def find_flip_target():
"""查找翻转目标"""
try:
print("查找翻转目标")
except Exception as e:
print(f"查找翻转目标时出错: {str(e)}")
def add_blendshape():
"""添加BlendShape"""
try:
print("添加BlendShape")
except Exception as e:
print(f"添加BlendShape时出错: {str(e)}")
def delete_blendshape():
"""删除BlendShape"""
try:
print("删除BlendShape")
except Exception as e:
print(f"删除BlendShape时出错: {str(e)}")
def batch_blendshape():
"""批量处理BlendShape"""
try:
print("批量处理BlendShape")
except Exception as e:
print(f"批量处理BlendShape时出错: {str(e)}")
def rebuild_select():
"""重建选择"""
try:
print("重建选择")
except Exception as e:
print(f"重建选择时出错: {str(e)}")
def reposition():
"""重新定位"""
try:
print("重新定位")
except Exception as e:
print(f"重新定位时出错: {str(e)}")
def blend_select():
"""混合选择"""
try:
print("混合选择")
except Exception as e:
print(f"混合选择时出错: {str(e)}")
# BlendShape操作相关功能 # BlendShape操作相关功能
def reset_blendshape(): def reset_blendshape():
@@ -131,14 +209,6 @@ def mirror_blendshape():
# 这里实现镜像混合形状的功能 # 这里实现镜像混合形状的功能
return True return True
def add_blendshape():
"""
添加混合形状
"""
print("添加混合形状")
# 这里实现添加混合形状的功能
return True
def remove_blendshape(): def remove_blendshape():
""" """
移除混合形状 移除混合形状
@@ -181,21 +251,61 @@ def combine_blendshape():
# 标签页相关功能 # 标签页相关功能
def switch_tab(tab): def switch_tab(tab):
""" """切换标签页"""
切换标签页 try:
""" print(f"切换到标签页: {tab}")
print(f"切换标签页: {tab}") except Exception as e:
# 这里实现切换标签页的功能 print(f"切换标签页时出错: {str(e)}")
return True
# 底部相关功能 # 底部相关功能
def update_main_value(value): def update_main_value(value):
""" """更新主滑块值"""
更新主值 try:
""" print(f"更新主滑块值: {value}")
print(f"更新主值: {value}") except Exception as e:
# 这里实现更新主值的功能 print(f"更新主滑块值时出错: {str(e)}")
return True
def reset_default_expression():
"""重置默认表情"""
try:
print("重置默认表情")
except Exception as e:
print(f"重置默认表情时出错: {str(e)}")
def find_select_expression():
"""查找选择表情"""
try:
print("查找选择表情")
except Exception as e:
print(f"查找选择表情时出错: {str(e)}")
def write_current_expressions():
"""写入当前表情"""
try:
print("写入当前表情")
except Exception as e:
print(f"写入当前表情时出错: {str(e)}")
def controller_find():
"""控制器查找"""
try:
print("控制器查找")
except Exception as e:
print(f"控制器查找时出错: {str(e)}")
def select_associated_joint():
"""选择关联关节"""
try:
print("选择关联关节")
except Exception as e:
print(f"选择关联关节时出错: {str(e)}")
def write_find_mirror():
"""写入查找镜像"""
try:
print("写入查找镜像")
except Exception as e:
print(f"写入查找镜像时出错: {str(e)}")
def exchange_from_expression(): def exchange_from_expression():
""" """
@@ -265,6 +375,239 @@ def import_map_expression():
print(f"导入表情映射时出错: {e}") print(f"导入表情映射时出错: {e}")
return True return True
# 勾选框处理函数
def toggle_raw_slider_all(state):
"""
切换左侧滑块的All选项
Args:
state (int): 勾选框状态
"""
try:
print(f"切换左侧滑块All选项: {state}")
# 根据勾选状态执行相应操作
if state == 2: # 选中状态
print("应用到所有控制器")
# TODO: 实现应用到所有控制器的逻辑
else: # 未选中状态
print("仅应用到选中的控制器")
# TODO: 实现仅应用到选中控制器的逻辑
except Exception as e:
print(f"切换左侧滑块All选项时出错: {str(e)}")
def toggle_bs_slider_all(state):
"""
切换右侧BS滑块的All选项
Args:
state (int): 勾选框状态
"""
try:
print(f"切换右侧BS滑块All选项: {state}")
# 根据勾选状态执行相应操作
if state == 2: # 选中状态
print("应用到所有BlendShape")
# TODO: 实现应用到所有BlendShape的逻辑
else: # 未选中状态
print("仅应用到选中的BlendShape")
# TODO: 实现仅应用到选中BlendShape的逻辑
except Exception as e:
print(f"切换右侧BS滑块All选项时出错: {str(e)}")
def toggle_bottom_slider_all(state):
"""
切换底部主滑块的All选项
Args:
state (int): 勾选框状态
"""
try:
print(f"切换底部主滑块All选项: {state}")
# 根据勾选状态执行相应操作
if state == 2: # 选中状态
print("应用到所有表情")
# TODO: 实现应用到所有表情的逻辑
else: # 未选中状态
print("仅应用到当前表情")
# TODO: 实现仅应用到当前表情的逻辑
except Exception as e:
print(f"切换底部主滑块All选项时出错: {str(e)}")
# 滑块更新函数
def update_raw_slider_value(value):
"""
更新左侧滑块数值显示
Args:
value (int): 滑块值
Returns:
float: 标准化后的值(0-1范围)
"""
# 将滑块值转换为0-1范围的浮点数并显示3位小数
normalized_value = value / 100.0
# 获取UI实例并更新显示
from scripts.ui.behaviour import BehaviourUI
ui_instance = BehaviourUI.get_instance()
if ui_instance and hasattr(ui_instance, 'controls'):
ui_instance.controls["raw_slider_value"].setText(f"{normalized_value:.3f}")
# 更新控制值
update_control_value(value)
return normalized_value
def update_bs_slider_value(value):
"""
更新右侧BS滑块数值显示
Args:
value (int): 滑块值
Returns:
float: 标准化后的值(0-1范围)
"""
# 将滑块值转换为0-1范围的浮点数并显示3位小数
normalized_value = value / 100.0
# 获取UI实例并更新显示
from scripts.ui.behaviour import BehaviourUI
ui_instance = BehaviourUI.get_instance()
if ui_instance and hasattr(ui_instance, 'controls'):
ui_instance.controls["bs_slider_value"].setText(f"{normalized_value:.3f}")
# 更新BlendShape值
update_blendshape_value(value)
return normalized_value
def update_bottom_slider_value(value):
"""
更新底部主滑块数值显示
Args:
value (int): 滑块值
Returns:
float: 标准化后的值(0-1范围)
"""
# 将滑块值转换为0-1范围的浮点数并显示3位小数
normalized_value = value / 100.0
# 获取UI实例并更新显示
from scripts.ui.behaviour import BehaviourUI
ui_instance = BehaviourUI.get_instance()
if ui_instance and hasattr(ui_instance, 'controls'):
ui_instance.controls["bottom_slider_value"].setText(f"{normalized_value:.3f}")
# 更新主值
update_main_value(value)
return normalized_value
# UI事件处理相关的功能函数
def on_selection_changed(ui_instance):
"""
处理Maya选择变化事件
Args:
ui_instance: UI实例对象
"""
print("选择变化事件触发")
# 这里将来会实现选择变化的处理逻辑
# 目前仅作为占位函数
def switch_tab(ui_instance, index):
"""
切换标签页
Args:
ui_instance: UI实例对象
index (int): 标签页索引
"""
if hasattr(ui_instance, 'controls') and 'bottom_tab_widget' in ui_instance.controls:
ui_instance.controls["bottom_tab_widget"].setCurrentIndex(index)
print(f"切换到标签页: {index}")
def adjust_range(delta):
"""
调整滑块范围
Args:
delta (float): 范围调整值
"""
if delta > 0:
increase_range()
else:
decrease_range()
def reset_splitter_sizes(ui_instance):
"""
重置分割器大小,确保左右分栏均等
Args:
ui_instance: UI实例对象
"""
from scripts.ui import ui_utils
ui_utils.reset_splitter_sizes(ui_instance)
def on_raw_slider_changed(value):
"""
处理Raw滑块值变化
Args:
value (int): 滑块值
"""
# 更新显示值
print(f"Raw滑块值变化: {value}")
# 调用实际的更新函数
update_raw_slider_value(value)
def on_bs_slider_changed(value):
"""
处理BlendShape滑块值变化
Args:
value (int): 滑块值
"""
# 更新显示值
print(f"BlendShape滑块值变化: {value}")
# 调用实际的更新函数
update_bs_slider_value(value)
def on_bottom_slider_changed(value):
"""
处理底部主滑块值变化
Args:
value (int): 滑块值
"""
# 更新显示值
print(f"底部主滑块值变化: {value}")
# 调用实际的更新函数
update_bottom_slider_value(value)
def on_control_selection_changed():
"""处理控制列表选择变化"""
print("控制列表选择变化")
update_control_selection()
def on_blendshape_selection_changed():
"""处理BlendShape列表选择变化"""
print("BlendShape列表选择变化")
update_blendshape_selection()
def adjust_bs_range(delta):
"""
调整BlendShape滑块范围
Args:
delta (float): 范围调整值
"""
print(f"调整BlendShape滑块范围: {delta}")
adjust_range(delta)
# 保留原来的函数作为兼容性考虑 # 保留原来的函数作为兼容性考虑
def behaviour_temp_utils_function(): def behaviour_temp_utils_function():
""" """
@@ -273,3 +616,13 @@ def behaviour_temp_utils_function():
""" """
print("Behaviour module temporary function called") print("Behaviour module temporary function called")
return True return True
def reposition_joints():
"""
重新定位关节
根据当前选择的模型重新定位关节位置
"""
print("重新定位关节...")
# 这里将来会实现关节重新定位的功能
# 目前仅作为占位函数
cmds.warning("关节重新定位功能尚未实现")

View File

@@ -3,7 +3,7 @@
""" """
Definition function module Definition function module
定义系统工具函数模块 - 提供定义系统UI所需的所有功能函数 定义系统功能模块 - 提供定义系统相关的功能函数
""" """
#===================================== IMPORT MODULES ===================================== #===================================== IMPORT MODULES =====================================
@@ -19,12 +19,32 @@ import importlib
import traceback import traceback
import sys import sys
import os import os
# 导入配置 # 导入配置
import config import config
TOOL_NAME = config.TOOL_NAME
TOOL_VERSION = config.TOOL_VERSION
TOOL_AUTHOR = config.TOOL_AUTHOR
TOOL_YEAR = config.TOOL_YEAR
TOOL_MOD_FILENAME = config.TOOL_MOD_FILENAME
TOOL_LANG = config.TOOL_LANG
TOOL_WSCL_NAME = config.TOOL_WSCL_NAME
TOOL_HELP_URL = config.TOOL_HELP_URL
TOOL_PATH = config.TOOL_PATH
SCRIPTS_PATH = config.SCRIPTS_PATH
TOOL_MAIN_SCRIPT = config.TOOL_MAIN_SCRIPT
UI_PATH = config.UI_PATH
STYLE_FILE = config.STYLE_FILE
ICONS_PATH = config.ICONS_PATH
TOOL_ICON = config.TOOL_ICON
ASSETS_PATH = config.ASSETS_PATH
DNA_FILE_PATH = config.DNA_FILE_PATH
DNA_IMG_PATH = config.DNA_IMG_PATH
TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON
TOOL_WIDTH = config.TOOL_WIDTH
TOOL_HEIGHT = config.TOOL_HEIGHT
# Localization
from scripts.ui import localization from scripts.ui import localization
LANG = localization.LANG LANG = localization.LANG
#========================================== FUNCTIONS ======================================== #========================================== FUNCTIONS ========================================
# 左侧面板功能 # 左侧面板功能

View File

@@ -3,7 +3,7 @@
""" """
Geometry function module Geometry function module
几何模型工具函数模块 - 提供几何模型UI所需的所有功能函数 几何模型功能模块 - 提供几何模型相关的功能函数
""" """
#===================================== IMPORT MODULES ===================================== #===================================== IMPORT MODULES =====================================
@@ -19,12 +19,32 @@ import importlib
import traceback import traceback
import sys import sys
import os import os
# 导入配置 # 导入配置
import config import config
TOOL_NAME = config.TOOL_NAME
TOOL_VERSION = config.TOOL_VERSION
TOOL_AUTHOR = config.TOOL_AUTHOR
TOOL_YEAR = config.TOOL_YEAR
TOOL_MOD_FILENAME = config.TOOL_MOD_FILENAME
TOOL_LANG = config.TOOL_LANG
TOOL_WSCL_NAME = config.TOOL_WSCL_NAME
TOOL_HELP_URL = config.TOOL_HELP_URL
TOOL_PATH = config.TOOL_PATH
SCRIPTS_PATH = config.SCRIPTS_PATH
TOOL_MAIN_SCRIPT = config.TOOL_MAIN_SCRIPT
UI_PATH = config.UI_PATH
STYLE_FILE = config.STYLE_FILE
ICONS_PATH = config.ICONS_PATH
TOOL_ICON = config.TOOL_ICON
ASSETS_PATH = config.ASSETS_PATH
DNA_FILE_PATH = config.DNA_FILE_PATH
DNA_IMG_PATH = config.DNA_IMG_PATH
TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON
TOOL_WIDTH = config.TOOL_WIDTH
TOOL_HEIGHT = config.TOOL_HEIGHT
# Localization
from scripts.ui import localization from scripts.ui import localization
LANG = localization.LANG LANG = localization.LANG
#========================================== FUNCTIONS ======================================== #========================================== FUNCTIONS ========================================
# 左侧面板功能 # 左侧面板功能

View File

@@ -9,12 +9,42 @@ Rigging function module
#===================================== IMPORT MODULES ===================================== #===================================== IMPORT MODULES =====================================
import maya.cmds as cmds import maya.cmds as cmds
import pymel.core as pm import pymel.core as pm
import maya.mel as mel
from maya import OpenMayaUI as omui
from Qt import QtWidgets, QtCore, QtGui
from Qt.QtCompat import wrapInstance
import webbrowser
import subprocess
import importlib import importlib
import traceback
import sys import sys
import os import os
import random # 导入配置
from PySide2 import QtWidgets, QtCore, QtGui import config
from functools import partial TOOL_NAME = config.TOOL_NAME
TOOL_VERSION = config.TOOL_VERSION
TOOL_AUTHOR = config.TOOL_AUTHOR
TOOL_YEAR = config.TOOL_YEAR
TOOL_MOD_FILENAME = config.TOOL_MOD_FILENAME
TOOL_LANG = config.TOOL_LANG
TOOL_WSCL_NAME = config.TOOL_WSCL_NAME
TOOL_HELP_URL = config.TOOL_HELP_URL
TOOL_PATH = config.TOOL_PATH
SCRIPTS_PATH = config.SCRIPTS_PATH
TOOL_MAIN_SCRIPT = config.TOOL_MAIN_SCRIPT
UI_PATH = config.UI_PATH
STYLE_FILE = config.STYLE_FILE
ICONS_PATH = config.ICONS_PATH
TOOL_ICON = config.TOOL_ICON
ASSETS_PATH = config.ASSETS_PATH
DNA_FILE_PATH = config.DNA_FILE_PATH
DNA_IMG_PATH = config.DNA_IMG_PATH
TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON
TOOL_WIDTH = config.TOOL_WIDTH
TOOL_HEIGHT = config.TOOL_HEIGHT
# Localization
from scripts.ui import localization
LANG = localization.LANG
#========================================== GLOBALS ======================================== #========================================== GLOBALS ========================================
# 存储当前选中的关节和控制器信息 # 存储当前选中的关节和控制器信息
@@ -754,3 +784,112 @@ def on_selection_changed():
# 可以在这里添加代码来更新UI # 可以在这里添加代码来更新UI
print(f"选中控制器: {selected_controller}") print(f"选中控制器: {selected_controller}")
break break
def browse_file(ui_instance, title, input_widget, file_filter=None):
"""浏览文件或目录
Args:
ui_instance: UI实例用于获取主窗口
title (str): 对话框标题
input_widget (QLineEdit): 用于显示路径的输入框控件
file_filter (str, optional): 文件过滤器如果为None则浏览目录否则浏览文件
"""
from Qt import QtWidgets
import os
current_path = input_widget.text() or os.path.expanduser("~")
if file_filter:
# 浏览文件
if file_filter == "dna":
file_filter = "DNA文件 (*.dna);;所有文件 (*.*)"
elif file_filter == "json":
file_filter = "JSON文件 (*.json);;所有文件 (*.*)"
else:
file_filter = "所有文件 (*.*)"
file_path, _ = QtWidgets.QFileDialog.getOpenFileName(
ui_instance.main_widget, title, current_path, file_filter
)
if file_path:
input_widget.setText(file_path)
else:
# 浏览目录
dir_path = QtWidgets.QFileDialog.getExistingDirectory(
ui_instance.main_widget, title, current_path
)
if dir_path:
input_widget.setText(dir_path)
def on_selection_changed(ui_instance=None):
"""处理Maya选择变化事件
Args:
ui_instance: UI实例可选
"""
import maya.cmds as cmds
# 获取当前选择
selection = cmds.ls(selection=True)
if not selection:
return
# 更新UI显示
print(f"当前选择: {selection}")
# 这里可以添加更多的选择处理逻辑
# 如果提供了UI实例可以更新UI
if ui_instance:
# 更新UI显示
pass
def export_presets(*args):
"""导出预设"""
print("导出预设")
# 实现导出预设的功能
def import_presets(*args):
"""导入预设"""
print("导入预设")
# 实现导入预设的功能
# ======================================= 占位函数 =======================================
# 以下是未实现功能的占位函数,用于连接未创建的按钮
def add_joint(*args):
"""添加关节(占位函数)"""
print("添加关节功能尚未实现")
def remove_joint(*args):
"""删除关节(占位函数)"""
print("删除关节功能尚未实现")
def duplicate_joint(*args):
"""复制关节(占位函数)"""
print("复制关节功能尚未实现")
def add_controller(*args):
"""添加控制器(占位函数)"""
print("添加控制器功能尚未实现")
def remove_controller(*args):
"""删除控制器(占位函数)"""
print("删除控制器功能尚未实现")
def duplicate_controller(*args):
"""复制控制器(占位函数)"""
print("复制控制器功能尚未实现")
def import_dna(*args):
"""导入DNA占位函数"""
print("导入DNA功能尚未实现")
def export_dna(*args):
"""导出DNA占位函数"""
print("导出DNA功能尚未实现")
def calibrate_dna(*args):
"""校准DNA占位函数"""
print("校准DNA功能尚未实现")

View File

@@ -14,9 +14,30 @@ import importlib
import traceback import traceback
import sys import sys
import os import os
# 导入配置 # 导入配置
import config import config
TOOL_NAME = config.TOOL_NAME
TOOL_VERSION = config.TOOL_VERSION
TOOL_AUTHOR = config.TOOL_AUTHOR
TOOL_YEAR = config.TOOL_YEAR
TOOL_MOD_FILENAME = config.TOOL_MOD_FILENAME
TOOL_LANG = config.TOOL_LANG
TOOL_WSCL_NAME = config.TOOL_WSCL_NAME
TOOL_HELP_URL = config.TOOL_HELP_URL
TOOL_PATH = config.TOOL_PATH
SCRIPTS_PATH = config.SCRIPTS_PATH
TOOL_MAIN_SCRIPT = config.TOOL_MAIN_SCRIPT
UI_PATH = config.UI_PATH
STYLE_FILE = config.STYLE_FILE
ICONS_PATH = config.ICONS_PATH
TOOL_ICON = config.TOOL_ICON
ASSETS_PATH = config.ASSETS_PATH
DNA_FILE_PATH = config.DNA_FILE_PATH
DNA_IMG_PATH = config.DNA_IMG_PATH
TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON
TOOL_WIDTH = config.TOOL_WIDTH
TOOL_HEIGHT = config.TOOL_HEIGHT
# Localization
from scripts.ui import localization from scripts.ui import localization
LANG = localization.LANG LANG = localization.LANG
@@ -151,7 +172,7 @@ def show_help():
# 打开帮助文档或显示帮助对话框 # 打开帮助文档或显示帮助对话框
help_dialog = QtWidgets.QMessageBox() help_dialog = QtWidgets.QMessageBox()
help_dialog.setWindowTitle(LANG.get("help_title", "帮助")) help_dialog.setWindowTitle(LANG.get("help_title", "帮助"))
help_dialog.setText(LANG.get("help_message", "MetaFusion是一个用于自定义MetaHuman的Maya插件。\n\n详细信息请参考文档。")) help_dialog.setText(LANG.get("help_message", "该插件是一个用于自定义MetaHuman的Maya插件。\n\n详细信息请参考文档。"))
help_dialog.setStandardButtons(QtWidgets.QMessageBox.Ok) help_dialog.setStandardButtons(QtWidgets.QMessageBox.Ok)
help_dialog.exec_() help_dialog.exec_()
except Exception as e: except Exception as e: