#!/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
#========================================= LOCATION =======================================
from scripts.ui import localization
LANG = localization.LANG
class BehaviourUI(ui_utils.BaseUI):
"""
行为系统UI类 - 负责显示角色行为编辑界面和基础操作
继承自BaseUI类,实现行为系统相关的UI功能
"""
#========================================== INIT ========================================
def __init__(self):
"""
初始化行为系统UI
创建主控件和布局,并连接信号和槽
"""
super(BehaviourUI, self).__init__()
# 创建主控件
self.main_widget = QtWidgets.QWidget()
self.main_widget.setObjectName("behaviourMainWidget")
# 初始化UI
self.create_widgets()
self.create_layouts()
self.create_connections()
#========================================= WIDGET =======================================
def create_widgets(self):
"""
创建行为系统UI控件
包括按钮、标签、列表等
"""
# 标题标签 - 使用HTML格式化标题
title_text = f"
{LANG.get('behaviour_title', '行为')}
"
self.controls["title_label"] = QtWidgets.QLabel(title_text)
self.controls["title_label"].setObjectName("behaviourTitleLabel")
self.controls["title_label"].setAlignment(QtCore.Qt.AlignCenter)
self.controls["title_label"].setMaximumHeight(30) # 限制标题高度
# 左侧面板
self.controls["left_panel"] = QtWidgets.QWidget()
self.controls["left_panel"].setObjectName("behaviourLeftPanel")
# 右侧面板
self.controls["right_panel"] = QtWidgets.QWidget()
self.controls["right_panel"].setObjectName("behaviourRightPanel")
# 左侧面板控件 - Raw Control
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"].setObjectName("searchInput")
self.controls["search_input"].setPlaceholderText(LANG.get("search", "搜索..."))
# 控制列表
self.controls["control_list"] = QtWidgets.QListWidget()
self.controls["control_list"].setObjectName("controlList")
# 底部滑块和按钮
self.controls["bottom_slider"] = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.controls["bottom_slider"].setObjectName("bottomSlider")
self.controls["bottom_slider"].setMinimum(0)
self.controls["bottom_slider"].setMaximum(100)
self.controls["bottom_slider"].setValue(10)
# 页码按钮
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)
# 左下角按钮
self.buttons["range_minus"] = QtWidgets.QPushButton("-")
self.buttons["range_minus"].setObjectName("rangeMinusButton")
self.buttons["range_plus"] = QtWidgets.QPushButton("+")
self.buttons["range_plus"].setObjectName("rangePlusButton")
# 右侧面板控件 - Related BlendShapes
self.controls["blendshapes_group"] = QtWidgets.QGroupBox("Related BlendShapes [000]")
self.controls["blendshapes_group"].setObjectName("blendshapesGroup")
# BlendShapes列表
self.controls["blendshapes_list"] = QtWidgets.QListWidget()
self.controls["blendshapes_list"].setObjectName("blendshapesList")
# 底部滑块和按钮
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)
# BlendShape操作按钮
self.buttons["reset_blendshape"] = QtWidgets.QPushButton(LANG.get("reset_blendshape", "重置混合形状"))
self.buttons["reset_blendshape"].setObjectName("resetBlendshapeButton")
self.buttons["mirror_blendshape"] = QtWidgets.QPushButton(LANG.get("mirror_blendshape", "镜像混合形状"))
self.buttons["mirror_blendshape"].setObjectName("mirrorBlendshapeButton")
self.buttons["add_blendshape"] = QtWidgets.QPushButton(LANG.get("add_blendshape", "添加混合形状"))
self.buttons["add_blendshape"].setObjectName("addBlendshapeButton")
self.buttons["remove_blendshape"] = QtWidgets.QPushButton(LANG.get("remove_blendshape", "移除混合形状"))
self.buttons["remove_blendshape"].setObjectName("removeBlendshapeButton")
self.buttons["update_blendshape"] = QtWidgets.QPushButton(LANG.get("update_blendshape", "更新混合形状"))
self.buttons["update_blendshape"].setObjectName("updateBlendshapeButton")
self.buttons["isolate_blendshape"] = QtWidgets.QPushButton(LANG.get("isolate_blendshape", "隔离混合形状"))
self.buttons["isolate_blendshape"].setObjectName("isolateBlendshapeButton")
self.buttons["new_blendshape"] = QtWidgets.QPushButton(LANG.get("new_blendshape", "新建混合形状"))
self.buttons["new_blendshape"].setObjectName("newBlendshapeButton")
self.buttons["combine_blendshape"] = QtWidgets.QPushButton(LANG.get("combine_blendshape", "组合混合形状"))
self.buttons["combine_blendshape"].setObjectName("combineBlendshapeButton")
# 底部标签页按钮
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"].setObjectName("tabPsdButton")
self.buttons["tab_psd"].setCheckable(True)
self.buttons["tab_key"] = QtWidgets.QPushButton("KEY")
self.buttons["tab_key"].setObjectName("tabKeyButton")
self.buttons["tab_key"].setCheckable(True)
self.buttons["tab_jnt"] = QtWidgets.QPushButton("JNT")
self.buttons["tab_jnt"].setObjectName("tabJntButton")
self.buttons["tab_jnt"].setCheckable(True)
self.buttons["tab_ark"] = QtWidgets.QPushButton("ARK")
self.buttons["tab_ark"].setObjectName("tabArkButton")
self.buttons["tab_ark"].setCheckable(True)
self.buttons["tab_ctr"] = QtWidgets.QPushButton("CTR")
self.buttons["tab_ctr"].setObjectName("tabCtrButton")
self.buttons["tab_ctr"].setCheckable(True)
# 底部滑块
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.buttons["exchange_from_expression"] = QtWidgets.QPushButton(LANG.get("exchange_from_expression", "交换从表情"))
self.buttons["exchange_from_expression"].setObjectName("exchangeFromExpressionButton")
self.buttons["reset_selected_expression"] = QtWidgets.QPushButton(LANG.get("reset_selected_expression", "重置选定表情"))
self.buttons["reset_selected_expression"].setObjectName("resetSelectedExpressionButton")
self.buttons["import_expression"] = QtWidgets.QPushButton(LANG.get("import_expression", "导入表情"))
self.buttons["import_expression"].setObjectName("importExpressionButton")
self.buttons["control_panel_search"] = QtWidgets.QPushButton(LANG.get("control_panel_search", "控制面板搜索"))
self.buttons["control_panel_search"].setObjectName("controlPanelSearchButton")
self.buttons["select_related_keys"] = QtWidgets.QPushButton(LANG.get("select_related_keys", "选择相关键值"))
self.buttons["select_related_keys"].setObjectName("selectRelatedKeysButton")
self.buttons["import_map_expression"] = QtWidgets.QPushButton(LANG.get("import_map_expression", "导入表情映射"))
self.buttons["import_map_expression"].setObjectName("importMapExpressionButton")
# 表情控制按钮
self.buttons["reset_expression"] = QtWidgets.QPushButton(LANG.get("reset_expression", "还原默认表情"))
self.buttons["find_expression"] = QtWidgets.QPushButton(LANG.get("find_expression", "查找选择表情"))
self.buttons["find_control_panel"] = QtWidgets.QPushButton(LANG.get("find_control_panel", "控制面板查找"))
#========================================= 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"])
# 创建主分割器
self.splitters["main_splitter"] = QtWidgets.QSplitter(QtCore.Qt.Horizontal)
self.splitters["main_splitter"].setObjectName("behaviourMainSplitter")
self.layouts["main_layout"].addWidget(self.splitters["main_splitter"])
# 左侧面板布局
self.layouts["left_layout"] = QtWidgets.QVBoxLayout(self.controls["left_panel"])
self.layouts["left_layout"].setContentsMargins(5, 5, 5, 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"].setContentsMargins(5, 5, 5, 5)
self.layouts["right_layout"].setSpacing(5)
# BlendShapes组布局
self.layouts["blendshapes_layout"] = QtWidgets.QVBoxLayout(self.controls["blendshapes_group"])
self.layouts["blendshapes_layout"].setContentsMargins(5, 10, 5, 5)
self.layouts["blendshapes_layout"].setSpacing(5)
# BlendShapes列表
self.layouts["blendshapes_layout"].addWidget(self.controls["blendshapes_list"])
# 滑块
self.layouts["blendshapes_layout"].addWidget(self.controls["bs_slider"])
# 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["reset_blendshape"], 0, 0)
self.layouts["bs_buttons_grid"].addWidget(self.buttons["mirror_blendshape"], 0, 1)
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["update_blendshape"], 2, 0)
self.layouts["bs_buttons_grid"].addWidget(self.buttons["isolate_blendshape"], 2, 1)
self.layouts["bs_buttons_grid"].addWidget(self.buttons["new_blendshape"], 3, 0)
self.layouts["bs_buttons_grid"].addWidget(self.buttons["combine_blendshape"], 3, 1)
self.layouts["blendshapes_layout"].addLayout(self.layouts["bs_buttons_grid"])
# 标签页按钮行
self.layouts["tab_buttons_layout"] = QtWidgets.QHBoxLayout()
self.layouts["tab_buttons_layout"].setContentsMargins(0, 0, 0, 0)
self.layouts["tab_buttons_layout"].setSpacing(2)
self.layouts["tab_buttons_layout"].addWidget(self.buttons["tab_bs"])
self.layouts["tab_buttons_layout"].addWidget(self.buttons["tab_psd"])
self.layouts["tab_buttons_layout"].addWidget(self.buttons["tab_key"])
self.layouts["tab_buttons_layout"].addWidget(self.buttons["tab_jnt"])
self.layouts["tab_buttons_layout"].addWidget(self.buttons["tab_ark"])
self.layouts["tab_buttons_layout"].addWidget(self.buttons["tab_ctr"])
self.layouts["blendshapes_layout"].addLayout(self.layouts["tab_buttons_layout"])
# 添加BlendShapes组到右侧面板
self.layouts["right_layout"].addWidget(self.controls["blendshapes_group"])
# 底部工具面板
self.layouts["bottom_panel"] = QtWidgets.QVBoxLayout()
self.layouts["bottom_panel"].setContentsMargins(2, 2, 2, 2)
self.layouts["bottom_panel"].setSpacing(2)
# 底部滑块
self.layouts["bottom_panel"].addWidget(self.controls["bottom_main_slider"])
# 底部按钮网格
self.layouts["bottom_buttons_grid"] = QtWidgets.QGridLayout()
self.layouts["bottom_buttons_grid"].setContentsMargins(0, 0, 0, 0)
self.layouts["bottom_buttons_grid"].setSpacing(2)
# 添加底部按钮
self.layouts["bottom_buttons_grid"].addWidget(self.buttons["exchange_from_expression"], 0, 0)
self.layouts["bottom_buttons_grid"].addWidget(self.buttons["reset_selected_expression"], 0, 1)
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["main_layout"].addLayout(self.layouts["bottom_panel"])
# 将左右面板添加到主分割器
self.splitters["main_splitter"].addWidget(self.controls["left_panel"])
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 =====================================
def create_connections(self):
"""
连接信号和槽
设置UI控件的交互行为
"""
# 导入行为工具函数
from scripts.utils import utils_behaviour
# 左侧面板连接
# 搜索框连接
self.controls["search_input"].textChanged.connect(utils_behaviour.filter_controls)
# 控制列表连接
self.controls["control_list"].itemClicked.connect(utils_behaviour.control_selected)
self.controls["control_list"].itemSelectionChanged.connect(utils_behaviour.update_control_selection)
# 底部滑块连接
self.controls["bottom_slider"].valueChanged.connect(utils_behaviour.update_control_value)
# 页码按钮连接
self.buttons["page_all"].clicked.connect(lambda: utils_behaviour.switch_page("all"))
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"))
# 左下角按钮连接
self.buttons["range_minus"].clicked.connect(utils_behaviour.decrease_range)
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)