#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Geometry UI Module for Plugin
几何模型UI模块 - 负责显示几何模型编辑界面和基础操作
基本功能:
- 模型拾取以及加载
- LOD模型分级过滤
- LOD模型创建
- 自动加载模型
- 标准化命名
- 自动分组
- 生成面部配件(睑毛,舌头,泪腺 等)
- 修复接缝(修复法线)
- 修复点序
"""
#========================================= 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_geometry
#========================================== 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 GeometryUI(ui_utils.BaseUI):
"""
几何模型UI类 - 负责显示几何模型编辑界面和基础操作
继承自BaseUI类,实现几何模型相关的UI功能
"""
#========================================== INIT ========================================
def __init__(self):
"""
初始化几何模型UI
创建主控件和布局,并连接信号和槽
"""
super(GeometryUI, self).__init__()
# 创建主控件
self.main_widget = QtWidgets.QWidget()
self.main_widget.setObjectName("geometryMainWidget")
# 初始化UI
self.create_widgets()
self.create_layouts()
self.create_connections()
#========================================= WIDGET =======================================
def create_widgets(self):
"""
创建几何模型UI控件
包括按钮、标签、列表等
"""
# 标题标签 - 使用HTML格式化标题
title_text = f"
{LANG.get('geometry_title', '几何模型')}
"
self.controls["title_label"] = QtWidgets.QLabel(title_text)
self.controls["title_label"].setObjectName("geometryTitleLabel")
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("geometryLeftPanel")
# 右侧面板
self.controls["right_panel"] = QtWidgets.QWidget()
self.controls["right_panel"].setObjectName("geometryRightPanel")
# 左侧面板控件 - LOD列表
self.controls["lod_group"] = QtWidgets.QGroupBox("LOD [000]")
self.controls["lod_group"].setObjectName("lodGroup")
# LOD列表
self.controls["lod_list"] = QtWidgets.QListWidget()
self.controls["lod_list"].setObjectName("lodList")
# 添加测试项目
for i in range(4):
item = QtWidgets.QListWidgetItem(f"LOD{i}")
self.controls["lod_list"].addItem(item)
# LOD操作按钮
self.buttons["add_lod"] = QtWidgets.QPushButton(LANG.get("add_lod", "添加LOD"))
self.buttons["add_lod"].setObjectName("addLodButton")
self.buttons["remove_lod"] = QtWidgets.QPushButton(LANG.get("remove_lod", "移除LOD"))
self.buttons["remove_lod"].setObjectName("removeLodButton")
self.buttons["duplicate_lod"] = QtWidgets.QPushButton(LANG.get("duplicate_lod", "复制LOD"))
self.buttons["duplicate_lod"].setObjectName("duplicateLodButton")
# 左侧面板控件 - 模型列表
self.controls["model_group"] = QtWidgets.QGroupBox("Models [000]")
self.controls["model_group"].setObjectName("modelGroup")
# 模型列表
self.controls["model_list"] = QtWidgets.QListWidget()
self.controls["model_list"].setObjectName("modelList")
# 模型操作按钮
self.buttons["add_model"] = QtWidgets.QPushButton(LANG.get("add_model", "添加模型"))
self.buttons["add_model"].setObjectName("addModelButton")
self.buttons["remove_model"] = QtWidgets.QPushButton(LANG.get("remove_model", "移除模型"))
self.buttons["remove_model"].setObjectName("removeModelButton")
self.buttons["duplicate_model"] = QtWidgets.QPushButton(LANG.get("duplicate_model", "复制模型"))
self.buttons["duplicate_model"].setObjectName("duplicateModelButton")
# 右侧面板控件 - 模型属性
self.controls["model_properties_group"] = QtWidgets.QGroupBox(LANG.get("model_properties", "模型属性"))
self.controls["model_properties_group"].setObjectName("modelPropertiesGroup")
# 模型名称标签和输入框
self.controls["model_name_label"] = QtWidgets.QLabel(LANG.get("name", "名称:"))
self.controls["model_name_label"].setObjectName("modelNameLabel")
self.controls["model_name_input"] = QtWidgets.QLineEdit()
self.controls["model_name_input"].setObjectName("modelNameInput")
self.controls["model_name_input"].setPlaceholderText(LANG.get("enter_model_name", "输入模型名称"))
# 模型类型标签和下拉框
self.controls["model_type_label"] = QtWidgets.QLabel(LANG.get("type", "类型:"))
self.controls["model_type_label"].setObjectName("modelTypeLabel")
self.controls["model_type_combo"] = QtWidgets.QComboBox()
self.controls["model_type_combo"].setObjectName("modelTypeCombo")
self.controls["model_type_combo"].addItems(["Base", "Head", "Body", "Accessory", "Other"])
# 模型可见性复选框
self.controls["model_visible_check"] = QtWidgets.QCheckBox(LANG.get("visible", "可见"))
self.controls["model_visible_check"].setObjectName("modelVisibleCheck")
self.controls["model_visible_check"].setChecked(True)
# 模型属性按钮
self.buttons["apply_properties"] = QtWidgets.QPushButton(LANG.get("apply", "应用"))
self.buttons["apply_properties"].setObjectName("applyPropertiesButton")
self.buttons["reset_properties"] = QtWidgets.QPushButton(LANG.get("reset", "重置"))
self.buttons["reset_properties"].setObjectName("resetPropertiesButton")
# 右侧面板控件 - 模型工具
self.controls["model_tools_group"] = QtWidgets.QGroupBox(LANG.get("model_tools", "模型工具"))
self.controls["model_tools_group"].setObjectName("modelToolsGroup")
# 模型工具按钮
self.buttons["standardize_names"] = QtWidgets.QPushButton(LANG.get("standardize_names", "标准化命名"))
self.buttons["standardize_names"].setObjectName("standardizeNamesButton")
self.buttons["auto_group"] = QtWidgets.QPushButton(LANG.get("auto_group", "自动分组"))
self.buttons["auto_group"].setObjectName("autoGroupButton")
self.buttons["generate_accessories"] = QtWidgets.QPushButton(LANG.get("generate_accessories", "生成配件"))
self.buttons["generate_accessories"].setObjectName("generateAccessoriesButton")
self.buttons["fix_seams"] = QtWidgets.QPushButton(LANG.get("fix_seams", "修复接缝"))
self.buttons["fix_seams"].setObjectName("fixSeamsButton")
self.buttons["fix_vertex_order"] = QtWidgets.QPushButton(LANG.get("fix_vertex_order", "修复点序"))
self.buttons["fix_vertex_order"].setObjectName("fixVertexOrderButton")
# 底部工具面板
# 导入部分
self.controls["import_group"] = QtWidgets.QGroupBox(LANG.get("import", "导入"))
self.controls["import_group"].setObjectName("importGroup")
self.buttons["import_model"] = QtWidgets.QPushButton(LANG.get("import_model", "导入模型"))
self.buttons["import_model"].setObjectName("importModelButton")
self.buttons["import_fbx"] = QtWidgets.QPushButton(LANG.get("import_fbx", "导入FBX"))
self.buttons["import_fbx"].setObjectName("importFbxButton")
self.buttons["import_obj"] = QtWidgets.QPushButton(LANG.get("import_obj", "导入OBJ"))
self.buttons["import_obj"].setObjectName("importObjButton")
# 导出部分
self.controls["export_group"] = QtWidgets.QGroupBox(LANG.get("export", "导出"))
self.controls["export_group"].setObjectName("exportGroup")
self.buttons["export_model"] = QtWidgets.QPushButton(LANG.get("export_model", "导出模型"))
self.buttons["export_model"].setObjectName("exportModelButton")
self.buttons["export_fbx"] = QtWidgets.QPushButton(LANG.get("export_fbx", "导出 FBX"))
self.buttons["export_fbx"].setObjectName("exportFbxButton")
self.buttons["export_obj"] = QtWidgets.QPushButton(LANG.get("export_obj", "导出 OBJ"))
self.buttons["export_obj"].setObjectName("exportObjButton")
# 工具部分
self.controls["tools_group"] = QtWidgets.QGroupBox(LANG.get("tools", "工具"))
self.controls["tools_group"].setObjectName("toolsGroup")
self.buttons["check_model"] = QtWidgets.QPushButton(LANG.get("check_model", "检查模型"))
self.buttons["check_model"].setObjectName("checkModelButton")
self.buttons["optimize_model"] = QtWidgets.QPushButton(LANG.get("optimize_model", "优化模型"))
self.buttons["optimize_model"].setObjectName("optimizeModelButton")
self.buttons["clean_model"] = QtWidgets.QPushButton(LANG.get("clean_model", "清理模型"))
self.buttons["clean_model"].setObjectName("cleanModelButton")
self.buttons["uv_tools"] = QtWidgets.QPushButton(LANG.get("uv_tools", "UV工具"))
self.buttons["uv_tools"].setObjectName("uvToolsButton")
#========================================= 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("geometryMainSplitter")
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)
# LOD组布局
self.layouts["lod_group_layout"] = QtWidgets.QVBoxLayout(self.controls["lod_group"])
self.layouts["lod_group_layout"].setContentsMargins(5, 10, 5, 5)
self.layouts["lod_group_layout"].setSpacing(5)
# LOD列表
self.layouts["lod_group_layout"].addWidget(self.controls["lod_list"])
# LOD操作按钮行
self.layouts["lod_buttons_layout"] = QtWidgets.QHBoxLayout()
self.layouts["lod_buttons_layout"].setContentsMargins(0, 0, 0, 0)
self.layouts["lod_buttons_layout"].setSpacing(2)
self.layouts["lod_buttons_layout"].addWidget(self.buttons["add_lod"])
self.layouts["lod_buttons_layout"].addWidget(self.buttons["remove_lod"])
self.layouts["lod_buttons_layout"].addWidget(self.buttons["duplicate_lod"])
self.layouts["lod_group_layout"].addLayout(self.layouts["lod_buttons_layout"])
# 模型组布局
self.layouts["model_group_layout"] = QtWidgets.QVBoxLayout(self.controls["model_group"])
self.layouts["model_group_layout"].setContentsMargins(5, 10, 5, 5)
self.layouts["model_group_layout"].setSpacing(5)
# 模型列表
self.layouts["model_group_layout"].addWidget(self.controls["model_list"])
# 模型操作按钮行
self.layouts["model_buttons_layout"] = QtWidgets.QHBoxLayout()
self.layouts["model_buttons_layout"].setContentsMargins(0, 0, 0, 0)
self.layouts["model_buttons_layout"].setSpacing(2)
self.layouts["model_buttons_layout"].addWidget(self.buttons["add_model"])
self.layouts["model_buttons_layout"].addWidget(self.buttons["remove_model"])
self.layouts["model_buttons_layout"].addWidget(self.buttons["duplicate_model"])
self.layouts["model_group_layout"].addLayout(self.layouts["model_buttons_layout"])
# 添加组到左侧面板
self.layouts["left_layout"].addWidget(self.controls["lod_group"])
self.layouts["left_layout"].addWidget(self.controls["model_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)
# 模型属性组布局
self.layouts["model_properties_layout"] = QtWidgets.QVBoxLayout(self.controls["model_properties_group"])
self.layouts["model_properties_layout"].setContentsMargins(5, 10, 5, 5)
self.layouts["model_properties_layout"].setSpacing(5)
# 模型名称行
self.layouts["model_name_layout"] = QtWidgets.QHBoxLayout()
self.layouts["model_name_layout"].setContentsMargins(0, 0, 0, 0)
self.layouts["model_name_layout"].setSpacing(5)
self.layouts["model_name_layout"].addWidget(self.controls["model_name_label"])
self.layouts["model_name_layout"].addWidget(self.controls["model_name_input"])
self.layouts["model_properties_layout"].addLayout(self.layouts["model_name_layout"])
# 模型类型行
self.layouts["model_type_layout"] = QtWidgets.QHBoxLayout()
self.layouts["model_type_layout"].setContentsMargins(0, 0, 0, 0)
self.layouts["model_type_layout"].setSpacing(5)
self.layouts["model_type_layout"].addWidget(self.controls["model_type_label"])
self.layouts["model_type_layout"].addWidget(self.controls["model_type_combo"])
self.layouts["model_properties_layout"].addLayout(self.layouts["model_type_layout"])
# 可见性复选框
self.layouts["model_properties_layout"].addWidget(self.controls["model_visible_check"])
# 属性按钮行
self.layouts["properties_buttons_layout"] = QtWidgets.QHBoxLayout()
self.layouts["properties_buttons_layout"].setContentsMargins(0, 0, 0, 0)
self.layouts["properties_buttons_layout"].setSpacing(5)
self.layouts["properties_buttons_layout"].addWidget(self.buttons["apply_properties"])
self.layouts["properties_buttons_layout"].addWidget(self.buttons["reset_properties"])
self.layouts["model_properties_layout"].addLayout(self.layouts["properties_buttons_layout"])
# 模型工具组布局
self.layouts["model_tools_layout"] = QtWidgets.QVBoxLayout(self.controls["model_tools_group"])
self.layouts["model_tools_layout"].setContentsMargins(5, 10, 5, 5)
self.layouts["model_tools_layout"].setSpacing(5)
# 模型工具按钮网格
self.layouts["tools_grid"] = QtWidgets.QGridLayout()
self.layouts["tools_grid"].setContentsMargins(0, 0, 0, 0)
self.layouts["tools_grid"].setSpacing(5)
self.layouts["tools_grid"].addWidget(self.buttons["standardize_names"], 0, 0)
self.layouts["tools_grid"].addWidget(self.buttons["auto_group"], 0, 1)
self.layouts["tools_grid"].addWidget(self.buttons["generate_accessories"], 1, 0)
self.layouts["tools_grid"].addWidget(self.buttons["fix_seams"], 1, 1)
self.layouts["tools_grid"].addWidget(self.buttons["fix_vertex_order"], 2, 0, 1, 2)
self.layouts["model_tools_layout"].addLayout(self.layouts["tools_grid"])
# 添加组到右侧面板
self.layouts["right_layout"].addWidget(self.controls["model_properties_group"])
self.layouts["right_layout"].addWidget(self.controls["model_tools_group"])
# 底部工具面板
self.layouts["bottom_panel"] = QtWidgets.QHBoxLayout()
self.layouts["bottom_panel"].setContentsMargins(2, 2, 2, 2)
self.layouts["bottom_panel"].setSpacing(5)
# 导入组布局
self.layouts["import_layout"] = QtWidgets.QVBoxLayout(self.controls["import_group"])
self.layouts["import_layout"].setContentsMargins(5, 10, 5, 5)
self.layouts["import_layout"].setSpacing(5)
self.layouts["import_layout"].addWidget(self.buttons["import_model"])
self.layouts["import_layout"].addWidget(self.buttons["import_fbx"])
self.layouts["import_layout"].addWidget(self.buttons["import_obj"])
# 导出组布局
self.layouts["export_layout"] = QtWidgets.QVBoxLayout(self.controls["export_group"])
self.layouts["export_layout"].setContentsMargins(5, 10, 5, 5)
self.layouts["export_layout"].setSpacing(5)
self.layouts["export_layout"].addWidget(self.buttons["export_model"])
self.layouts["export_layout"].addWidget(self.buttons["export_fbx"])
self.layouts["export_layout"].addWidget(self.buttons["export_obj"])
# 工具组布局
self.layouts["tools_layout"] = QtWidgets.QVBoxLayout(self.controls["tools_group"])
self.layouts["tools_layout"].setContentsMargins(5, 10, 5, 5)
self.layouts["tools_layout"].setSpacing(5)
self.layouts["tools_layout"].addWidget(self.buttons["check_model"])
self.layouts["tools_layout"].addWidget(self.buttons["optimize_model"])
self.layouts["tools_layout"].addWidget(self.buttons["clean_model"])
self.layouts["tools_layout"].addWidget(self.buttons["uv_tools"])
# 添加组到底部面板
self.layouts["bottom_panel"].addWidget(self.controls["import_group"])
self.layouts["bottom_panel"].addWidget(self.controls["export_group"])
self.layouts["bottom_panel"].addWidget(self.controls["tools_group"])
# 添加底部面板到主布局
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_geometry
# 左侧面板连接
# LOD列表连接
self.controls["lod_list"].itemClicked.connect(utils_geometry.lod_selected)
self.controls["lod_list"].itemSelectionChanged.connect(utils_geometry.update_lod_selection)
# LOD操作按钮连接
self.buttons["add_lod"].clicked.connect(utils_geometry.add_lod)
self.buttons["remove_lod"].clicked.connect(utils_geometry.remove_lod)
self.buttons["duplicate_lod"].clicked.connect(utils_geometry.duplicate_lod)
# 模型列表连接
self.controls["model_list"].itemClicked.connect(utils_geometry.model_selected)
self.controls["model_list"].itemSelectionChanged.connect(utils_geometry.update_model_selection)
# 模型操作按钮连接
self.buttons["add_model"].clicked.connect(utils_geometry.add_model)
self.buttons["remove_model"].clicked.connect(utils_geometry.remove_model)
self.buttons["duplicate_model"].clicked.connect(utils_geometry.duplicate_model)
# 右侧面板连接
# 模型属性连接
self.controls["model_name_input"].textChanged.connect(utils_geometry.update_model_name)
self.controls["model_type_combo"].currentIndexChanged.connect(utils_geometry.update_model_type)
self.controls["model_visible_check"].stateChanged.connect(utils_geometry.toggle_model_visibility)
# 属性按钮连接
self.buttons["apply_properties"].clicked.connect(utils_geometry.apply_properties)
self.buttons["reset_properties"].clicked.connect(utils_geometry.reset_properties)
# 模型工具按钮连接
self.buttons["standardize_names"].clicked.connect(utils_geometry.standardize_names)
self.buttons["auto_group"].clicked.connect(utils_geometry.auto_group)
self.buttons["generate_accessories"].clicked.connect(utils_geometry.generate_accessories)
self.buttons["fix_seams"].clicked.connect(utils_geometry.fix_seams)
self.buttons["fix_vertex_order"].clicked.connect(utils_geometry.fix_vertex_order)
# 底部工具面板连接
# 导入按钮连接
self.buttons["import_model"].clicked.connect(utils_geometry.import_model)
self.buttons["import_fbx"].clicked.connect(utils_geometry.import_fbx)
self.buttons["import_obj"].clicked.connect(utils_geometry.import_obj)
# 导出按钮连接
self.buttons["export_model"].clicked.connect(utils_geometry.export_model)
self.buttons["export_fbx"].clicked.connect(utils_geometry.export_fbx)
self.buttons["export_obj"].clicked.connect(utils_geometry.export_obj)
# 工具按钮连接
self.buttons["check_model"].clicked.connect(utils_geometry.check_model)
self.buttons["optimize_model"].clicked.connect(utils_geometry.optimize_model)
self.buttons["clean_model"].clicked.connect(utils_geometry.clean_model)
self.buttons["uv_tools"].clicked.connect(utils_geometry.uv_tools)