MetaWhiz/scripts/ui/settings_dialog.py
2025-04-17 13:00:39 +08:00

568 lines
25 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Tool - Settings Dialog
Provides a dialog for managing user settings and preferences
"""
import os
import sys
import maya.cmds as cmds
import maya.OpenMayaUI as omui
try:
from PySide2 import QtCore, QtGui, QtWidgets
from shiboken2 import wrapInstance
except ImportError:
try:
from PySide6 import QtCore, QtGui, QtWidgets
from shiboken6 import wrapInstance
except ImportError:
from PySide import QtCore, QtGui, QtWidgets
from shiboken import wrapInstance
# Import configuration
import config
# Import settings utilities
from utils.settings_utils import SettingsUtils
class SettingsDialog(QtWidgets.QDialog):
"""Settings dialog for the Tool plugin"""
def __init__(self, parent=None):
"""Initialize the settings dialog"""
super(SettingsDialog, self).__init__(parent)
self.setWindowTitle(f"{config.TOOL_NAME} - Settings")
self.setMinimumSize(600, 500)
self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint)
# Initialize settings utility
self.settings_utils = SettingsUtils()
self.settings = self.settings_utils.get_all_settings()
# Initialize UI
self._create_widgets()
self._create_layouts()
self._create_connections()
self._load_settings()
def _create_widgets(self):
"""Create dialog widgets"""
# Create tab widget
self.tab_widget = QtWidgets.QTabWidget()
# Create general settings page
self.general_widget = QtWidgets.QWidget()
self.general_layout = QtWidgets.QVBoxLayout(self.general_widget)
# Language settings
self.language_group = QtWidgets.QGroupBox("Language")
self.language_layout = QtWidgets.QVBoxLayout(self.language_group)
self.language_combo = QtWidgets.QComboBox()
self.language_combo.addItem("Chinese (Simplified)", "zh_CN")
self.language_combo.addItem("English", "en_US")
self.language_layout.addWidget(self.language_combo)
# Theme settings
self.theme_group = QtWidgets.QGroupBox("Theme")
self.theme_layout = QtWidgets.QVBoxLayout(self.theme_group)
self.theme_combo = QtWidgets.QComboBox()
self.theme_combo.addItem("Dark Theme", "dark")
self.theme_combo.addItem("Light Theme", "light")
self.theme_layout.addWidget(self.theme_combo)
# Auto-save settings
self.autosave_group = QtWidgets.QGroupBox("Auto Save")
self.autosave_layout = QtWidgets.QVBoxLayout(self.autosave_group)
self.autosave_check = QtWidgets.QCheckBox("Enable Auto Save")
self.autosave_layout.addWidget(self.autosave_check)
self.autosave_interval_layout = QtWidgets.QHBoxLayout()
self.autosave_interval_label = QtWidgets.QLabel("Auto Save Interval (minutes):")
self.autosave_interval_spin = QtWidgets.QSpinBox()
self.autosave_interval_spin.setMinimum(1)
self.autosave_interval_spin.setMaximum(60)
self.autosave_interval_layout.addWidget(self.autosave_interval_label)
self.autosave_interval_layout.addWidget(self.autosave_interval_spin)
self.autosave_layout.addLayout(self.autosave_interval_layout)
# Welcome message settings
self.welcome_check = QtWidgets.QCheckBox("Show Welcome Message")
# Add widgets to general settings page
self.general_layout.addWidget(self.language_group)
self.general_layout.addWidget(self.theme_group)
self.general_layout.addWidget(self.autosave_group)
self.general_layout.addWidget(self.welcome_check)
self.general_layout.addStretch()
# Create DNA settings page
self.dna_widget = QtWidgets.QWidget()
self.dna_layout = QtWidgets.QVBoxLayout(self.dna_widget)
# DNA path settings
self.dna_path_group = QtWidgets.QGroupBox("DNA File Path")
self.dna_path_layout = QtWidgets.QVBoxLayout(self.dna_path_group)
self.dna_path_layout_h = QtWidgets.QHBoxLayout()
self.dna_path_edit = QtWidgets.QLineEdit()
self.dna_path_button = QtWidgets.QPushButton("浏览...")
self.dna_path_layout_h.addWidget(self.dna_path_edit)
self.dna_path_layout_h.addWidget(self.dna_path_button)
self.dna_path_layout.addLayout(self.dna_path_layout_h)
# DNA选项设置
self.dna_options_group = QtWidgets.QGroupBox("DNA选项")
self.dna_options_layout = QtWidgets.QVBoxLayout(self.dna_options_group)
self.dna_autoload_check = QtWidgets.QCheckBox("自动加载上次使用的DNA文件")
self.dna_backup_check = QtWidgets.QCheckBox("编辑前备份DNA文件")
self.dna_options_layout.addWidget(self.dna_autoload_check)
self.dna_options_layout.addWidget(self.dna_backup_check)
# 添加控件到DNA设置页
self.dna_layout.addWidget(self.dna_path_group)
self.dna_layout.addWidget(self.dna_options_group)
self.dna_layout.addStretch()
# 创建校准设置页
self.calibration_widget = QtWidgets.QWidget()
self.calibration_layout = QtWidgets.QVBoxLayout(self.calibration_widget)
# 校准选项设置
self.calibration_options_group = QtWidgets.QGroupBox("校准选项")
self.calibration_options_layout = QtWidgets.QVBoxLayout(self.calibration_options_group)
self.calibration_auto_check = QtWidgets.QCheckBox("自动校准")
self.calibration_reference_check = QtWidgets.QCheckBox("显示参考骨骼")
self.calibration_diff_check = QtWidgets.QCheckBox("显示差异")
self.calibration_options_layout.addWidget(self.calibration_auto_check)
self.calibration_options_layout.addWidget(self.calibration_reference_check)
self.calibration_options_layout.addWidget(self.calibration_diff_check)
# 添加控件到校准设置页
self.calibration_layout.addWidget(self.calibration_options_group)
self.calibration_layout.addStretch()
# 创建绑定设置页
self.binding_widget = QtWidgets.QWidget()
self.binding_layout = QtWidgets.QVBoxLayout(self.binding_widget)
# 绑定方法设置
self.binding_method_group = QtWidgets.QGroupBox("绑定方法")
self.binding_method_layout = QtWidgets.QVBoxLayout(self.binding_method_group)
self.binding_method_combo = QtWidgets.QComboBox()
self.binding_method_combo.addItem("平滑绑定", "smooth")
self.binding_method_combo.addItem("刚性绑定", "rigid")
self.binding_method_combo.addItem("双四元数绑定", "dual_quaternion")
self.binding_method_layout.addWidget(self.binding_method_combo)
# 绑定选项设置
self.binding_options_group = QtWidgets.QGroupBox("绑定选项")
self.binding_options_layout = QtWidgets.QVBoxLayout(self.binding_options_group)
self.binding_mirror_check = QtWidgets.QCheckBox("镜像权重")
self.binding_normalize_check = QtWidgets.QCheckBox("标准化权重")
self.binding_options_layout.addWidget(self.binding_mirror_check)
self.binding_options_layout.addWidget(self.binding_normalize_check)
# 添加控件到绑定设置页
self.binding_layout.addWidget(self.binding_method_group)
self.binding_layout.addWidget(self.binding_options_group)
self.binding_layout.addStretch()
# 创建BlendShape设置页
self.blendshape_widget = QtWidgets.QWidget()
self.blendshape_layout = QtWidgets.QVBoxLayout(self.blendshape_widget)
# 导出格式设置
self.blendshape_format_group = QtWidgets.QGroupBox("导出格式")
self.blendshape_format_layout = QtWidgets.QVBoxLayout(self.blendshape_format_group)
self.blendshape_format_combo = QtWidgets.QComboBox()
self.blendshape_format_combo.addItem("OBJ格式", "obj")
self.blendshape_format_combo.addItem("Maya ASCII格式", "ma")
self.blendshape_format_combo.addItem("FBX格式", "fbx")
self.blendshape_format_layout.addWidget(self.blendshape_format_combo)
# BlendShape选项设置
self.blendshape_options_group = QtWidgets.QGroupBox("BlendShape选项")
self.blendshape_options_layout = QtWidgets.QVBoxLayout(self.blendshape_options_group)
self.blendshape_connect_check = QtWidgets.QCheckBox("自动连接到控制器")
self.blendshape_corrective_check = QtWidgets.QCheckBox("创建修正形状")
self.blendshape_options_layout.addWidget(self.blendshape_connect_check)
self.blendshape_options_layout.addWidget(self.blendshape_corrective_check)
# 添加控件到BlendShape设置页
self.blendshape_layout.addWidget(self.blendshape_format_group)
self.blendshape_layout.addWidget(self.blendshape_options_group)
self.blendshape_layout.addStretch()
# 创建UI设置页
self.ui_widget = QtWidgets.QWidget()
self.ui_layout = QtWidgets.QVBoxLayout(self.ui_widget)
# 字体大小设置
self.ui_font_group = QtWidgets.QGroupBox("字体大小")
self.ui_font_layout = QtWidgets.QVBoxLayout(self.ui_font_group)
self.ui_font_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.ui_font_slider.setMinimum(8)
self.ui_font_slider.setMaximum(14)
self.ui_font_slider.setTickPosition(QtWidgets.QSlider.TicksBelow)
self.ui_font_slider.setTickInterval(1)
self.ui_font_label = QtWidgets.QLabel("9")
self.ui_font_label.setAlignment(QtCore.Qt.AlignCenter)
self.ui_font_layout.addWidget(self.ui_font_slider)
self.ui_font_layout.addWidget(self.ui_font_label)
# 图标大小设置
self.ui_icon_group = QtWidgets.QGroupBox("图标大小")
self.ui_icon_layout = QtWidgets.QVBoxLayout(self.ui_icon_group)
self.ui_icon_combo = QtWidgets.QComboBox()
self.ui_icon_combo.addItem("", "small")
self.ui_icon_combo.addItem("", "medium")
self.ui_icon_combo.addItem("", "large")
self.ui_icon_layout.addWidget(self.ui_icon_combo)
# UI选项设置
self.ui_options_group = QtWidgets.QGroupBox("UI选项")
self.ui_options_layout = QtWidgets.QVBoxLayout(self.ui_options_group)
self.ui_tooltips_check = QtWidgets.QCheckBox("显示工具提示")
self.ui_confirm_check = QtWidgets.QCheckBox("退出时确认")
self.ui_options_layout.addWidget(self.ui_tooltips_check)
self.ui_options_layout.addWidget(self.ui_confirm_check)
# 添加控件到UI设置页
self.ui_layout.addWidget(self.ui_font_group)
self.ui_layout.addWidget(self.ui_icon_group)
self.ui_layout.addWidget(self.ui_options_group)
self.ui_layout.addStretch()
# 创建性能设置页
self.performance_widget = QtWidgets.QWidget()
self.performance_layout = QtWidgets.QVBoxLayout(self.performance_widget)
# 撤销队列大小设置
self.performance_undo_group = QtWidgets.QGroupBox("撤销队列大小")
self.performance_undo_layout = QtWidgets.QVBoxLayout(self.performance_undo_group)
self.performance_undo_spin = QtWidgets.QSpinBox()
self.performance_undo_spin.setMinimum(10)
self.performance_undo_spin.setMaximum(100)
self.performance_undo_layout.addWidget(self.performance_undo_spin)
# 视口质量设置
self.performance_viewport_group = QtWidgets.QGroupBox("视口质量")
self.performance_viewport_layout = QtWidgets.QVBoxLayout(self.performance_viewport_group)
self.performance_viewport_combo = QtWidgets.QComboBox()
self.performance_viewport_combo.addItem("", "low")
self.performance_viewport_combo.addItem("", "medium")
self.performance_viewport_combo.addItem("", "high")
self.performance_viewport_layout.addWidget(self.performance_viewport_combo)
# 性能选项设置
self.performance_options_group = QtWidgets.QGroupBox("性能选项")
self.performance_options_layout = QtWidgets.QVBoxLayout(self.performance_options_group)
self.performance_gpu_check = QtWidgets.QCheckBox("使用GPU加速")
self.performance_options_layout.addWidget(self.performance_gpu_check)
# 添加控件到性能设置页
self.performance_layout.addWidget(self.performance_undo_group)
self.performance_layout.addWidget(self.performance_viewport_group)
self.performance_layout.addWidget(self.performance_options_group)
self.performance_layout.addStretch()
# 添加标签页
self.tab_widget.addTab(self.general_widget, "常规")
self.tab_widget.addTab(self.dna_widget, "DNA")
self.tab_widget.addTab(self.calibration_widget, "校准")
self.tab_widget.addTab(self.binding_widget, "绑定")
self.tab_widget.addTab(self.blendshape_widget, "BlendShape")
self.tab_widget.addTab(self.ui_widget, "UI")
self.tab_widget.addTab(self.performance_widget, "性能")
# 创建按钮
self.button_box = QtWidgets.QDialogButtonBox(
QtWidgets.QDialogButtonBox.Ok |
QtWidgets.QDialogButtonBox.Cancel |
QtWidgets.QDialogButtonBox.Apply |
QtWidgets.QDialogButtonBox.Reset
)
# 添加导入/导出按钮
self.import_button = QtWidgets.QPushButton("导入设置...")
self.export_button = QtWidgets.QPushButton("导出设置...")
self.button_box.addButton(self.import_button, QtWidgets.QDialogButtonBox.ActionRole)
self.button_box.addButton(self.export_button, QtWidgets.QDialogButtonBox.ActionRole)
def _create_layouts(self):
"""Create dialog layouts"""
# Create main layout
main_layout = QtWidgets.QVBoxLayout(self)
main_layout.setContentsMargins(10, 10, 10, 10)
main_layout.setSpacing(10)
# 添加控件到布局
main_layout.addWidget(self.tab_widget)
main_layout.addWidget(self.button_box)
def _create_connections(self):
"""Create signal/slot connections"""
# Button connections
self.button_box.accepted.connect(self.accept)
self.button_box.rejected.connect(self.reject)
self.button_box.button(QtWidgets.QDialogButtonBox.Apply).clicked.connect(self._apply_settings)
self.button_box.button(QtWidgets.QDialogButtonBox.Reset).clicked.connect(self._reset_settings)
# 导入/导出按钮连接
self.import_button.clicked.connect(self._import_settings)
self.export_button.clicked.connect(self._export_settings)
# 控件连接
self.ui_font_slider.valueChanged.connect(self._update_font_label)
self.dna_path_button.clicked.connect(self._browse_dna_path)
# 自动保存连接
self.autosave_check.toggled.connect(self.autosave_interval_spin.setEnabled)
def _load_settings(self):
"""Load settings to dialog widgets"""
try:
# 常规设置
language = self.settings["general"]["language"]
index = self.language_combo.findData(language)
if index >= 0:
self.language_combo.setCurrentIndex(index)
theme = self.settings["general"]["theme"]
index = self.theme_combo.findData(theme)
if index >= 0:
self.theme_combo.setCurrentIndex(index)
self.autosave_check.setChecked(self.settings["general"]["auto_save"])
self.autosave_interval_spin.setValue(self.settings["general"]["save_interval"])
self.autosave_interval_spin.setEnabled(self.settings["general"]["auto_save"])
self.welcome_check.setChecked(self.settings["general"]["show_welcome"])
# DNA设置
self.dna_path_edit.setText(self.settings["dna"]["default_dna_path"])
self.dna_autoload_check.setChecked(self.settings["dna"]["auto_load_last"])
self.dna_backup_check.setChecked(self.settings["dna"]["backup_before_edit"])
# 校准设置
self.calibration_auto_check.setChecked(self.settings["calibration"]["auto_calibrate"])
self.calibration_reference_check.setChecked(self.settings["calibration"]["show_reference"])
self.calibration_diff_check.setChecked(self.settings["calibration"]["show_differences"])
# 绑定设置
method = self.settings["binding"]["default_method"]
index = self.binding_method_combo.findData(method)
if index >= 0:
self.binding_method_combo.setCurrentIndex(index)
self.binding_mirror_check.setChecked(self.settings["binding"]["mirror_weights"])
self.binding_normalize_check.setChecked(self.settings["binding"]["normalize_weights"])
# BlendShape设置
format = self.settings["blendshape"]["default_export_format"]
index = self.blendshape_format_combo.findData(format)
if index >= 0:
self.blendshape_format_combo.setCurrentIndex(index)
self.blendshape_connect_check.setChecked(self.settings["blendshape"]["auto_connect_controllers"])
self.blendshape_corrective_check.setChecked(self.settings["blendshape"]["create_corrective_shapes"])
# UI设置
self.ui_font_slider.setValue(self.settings["ui"]["font_size"])
self._update_font_label(self.settings["ui"]["font_size"])
icon_size = self.settings["ui"]["icon_size"]
index = self.ui_icon_combo.findData(icon_size)
if index >= 0:
self.ui_icon_combo.setCurrentIndex(index)
self.ui_tooltips_check.setChecked(self.settings["ui"]["show_tooltips"])
self.ui_confirm_check.setChecked(self.settings["ui"]["confirm_on_exit"])
# 性能设置
self.performance_undo_spin.setValue(self.settings["performance"]["undo_queue_size"])
viewport_quality = self.settings["performance"]["viewport_quality"]
index = self.performance_viewport_combo.findData(viewport_quality)
if index >= 0:
self.performance_viewport_combo.setCurrentIndex(index)
self.performance_gpu_check.setChecked(self.settings["performance"]["use_gpu_acceleration"])
except Exception as e:
cmds.warning(f"Error loading settings to dialog: {str(e)}")
def _save_settings(self):
"""Save settings from dialog widgets"""
try:
# 常规设置
self.settings["general"]["language"] = self.language_combo.currentData()
self.settings["general"]["theme"] = self.theme_combo.currentData()
self.settings["general"]["auto_save"] = self.autosave_check.isChecked()
self.settings["general"]["save_interval"] = self.autosave_interval_spin.value()
self.settings["general"]["show_welcome"] = self.welcome_check.isChecked()
# DNA设置
self.settings["dna"]["default_dna_path"] = self.dna_path_edit.text()
self.settings["dna"]["auto_load_last"] = self.dna_autoload_check.isChecked()
self.settings["dna"]["backup_before_edit"] = self.dna_backup_check.isChecked()
# 校准设置
self.settings["calibration"]["auto_calibrate"] = self.calibration_auto_check.isChecked()
self.settings["calibration"]["show_reference"] = self.calibration_reference_check.isChecked()
self.settings["calibration"]["show_differences"] = self.calibration_diff_check.isChecked()
# 绑定设置
self.settings["binding"]["default_method"] = self.binding_method_combo.currentData()
self.settings["binding"]["mirror_weights"] = self.binding_mirror_check.isChecked()
self.settings["binding"]["normalize_weights"] = self.binding_normalize_check.isChecked()
# BlendShape设置
self.settings["blendshape"]["default_export_format"] = self.blendshape_format_combo.currentData()
self.settings["blendshape"]["auto_connect_controllers"] = self.blendshape_connect_check.isChecked()
self.settings["blendshape"]["create_corrective_shapes"] = self.blendshape_corrective_check.isChecked()
# UI设置
self.settings["ui"]["font_size"] = self.ui_font_slider.value()
self.settings["ui"]["icon_size"] = self.ui_icon_combo.currentData()
self.settings["ui"]["show_tooltips"] = self.ui_tooltips_check.isChecked()
self.settings["ui"]["confirm_on_exit"] = self.ui_confirm_check.isChecked()
# 性能设置
self.settings["performance"]["undo_queue_size"] = self.performance_undo_spin.value()
self.settings["performance"]["viewport_quality"] = self.performance_viewport_combo.currentData()
self.settings["performance"]["use_gpu_acceleration"] = self.performance_gpu_check.isChecked()
# 保存设置
self.settings_utils.save_settings(self.settings)
return True
except Exception as e:
cmds.warning(f"Error saving settings from dialog: {str(e)}")
return False
def _apply_settings(self):
"""Apply settings"""
if self._save_settings():
# 通知主窗口应用设置
self.parent().apply_settings(self.settings)
def _reset_settings(self):
"""Reset settings"""
reply = QtWidgets.QMessageBox.question(
self,
"Reset Settings",
"Are you sure you want to reset all settings to default values?",
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
QtWidgets.QMessageBox.No
)
if reply == QtWidgets.QMessageBox.Yes:
# Reset settings
self.settings_utils.reset_settings()
self.settings = self.settings_utils.get_all_settings()
# 重新加载设置到对话框
self._load_settings()
def _update_font_label(self, value):
"""Update font size label"""
self.ui_font_label.setText(str(value))
def _browse_dna_path(self):
"""Browse DNA path"""
dir_path = QtWidgets.QFileDialog.getExistingDirectory(
self,
"Select DNA File Directory",
self.dna_path_edit.text()
)
if dir_path:
self.dna_path_edit.setText(dir_path)
def _import_settings(self):
"""Import settings"""
file_path, _ = QtWidgets.QFileDialog.getOpenFileName(
self,
"导入设置",
"",
"JSON文件 (*.json);;所有文件 (*.*)"
)
if file_path:
if self.settings_utils.import_settings(file_path):
self.settings = self.settings_utils.get_all_settings()
self._load_settings()
QtWidgets.QMessageBox.information(
self,
"导入设置",
f"Successfully imported settings: {file_path}"
)
def _export_settings(self):
"""Export settings"""
file_path, _ = QtWidgets.QFileDialog.getSaveFileName(
self,
"导出设置",
"",
"JSON文件 (*.json);;所有文件 (*.*)"
)
if file_path:
# 先保存当前设置
self._save_settings()
if self.settings_utils.export_settings(file_path):
QtWidgets.QMessageBox.information(
self,
"导出设置",
f"Successfully exported settings: {file_path}"
)
def accept(self):
"""Accept dialog"""
if self._save_settings():
# 通知主窗口应用设置
self.parent().apply_settings(self.settings)
super(SettingsDialog, self).accept()
def reject(self):
"""Reject dialog"""
reply = QtWidgets.QMessageBox.question(
self,
"Cancel Settings",
"Are you sure you want to cancel all setting changes?",
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
QtWidgets.QMessageBox.No
)
if reply == QtWidgets.QMessageBox.Yes:
super(SettingsDialog, self).reject()