Files
MetaFusion/scripts/ui/behaviour.py
2025-05-08 00:39:41 +08:00

670 lines
35 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

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

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Behaviour UI Module for Plugin
行为系统UI模块 - 负责显示角色行为编辑界面和基础操作
基本功能:
- Blendshape自动加载刷新筛选
- 次级Blendshape自动加载刷新筛选
- Blendshape批量导出和导入
- Blendshape范围编辑
- Blendshape镜像
- Blendshape查找翻转目标
- 表情控制器还原默认表情
- 查找选择表情
- 控制面板查找
"""
#========================================= IMPORT =========================================
from Qt import QtWidgets, QtCore, QtGui
from Qt.QtCompat import wrapInstance
from maya import OpenMayaUI as omui
import maya.cmds as cmds
import maya.mel as mel
import maya.utils as utils
import webbrowser
import subprocess
import importlib
import traceback
import locale
import sys
import os
from scripts.ui import ui_utils
from scripts.utils import utils_behaviour
#========================================== 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
#========================================= LOCATION =======================================
from scripts.ui import localization
LANG = localization.LANG
get_text = localization.get_text
class BehaviourUI(ui_utils.BaseUI):
"""
行为系统UI类 - 负责显示角色行为编辑界面和基础操作
继承自BaseUI类实现行为系统相关的UI功能
"""
# 类变量,存储单例实例
_instance = None
@classmethod
def get_instance(cls):
"""
获取BehaviourUI的单例实例
Returns:
BehaviourUI: 单例实例如果不存在则返回None
"""
return cls._instance
def __init__(self, parent=None):
"""
初始化行为系统UI
"""
super(BehaviourUI, self).__init__() # 不传递parent参数给BaseUI
# 设置单例实例
BehaviourUI._instance = self
# 创建主控件
self.main_widget = QtWidgets.QWidget(parent)
self.main_widget.setObjectName("behaviourMainWidget")
# 初始化控件、布局和按钮字典
self.controls = {}
self.layouts = {}
self.buttons = {}
self.splitters = {}
# 创建UI组件
self.create_widgets()
self.create_layouts()
self.create_connections()
# 设置全局样式,确保所有控件都没有最小宽度限制
self.main_widget.setStyleSheet("""
QWidget { min-width: 0px; }
QPushButton { min-width: 0px; }
QLabel { min-width: 0px; }
QListWidget { min-width: 0px; }
QGroupBox { min-width: 0px; }
QSlider { min-width: 0px; }
""")
def create_widgets(self):
"""
创建行为系统UI控件
包括标题标签、搜索框、控制列表、滑块等
"""
# 标题标签
self.controls["title_label"] = QtWidgets.QLabel(get_text("behaviour_title", "行为系统"))
self.controls["title_label"].setObjectName("behaviourTitleLabel")
self.controls["title_label"].setAlignment(QtCore.Qt.AlignCenter)
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(True) # 允许子部件折叠 - 关键设置
self.splitters["main_splitter"].setOpaqueResize(True) # 实时显示调整效果,更丝滑
# 左侧面板 - Raw Control
self.controls["left_panel"] = QtWidgets.QWidget()
self.controls["left_panel"].setObjectName("behaviourLeftPanel")
# 右侧面板 - BlendShapes
self.controls["right_panel"] = QtWidgets.QWidget()
self.controls["right_panel"].setObjectName("behaviourRightPanel")
# 搜索框
self.controls["search_input"] = QtWidgets.QLineEdit()
self.controls["search_input"].setObjectName("searchInput")
self.controls["search_input"].setPlaceholderText(get_text("search", "搜索..."))
self.controls["search_input"].setFixedHeight(25)
# 控制列表
self.controls["control_list"] = QtWidgets.QListWidget()
self.controls["control_list"].setObjectName("controlList")
# 底部滑块
self.controls["raw_slider"] = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.controls["raw_slider"].setObjectName("rawSlider")
self.controls["raw_slider"].setMinimum(0)
self.controls["raw_slider"].setMaximum(100)
self.controls["raw_slider"].setValue(0)
self.controls["raw_slider"].setFixedHeight(20)
# 添加数值显示标签
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)
# 添加"All"勾选框和标签
self.controls["raw_slider_all_check"] = QtWidgets.QCheckBox("All")
self.controls["raw_slider_all_check"].setObjectName("rawSliderAllCheck")
# 页码按钮
self.buttons["page_all"] = QtWidgets.QPushButton("All")
self.buttons["page_all"].setObjectName("pageAllButton")
self.buttons["page_all"].setCheckable(True)
self.buttons["page_all"].setChecked(True)
self.buttons["page_2"] = QtWidgets.QPushButton("2")
self.buttons["page_2"].setObjectName("page2Button")
self.buttons["page_2"].setCheckable(True)
self.buttons["page_3"] = QtWidgets.QPushButton("3")
self.buttons["page_3"].setObjectName("page3Button")
self.buttons["page_3"].setCheckable(True)
self.buttons["page_4"] = QtWidgets.QPushButton("4")
self.buttons["page_4"].setObjectName("page4Button")
self.buttons["page_4"].setCheckable(True)
self.buttons["page_5"] = QtWidgets.QPushButton("5")
self.buttons["page_5"].setObjectName("page5Button")
self.buttons["page_5"].setCheckable(True)
self.buttons["page_6"] = QtWidgets.QPushButton("6")
self.buttons["page_6"].setObjectName("page6Button")
self.buttons["page_6"].setCheckable(True)
# 左下角Range按钮
self.buttons["range_minus"] = QtWidgets.QPushButton(get_text(" Range - ", " 范围 - "))
self.buttons["range_minus"].setObjectName("rangeMinusButton")
self.buttons["range_minus"].setIcon(ui_utils.load_icon("behaviour.png"))
self.buttons["range_minus"].setMinimumWidth(0)
self.buttons["range_plus"] = QtWidgets.QPushButton(get_text(" Range + ", " 范围 + "))
self.buttons["range_plus"].setObjectName("rangePlusButton")
self.buttons["range_plus"].setIcon(ui_utils.load_icon("behaviour.png"))
self.buttons["range_plus"].setMinimumWidth(0)
# 左侧面板控件 - Raw Control
self.controls["raw_control_group"] = QtWidgets.QGroupBox(get_text("Raw Control", "原始控制"))
self.controls["raw_control_group"].setObjectName("rawControlGroup")
self.controls["raw_control_group"].setTitle(get_text("Raw Control", "原始控制"))
self.controls["raw_control_group"].setFixedHeight(25)
# 右侧面板控件 - Related BlendShapes
self.controls["blendshapes_group"] = QtWidgets.QGroupBox(get_text("Related BlendShapes", "相关BlendShapes"))
self.controls["blendshapes_group"].setObjectName("blendshapesGroup")
self.controls["blendshapes_group"].setTitle(get_text("Related BlendShapes", "相关BlendShapes"))
self.controls["blendshapes_group"].setFixedHeight(25)
# BlendShapes列表
self.controls["blendshapes_list"] = QtWidgets.QListWidget()
self.controls["blendshapes_list"].setObjectName("blendshapesList")
self.controls["blendshapes_list"].setMinimumHeight(100)
# 底部BS滑块
self.controls["bs_slider"] = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.controls["bs_slider"].setObjectName("bsSlider")
self.controls["bs_slider"].setMinimum(0)
self.controls["bs_slider"].setMaximum(100)
self.controls["bs_slider"].setValue(0)
# 添加BS数值显示标签
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)
# 添加BS "All"勾选框和标签
self.controls["bs_slider_all_check"] = QtWidgets.QCheckBox("All")
self.controls["bs_slider_all_check"].setObjectName("bsSliderAllCheck")
# 右下角BS Range按钮
self.buttons["bs_range_minus"] = QtWidgets.QPushButton(get_text(" Range - ", " 范围 - "))
self.buttons["bs_range_minus"].setObjectName("bsRangeMinusButton")
self.buttons["bs_range_minus"].setIcon(ui_utils.load_icon("behaviour.png"))
self.buttons["bs_range_minus"].setMinimumWidth(0)
self.buttons["bs_range_plus"] = QtWidgets.QPushButton(get_text(" Range + ", " 范围 + "))
self.buttons["bs_range_plus"].setObjectName("bsRangePlusButton")
self.buttons["bs_range_plus"].setIcon(ui_utils.load_icon("behaviour.png"))
self.buttons["bs_range_plus"].setMinimumWidth(0)
# BlendShape操作按钮
self.buttons["flip_target"] = QtWidgets.QPushButton(get_text("Flip Target", "翻转目标"))
self.buttons["flip_target"].setObjectName("flipTargetButton")
self.buttons["flip_target"].setIcon(ui_utils.load_icon("mirrorL.png"))
self.buttons["mirror_target"] = QtWidgets.QPushButton(get_text("Mirror Target", "镜像目标"))
self.buttons["mirror_target"].setObjectName("mirrorTargetButton")
self.buttons["mirror_target"].setIcon(ui_utils.load_icon("mirror.png"))
self.buttons["find_flip_target"] = QtWidgets.QPushButton(get_text("Find Flip Target", "查找翻转目标"))
self.buttons["find_flip_target"].setObjectName("findFlipTargetButton")
self.buttons["find_flip_target"].setIcon(ui_utils.load_icon("mirrorR.png"))
self.buttons["add_blendshape"] = QtWidgets.QPushButton(get_text("Add BlendShape", "添加BlendShape"))
self.buttons["add_blendshape"].setObjectName("addBlendshapeButton")
self.buttons["add_blendshape"].setIcon(ui_utils.load_icon("blendShape.png"))
self.buttons["delete_blendshape"] = QtWidgets.QPushButton(get_text("Delete BlendShape", "删除BlendShape"))
self.buttons["delete_blendshape"].setObjectName("deleteBlendshapeButton")
self.buttons["delete_blendshape"].setIcon(ui_utils.load_icon("blendShape.png"))
self.buttons["batch_blendshape"] = QtWidgets.QPushButton(get_text("Batch BlendShape", "批量BlendShape"))
self.buttons["batch_blendshape"].setObjectName("batchBlendshapeButton")
self.buttons["batch_blendshape"].setIcon(ui_utils.load_icon("blendShape.png"))
self.buttons["bs_range_minus"] = QtWidgets.QPushButton(get_text(" Range - ", " 范围 - "))
self.buttons["bs_range_minus"].setObjectName("bsRangeMinusButton")
self.buttons["bs_range_minus"].setIcon(ui_utils.load_icon("behaviour.png"))
self.buttons["bs_range_minus"].setMinimumWidth(0)
self.buttons["bs_range_plus"] = QtWidgets.QPushButton(get_text(" Range + ", " 范围 + "))
self.buttons["bs_range_plus"].setObjectName("bsRangePlusButton")
self.buttons["bs_range_plus"].setIcon(ui_utils.load_icon("behaviour.png"))
self.buttons["bs_range_plus"].setMinimumWidth(0)
self.buttons["rebuild_select"] = QtWidgets.QPushButton(get_text("Rebuild Select", "重建选择"))
self.buttons["rebuild_select"].setObjectName("rebuildSelectButton")
self.buttons["rebuild_select"].setIcon(ui_utils.load_icon("loading.png"))
self.buttons["reposition_joints"] = QtWidgets.QPushButton(get_text("Reposition Joints", "重新定位关节"))
self.buttons["reposition_joints"].setObjectName("repositionJointsButton")
self.buttons["reposition_joints"].setIcon(ui_utils.load_icon("loading.png"))
self.buttons["blend_select"] = QtWidgets.QPushButton(get_text("Blend Select", "混合选择"))
self.buttons["blend_select"].setObjectName("blendSelectButton")
self.buttons["blend_select"].setIcon(ui_utils.load_icon("loading.png"))
# 创建底部主滑块
self.controls["bottom_main_slider"] = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.controls["bottom_main_slider"].setObjectName("bottomMainSlider")
self.controls["bottom_main_slider"].setMinimum(0)
self.controls["bottom_main_slider"].setMaximum(100)
self.controls["bottom_main_slider"].setValue(0)
# 添加底部主滑块数值显示标签 - 修改命名以匹配功能代码
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)
# 添加底部主滑块"All"勾选框 - 修改命名以匹配功能代码
self.controls["bottom_slider_all_check"] = QtWidgets.QCheckBox("All")
self.controls["bottom_slider_all_check"].setObjectName("bottomSliderAllCheck")
# 底部标签页按钮
self.buttons["tab_psd"] = QtWidgets.QPushButton(get_text("PSD", "PSD"))
self.buttons["tab_psd"].setIcon(ui_utils.load_icon("psd.png"))
self.buttons["tab_psd"].setObjectName("tabPsdButton")
self.buttons["tab_psd"].setCheckable(True)
self.buttons["tab_psd"].setChecked(True)
self.buttons["tab_bse"] = QtWidgets.QPushButton(get_text("BSE", "BSE"))
self.buttons["tab_bse"].setIcon(ui_utils.load_icon("blendShape.png"))
self.buttons["tab_bse"].setObjectName("tabBseButton")
self.buttons["tab_bse"].setCheckable(True)
self.buttons["tab_bse"].setChecked(True)
self.buttons["tab_key"] = QtWidgets.QPushButton(get_text("KEY", "KEY"))
self.buttons["tab_key"].setIcon(ui_utils.load_icon("setKeyOnAnim.png"))
self.buttons["tab_key"].setObjectName("tabKeyButton")
self.buttons["tab_key"].setCheckable(True)
self.buttons["tab_key"].setChecked(True)
self.buttons["tab_mir"] = QtWidgets.QPushButton(get_text("MIR", "MIR"))
self.buttons["tab_mir"].setIcon(ui_utils.load_icon("mirrorR.png"))
self.buttons["tab_mir"].setObjectName("tabMirButton")
self.buttons["tab_mir"].setCheckable(True)
self.buttons["tab_mir"].setChecked(True)
self.buttons["tab_ark"] = QtWidgets.QPushButton(get_text("ARK", "ARK"))
self.buttons["tab_ark"].setIcon(ui_utils.load_icon("ARKit52.png"))
self.buttons["tab_ark"].setObjectName("tabArkButton")
self.buttons["tab_ark"].setCheckable(True)
self.buttons["tab_ark"].setChecked(True)
self.buttons["tab_ctr"] = QtWidgets.QPushButton(get_text("CTR", "CTR"))
self.buttons["tab_ctr"].setIcon(ui_utils.load_icon("ctrl_hide.png"))
self.buttons["tab_ctr"].setObjectName("tabCtrButton")
self.buttons["tab_ctr"].setCheckable(True)
self.buttons["tab_ctr"].setChecked(True)
# 底部功能按钮
self.buttons["reset_default_expression"] = QtWidgets.QPushButton(get_text("Reset Default", "重置默认"))
self.buttons["reset_default_expression"].setIcon(ui_utils.load_icon("reset.png"))
self.buttons["reset_default_expression"].setObjectName("resetDefaultExpressionButton")
self.buttons["find_select_expression"] = QtWidgets.QPushButton(get_text("Find Select", "查找选择"))
self.buttons["find_select_expression"].setIcon(ui_utils.load_icon("expressions_current.png"))
self.buttons["find_select_expression"].setObjectName("findSelectExpressionButton")
self.buttons["write_current_expressions"] = QtWidgets.QPushButton(get_text("Write Current", "写入当前"))
self.buttons["write_current_expressions"].setIcon(ui_utils.load_icon("expression.png"))
self.buttons["write_current_expressions"].setObjectName("writeCurrentExpressionsButton")
self.buttons["controller_find"] = QtWidgets.QPushButton(get_text("Controller Find", "控制器查找"))
self.buttons["controller_find"].setIcon(ui_utils.load_icon("controller.png"))
self.buttons["controller_find"].setObjectName("controllerFindButton")
self.buttons["select_associated_joint"] = QtWidgets.QPushButton(get_text("Select Joint", "选择关节"))
self.buttons["select_associated_joint"].setIcon(ui_utils.load_icon("out_joint.png"))
self.buttons["select_associated_joint"].setObjectName("selectAssociatedJointButton")
self.buttons["write_find_mirror"] = QtWidgets.QPushButton(get_text("Find Mirror", "查找镜像"))
self.buttons["write_find_mirror"].setIcon(ui_utils.load_icon("mirror.png"))
self.buttons["write_find_mirror"].setObjectName("writeFindMirrorButton")
# 设置所有按钮的最小宽度为0
for button_name in ["flip_target", "mirror_target", "find_flip_target",
"add_blendshape", "delete_blendshape", "batch_blendshape",
"rebuild_select", "reposition_joints", "blend_select",
"range_minus", "range_plus", "bs_range_minus", "bs_range_plus",
"page_all", "page_2", "page_3", "page_4", "page_5", "page_6"]:
if button_name in self.buttons:
self.buttons[button_name].setMinimumWidth(0)
#========================================= LAYOUT =======================================
def create_layouts(self):
"""
创建行为系统UI布局
包括主布局、左右面板布局、滑块布局等
"""
# 创建主布局
self.layouts["main_layout"] = QtWidgets.QVBoxLayout(self.main_widget)
self.layouts["main_layout"].setContentsMargins(2, 2, 2, 2) # 减小边距
self.layouts["main_layout"].setSpacing(2) # 减小间距
# 添加标题标签到主布局
self.layouts["main_layout"].addWidget(self.controls["title_label"])
# 添加主分割器到主布局 - 设置伸缩因子为1使其占据所有可用空间
self.layouts["main_layout"].addWidget(self.splitters["main_splitter"], 1)
# 创建左侧面板布局
self.layouts["left_layout"] = QtWidgets.QVBoxLayout(self.controls["left_panel"])
self.layouts["left_layout"].setContentsMargins(2, 2, 2, 2) # 减小边距
self.layouts["left_layout"].setSpacing(2) # 减小间距
# 创建右侧面板布局
self.layouts["right_layout"] = QtWidgets.QVBoxLayout(self.controls["right_panel"])
self.layouts["right_layout"].setContentsMargins(2, 2, 2, 2) # 减小边距
self.layouts["right_layout"].setSpacing(2) # 减小间距
# 创建左侧面板内容布局 - 不使用GroupBox直接使用垂直布局
self.layouts["left_content_layout"] = QtWidgets.QVBoxLayout()
self.layouts["left_content_layout"].setContentsMargins(0, 0, 0, 0)
self.layouts["left_content_layout"].setSpacing(5)
# 添加标题标签
left_title = QtWidgets.QLabel(get_text("Raw Control", "原始控制"))
left_title.setStyleSheet("font-weight: bold;")
self.layouts["left_content_layout"].addWidget(left_title)
# 添加搜索框
self.layouts["left_content_layout"].addWidget(self.controls["search_input"])
# 添加控制列表 - 设置伸缩因子为1使其占据所有可用空间
self.layouts["left_content_layout"].addWidget(self.controls["control_list"], 1)
# 创建滑块布局
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.layouts["raw_slider_layout"].addWidget(self.controls["raw_slider_value"])
self.layouts["raw_slider_layout"].addWidget(self.controls["raw_slider"])
self.layouts["raw_slider_layout"].addWidget(self.controls["raw_slider_all_check"])
# 添加滑块布局
self.layouts["left_content_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_minus"].setSizePolicy(size_policy)
self.buttons["range_plus"].setSizePolicy(size_policy)
self.layouts["range_buttons_layout"].addWidget(self.buttons["range_minus"])
self.layouts["range_buttons_layout"].addWidget(self.buttons["range_plus"])
# 添加范围按钮布局到Raw Control布局
self.layouts["left_content_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 [ "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 ["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["left_content_layout"].addLayout(self.layouts["page_buttons_layout"])
# 添加左侧内容布局到左侧面板布局 - 设置伸缩因子为1使其占据所有可用空间
self.layouts["left_layout"].addLayout(self.layouts["left_content_layout"], 1)
# 创建右侧面板内容布局 - 不使用GroupBox直接使用垂直布局
self.layouts["right_content_layout"] = QtWidgets.QVBoxLayout()
self.layouts["right_content_layout"].setContentsMargins(0, 0, 0, 0)
self.layouts["right_content_layout"].setSpacing(5)
# 添加标题标签
right_title = QtWidgets.QLabel(get_text("Related BlendShapes", "相关BlendShapes"))
right_title.setStyleSheet("font-weight: bold;")
self.layouts["right_content_layout"].addWidget(right_title)
# 添加BlendShapes列表 - 设置伸缩因子为1使其占据所有可用空间
self.layouts["right_content_layout"].addWidget(self.controls["blendshapes_list"], 1)
# 创建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.layouts["bs_slider_layout"].addWidget(self.controls["bs_slider_value"])
self.layouts["bs_slider_layout"].addWidget(self.controls["bs_slider"])
self.layouts["bs_slider_layout"].addWidget(self.controls["bs_slider_all_check"])
# 右侧滑块布局
self.layouts["right_content_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按钮的策略使其均等撑满一行
self.buttons["bs_range_minus"].setSizePolicy(size_policy)
self.buttons["bs_range_plus"].setSizePolicy(size_policy)
self.layouts["bs_range_buttons_layout"].addWidget(self.buttons["bs_range_minus"])
self.layouts["bs_range_buttons_layout"].addWidget(self.buttons["bs_range_plus"])
# 添加BS范围按钮布局到BlendShapes布局
self.layouts["right_content_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].setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
# BlendShape操作按钮网格
self.layouts["bs_buttons_grid"] = QtWidgets.QGridLayout()
self.layouts["bs_buttons_grid"].setContentsMargins(0, 0, 0, 0)
self.layouts["bs_buttons_grid"].setSpacing(2)
# 添加按钮到网格
self.layouts["bs_buttons_grid"].addWidget(self.buttons["flip_target"], 0, 0)
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["delete_blendshape"], 1, 1)
self.layouts["bs_buttons_grid"].addWidget(self.buttons["batch_blendshape"], 1, 2)
self.layouts["bs_buttons_grid"].addWidget(self.buttons["rebuild_select"], 2, 0)
self.layouts["bs_buttons_grid"].addWidget(self.buttons["reposition_joints"], 2, 1)
self.layouts["bs_buttons_grid"].addWidget(self.buttons["blend_select"], 2, 2)
# 添加按钮网格到BlendShapes布局
self.layouts["right_content_layout"].addLayout(self.layouts["bs_buttons_grid"])
# 添加右侧内容布局到右侧面板布局 - 设置伸缩因子为1使其占据所有可用空间
self.layouts["right_layout"].addLayout(self.layouts["right_content_layout"], 1)
# 底部标签页按钮行
self.layouts["tab_buttons_layout"] = QtWidgets.QHBoxLayout()
self.layouts["tab_buttons_layout"].setContentsMargins(0, 0, 0, 0)
self.layouts["tab_buttons_layout"].setSpacing(0)
# 设置标签页按钮的策略,使其均等撑满一行
for button_name in ["tab_psd", "tab_bse", "tab_key", "tab_mir", "tab_ark", "tab_ctr"]:
if button_name in self.buttons:
self.buttons[button_name].setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
self.layouts["tab_buttons_layout"].addWidget(self.buttons[button_name])
# 添加标签页按钮行到主布局
self.layouts["main_layout"].addLayout(self.layouts["tab_buttons_layout"])
# 底部按钮布局
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_buttons_row1"] = QtWidgets.QHBoxLayout()
self.layouts["bottom_buttons_row1"].setContentsMargins(0, 0, 0, 0)
self.layouts["bottom_buttons_row1"].setSpacing(5)
self.layouts["bottom_buttons_row1"].addWidget(self.buttons["reset_default_expression"])
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_row2"] = QtWidgets.QHBoxLayout()
self.layouts["bottom_buttons_row2"].setContentsMargins(0, 0, 0, 0)
self.layouts["bottom_buttons_row2"].setSpacing(5)
self.layouts["bottom_buttons_row2"].addWidget(self.buttons["controller_find"])
self.layouts["bottom_buttons_row2"].addWidget(self.buttons["select_associated_joint"])
self.layouts["bottom_buttons_row2"].addWidget(self.buttons["write_find_mirror"])
# 将两行按钮添加到底部按钮布局
self.layouts["bottom_buttons_layout"].addLayout(self.layouts["bottom_buttons_row1"])
self.layouts["bottom_buttons_layout"].addLayout(self.layouts["bottom_buttons_row2"])
# 添加底部主滑块
self.layouts["bottom_main_slider_layout"] = QtWidgets.QHBoxLayout()
self.layouts["bottom_main_slider_layout"].setContentsMargins(5, 5, 5, 5)
self.layouts["bottom_main_slider_layout"].setSpacing(5)
self.layouts["bottom_main_slider_layout"].addWidget(self.controls["bottom_slider_value"])
self.layouts["bottom_main_slider_layout"].addWidget(self.controls["bottom_main_slider"])
self.layouts["bottom_main_slider_layout"].addWidget(self.controls["bottom_slider_all_check"])
# 添加底部按钮布局和主滑块到主布局
self.layouts["main_layout"].addLayout(self.layouts["bottom_buttons_layout"])
self.layouts["main_layout"].addLayout(self.layouts["bottom_main_slider_layout"])
# 将面板添加到分割器
self.splitters["main_splitter"].addWidget(self.controls["left_panel"])
self.splitters["main_splitter"].addWidget(self.controls["right_panel"])
# 改变分割器处理大小变化的方式
self.splitters["main_splitter"].setStretchFactor(0, 1)
self.splitters["main_splitter"].setStretchFactor(1, 1)
# 在最后设置分割器比例
QtCore.QTimer.singleShot(100, lambda: ui_utils.set_splitter_proportions(self.splitters["main_splitter"], [0.5, 0.5]))
# 设置网格布局特性
self.layouts["bs_buttons_grid"].setHorizontalSpacing(1)
self.layouts["bs_buttons_grid"].setVerticalSpacing(1)
self.layouts["bs_buttons_grid"].setColumnMinimumWidth(0, 0)
self.layouts["bs_buttons_grid"].setColumnMinimumWidth(1, 0)
self.layouts["bs_buttons_grid"].setColumnMinimumWidth(2, 0)
self.layouts["bs_buttons_grid"].setColumnStretch(0, 1)
self.layouts["bs_buttons_grid"].setColumnStretch(1, 1)
self.layouts["bs_buttons_grid"].setColumnStretch(2, 1)
def create_connections(self):
"""
创建信号连接
连接按钮点击事件和其他UI事件
"""
# 底部标签页按钮连接
self.buttons["tab_psd"].clicked.connect(lambda: utils_behaviour.switch_tab(0))
self.buttons["tab_bse"].clicked.connect(lambda: utils_behaviour.switch_tab(1))
self.buttons["tab_key"].clicked.connect(lambda: utils_behaviour.switch_tab(2))
self.buttons["tab_mir"].clicked.connect(lambda: utils_behaviour.switch_tab(3))
self.buttons["tab_ark"].clicked.connect(lambda: utils_behaviour.switch_tab(4))
self.buttons["tab_ctr"].clicked.connect(lambda: utils_behaviour.switch_tab(5))
# 功能按钮连接
self.buttons["flip_target"].clicked.connect(utils_behaviour.flip_target)
self.buttons["mirror_target"].clicked.connect(utils_behaviour.mirror_target)
self.buttons["find_flip_target"].clicked.connect(utils_behaviour.find_flip_target)
self.buttons["add_blendshape"].clicked.connect(utils_behaviour.add_blendshape)
self.buttons["delete_blendshape"].clicked.connect(utils_behaviour.delete_blendshape)
self.buttons["batch_blendshape"].clicked.connect(utils_behaviour.batch_blendshape)
self.buttons["rebuild_select"].clicked.connect(utils_behaviour.rebuild_select)
self.buttons["reposition_joints"].clicked.connect(utils_behaviour.reposition_joints)
self.buttons["blend_select"].clicked.connect(utils_behaviour.blend_select)
# 表情控制按钮连接
self.buttons["reset_default_expression"].clicked.connect(utils_behaviour.reset_default_expression)
self.buttons["find_select_expression"].clicked.connect(utils_behaviour.find_select_expression)
self.buttons["write_current_expressions"].clicked.connect(utils_behaviour.write_current_expressions)
self.buttons["controller_find"].clicked.connect(utils_behaviour.controller_find)
self.buttons["select_associated_joint"].clicked.connect(utils_behaviour.select_associated_joint)
self.buttons["write_find_mirror"].clicked.connect(utils_behaviour.write_find_mirror)
# 范围按钮连接
self.buttons["range_plus"].clicked.connect(lambda: utils_behaviour.adjust_range(0.1))
self.buttons["range_minus"].clicked.connect(lambda: utils_behaviour.adjust_range(-0.1))
self.buttons["bs_range_plus"].clicked.connect(lambda: utils_behaviour.adjust_bs_range(0.1))
self.buttons["bs_range_minus"].clicked.connect(lambda: utils_behaviour.adjust_bs_range(-0.1))
# 滑块控件连接
self.controls["raw_slider"].valueChanged.connect(utils_behaviour.on_raw_slider_changed)
self.controls["bs_slider"].valueChanged.connect(utils_behaviour.on_bs_slider_changed)
self.controls["bottom_main_slider"].valueChanged.connect(utils_behaviour.on_bottom_slider_changed)
# 列表控件连接
self.controls["control_list"].itemSelectionChanged.connect(utils_behaviour.on_control_selection_changed)
self.controls["blendshapes_list"].itemSelectionChanged.connect(utils_behaviour.on_blendshape_selection_changed)
# 连接Maya选择变化事件
ui_utils.connect_maya_selection_changed(self, lambda: utils_behaviour.on_selection_changed(self), self.main_widget)