648 lines
26 KiB
Python
648 lines
26 KiB
Python
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
|
||
"""
|
||
Tool - Main Window UI
|
||
Provides the main UI window for the Tool plugin
|
||
"""
|
||
|
||
import os
|
||
import sys
|
||
import maya.cmds as cmds
|
||
import maya.OpenMayaUI as omui
|
||
|
||
# According to the import logic in Main.py, use the global variable QT_VERSION
|
||
# We don't try to import here, but use the modules already imported in Main.py
|
||
# If running this file directly (not through Main.py), you need to import manually
|
||
if 'QtCore' not in globals():
|
||
import maya.cmds as cmds
|
||
import maya.mel as mel
|
||
|
||
# Get Maya version
|
||
MAYA_VERSION = int(cmds.about(version=True).split()[0])
|
||
|
||
# Choose appropriate PySide version based on Maya version
|
||
if MAYA_VERSION >= 2025:
|
||
try:
|
||
from PySide6 import QtCore, QtGui, QtWidgets
|
||
from shiboken6 import wrapInstance
|
||
print(f"Using PySide6 (Maya {MAYA_VERSION})")
|
||
except ImportError as e:
|
||
print(f"PySide6 import error: {str(e)}")
|
||
try:
|
||
from PySide2 import QtCore, QtGui, QtWidgets
|
||
from shiboken2 import wrapInstance
|
||
print(f"Using PySide2 (fallback mode, Maya {MAYA_VERSION})")
|
||
except ImportError:
|
||
cmds.error("Cannot import PySide6 or PySide2, please make sure they are installed")
|
||
elif MAYA_VERSION >= 2022:
|
||
try:
|
||
from PySide2 import QtCore, QtGui, QtWidgets
|
||
from shiboken2 import wrapInstance
|
||
print(f"使用PySide2 (Maya {MAYA_VERSION})")
|
||
except ImportError:
|
||
try:
|
||
from PySide6 import QtCore, QtGui, QtWidgets
|
||
from shiboken6 import wrapInstance
|
||
print(f"Using PySide6 (non-standard configuration, Maya {MAYA_VERSION})")
|
||
except ImportError:
|
||
cmds.error("Cannot import PySide2 or PySide6, please make sure they are installed")
|
||
else:
|
||
try:
|
||
from PySide2 import QtCore, QtGui, QtWidgets
|
||
from shiboken2 import wrapInstance
|
||
print(f"使用PySide2 (Maya {MAYA_VERSION})")
|
||
except ImportError:
|
||
try:
|
||
from PySide import QtCore, QtGui, QtWidgets
|
||
from shiboken import wrapInstance
|
||
print(f"Using PySide (Maya {MAYA_VERSION})")
|
||
except ImportError:
|
||
cmds.error("Cannot import PySide or PySide2, please make sure they are installed")
|
||
|
||
# Import configuration
|
||
import config
|
||
|
||
# Import UI modules
|
||
from ui.dna_editor import DNAEditorWidget
|
||
from ui.calibration import CalibrationWidget
|
||
from ui.binding import BindingWidget
|
||
from ui.blendshape import BlendShapeWidget
|
||
from ui.settings_dialog import SettingsDialog
|
||
|
||
# Import utility modules
|
||
from utils.settings_utils import SettingsUtils
|
||
|
||
def maya_main_window():
|
||
"""Get the Maya main window as a QWidget"""
|
||
main_window_ptr = omui.MQtUtil.mainWindow()
|
||
if main_window_ptr:
|
||
return wrapInstance(int(main_window_ptr), QtWidgets.QWidget)
|
||
return None
|
||
|
||
class ToolWindow(QtWidgets.QMainWindow):
|
||
"""Main window for the Tool plugin"""
|
||
|
||
def __init__(self, parent=maya_main_window()):
|
||
"""Initialize the main window"""
|
||
super(ToolWindow, self).__init__(parent)
|
||
|
||
self.setWindowTitle(f"{config.TOOL_NAME} {config.TOOL_VERSION}")
|
||
self.setMinimumSize(900, 600)
|
||
self.setWindowFlags(QtCore.Qt.Window)
|
||
|
||
# Initialize settings utility
|
||
self.settings_utils = SettingsUtils()
|
||
self.settings = self.settings_utils.get_all_settings()
|
||
|
||
# Set window icon
|
||
icon_path = os.path.join(config.ICONS_PATH, "Logo", "Tool_logo.png")
|
||
if os.path.exists(icon_path):
|
||
self.setWindowIcon(QtGui.QIcon(icon_path))
|
||
|
||
# Initialize UI
|
||
self._create_widgets()
|
||
self._create_layouts()
|
||
self._create_connections()
|
||
self._load_stylesheet()
|
||
|
||
# Apply user settings
|
||
self.apply_settings(self.settings)
|
||
|
||
def _create_widgets(self):
|
||
"""Create main window widgets"""
|
||
# Create header frame
|
||
self.header_frame = QtWidgets.QFrame()
|
||
self.header_frame.setObjectName("headerFrame")
|
||
self.header_frame.setMinimumHeight(60)
|
||
self.header_frame.setMaximumHeight(60)
|
||
|
||
# 创建标题布局
|
||
header_layout = QtWidgets.QHBoxLayout(self.header_frame)
|
||
header_layout.setContentsMargins(10, 5, 10, 5)
|
||
|
||
# 创建Logo标签
|
||
self.logo_label = QtWidgets.QLabel()
|
||
self.logo_label.setObjectName("Logo")
|
||
|
||
# 根据当前主题选择Logo
|
||
theme = self.settings_utils.get_setting("ui", "theme", "dark")
|
||
logo_filename = "logo_dark.png" if theme == "light" else "logo.png"
|
||
logo_path = os.path.join(config.ICONS_PATH, "Logo", logo_filename)
|
||
|
||
# 如果特定主题的Logo不存在,尝试使用默认Logo
|
||
if not os.path.exists(logo_path):
|
||
logo_path = os.path.join(config.ICONS_PATH, "logo.png")
|
||
|
||
if os.path.exists(logo_path):
|
||
# 使用Qt版本兼容的方式缩放图像
|
||
if hasattr(QtCore.Qt, "KeepAspectRatio"):
|
||
logo_pixmap = QtGui.QPixmap(logo_path).scaled(
|
||
40, 40,
|
||
QtCore.Qt.KeepAspectRatio,
|
||
QtCore.Qt.SmoothTransformation
|
||
)
|
||
else:
|
||
# 兼容PySide6的枚举变化
|
||
logo_pixmap = QtGui.QPixmap(logo_path).scaled(
|
||
40, 40,
|
||
QtCore.Qt.AspectRatioMode.KeepAspectRatio,
|
||
QtCore.Qt.TransformationMode.SmoothTransformation
|
||
)
|
||
self.logo_label.setPixmap(logo_pixmap)
|
||
self.logo_label.setMinimumSize(40, 40)
|
||
self.logo_label.setMaximumSize(40, 40)
|
||
|
||
# 创建标题标签
|
||
self.title_label = QtWidgets.QLabel(f"{config.TOOL_NAME}")
|
||
self.title_label.setObjectName("welcomeLabel")
|
||
|
||
# 创建版本标签
|
||
self.version_label = QtWidgets.QLabel(f"v{config.TOOL_VERSION}")
|
||
self.version_label.setObjectName("versionLabel")
|
||
|
||
# 添加控件到标题布局
|
||
header_layout.addWidget(self.logo_label)
|
||
header_layout.addWidget(self.title_label)
|
||
header_layout.addStretch()
|
||
header_layout.addWidget(self.version_label)
|
||
|
||
# 创建主标签控件
|
||
self.tab_widget = QtWidgets.QTabWidget()
|
||
self.tab_widget.setTabPosition(QtWidgets.QTabWidget.North)
|
||
self.tab_widget.setDocumentMode(True)
|
||
self.tab_widget.setMovable(True)
|
||
|
||
# 创建标签页控件
|
||
try:
|
||
self.dna_editor_widget = DNAEditorWidget(self)
|
||
self.calibration_widget = CalibrationWidget(self)
|
||
self.binding_widget = BindingWidget(self)
|
||
self.blendshape_widget = BlendShapeWidget(self)
|
||
|
||
# 添加标签页
|
||
self.tab_widget.addTab(self.dna_editor_widget, "DNA 编辑器")
|
||
self.tab_widget.addTab(self.calibration_widget, "骨骼校准")
|
||
self.tab_widget.addTab(self.binding_widget, "自定义绑定")
|
||
self.tab_widget.addTab(self.blendshape_widget, "BlendShape 编辑")
|
||
except Exception as e:
|
||
print(f"创建标签页控件出错: {str(e)}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
# 创建底部状态栏
|
||
self.status_bar = QtWidgets.QStatusBar()
|
||
self.status_bar.setObjectName("footerFrame")
|
||
self.setStatusBar(self.status_bar)
|
||
self.status_bar.showMessage(f"{config.TOOL_NAME} {config.TOOL_VERSION} 已加载")
|
||
|
||
# 创建菜单栏
|
||
self._create_menu_bar()
|
||
|
||
def _create_menu_bar(self):
|
||
"""Create menu bar for the main window"""
|
||
# Create menu bar
|
||
self.menu_bar = QtWidgets.QMenuBar()
|
||
self.setMenuBar(self.menu_bar)
|
||
|
||
# File menu
|
||
self.file_menu = self.menu_bar.addMenu("文件")
|
||
|
||
# Load DNA action
|
||
self.load_dna_action = QtWidgets.QAction("加载 DNA 文件", self)
|
||
self.load_dna_action.setIcon(QtGui.QIcon(os.path.join(config.ICONS_PATH, "open.png")))
|
||
self.file_menu.addAction(self.load_dna_action)
|
||
|
||
# Save DNA action
|
||
self.save_dna_action = QtWidgets.QAction("保存 DNA 文件", self)
|
||
self.save_dna_action.setIcon(QtGui.QIcon(os.path.join(config.ICONS_PATH, "save.png")))
|
||
self.file_menu.addAction(self.save_dna_action)
|
||
|
||
self.file_menu.addSeparator()
|
||
|
||
# Export FBX action
|
||
self.export_fbx_action = QtWidgets.QAction("导出 FBX", self)
|
||
self.export_fbx_action.setIcon(QtGui.QIcon(os.path.join(config.ICONS_PATH, "export.png")))
|
||
self.file_menu.addAction(self.export_fbx_action)
|
||
|
||
self.file_menu.addSeparator()
|
||
|
||
# Exit action
|
||
self.exit_action = QtWidgets.QAction("退出", self)
|
||
self.exit_action.setIcon(QtGui.QIcon(os.path.join(config.ICONS_PATH, "exit.png")))
|
||
self.file_menu.addAction(self.exit_action)
|
||
|
||
# Tools menu
|
||
self.tools_menu = self.menu_bar.addMenu("工具")
|
||
|
||
# Create MetaHuman Rig action
|
||
self.create_rig_action = QtWidgets.QAction("创建 MetaHuman 骨架", self)
|
||
self.tools_menu.addAction(self.create_rig_action)
|
||
|
||
# Batch Export BlendShapes action
|
||
self.batch_export_blendshapes_action = QtWidgets.QAction("批量导出 BlendShape", self)
|
||
self.tools_menu.addAction(self.batch_export_blendshapes_action)
|
||
|
||
# Settings menu
|
||
self.settings_menu = self.menu_bar.addMenu("设置")
|
||
|
||
# Preferences action
|
||
self.preferences_action = QtWidgets.QAction("首选项", self)
|
||
self.preferences_action.setIcon(QtGui.QIcon(os.path.join(config.ICONS_PATH, "settings.png")))
|
||
self.settings_menu.addAction(self.preferences_action)
|
||
|
||
# Reset settings action
|
||
self.reset_settings_action = QtWidgets.QAction("重置设置", self)
|
||
self.reset_settings_action.setIcon(QtGui.QIcon(os.path.join(config.ICONS_PATH, "reset.png")))
|
||
self.settings_menu.addAction(self.reset_settings_action)
|
||
|
||
# Help menu
|
||
self.help_menu = self.menu_bar.addMenu("帮助")
|
||
|
||
# About action
|
||
self.about_action = QtWidgets.QAction("关于", self)
|
||
self.help_menu.addAction(self.about_action)
|
||
|
||
# Documentation action
|
||
self.documentation_action = QtWidgets.QAction("文档", self)
|
||
self.help_menu.addAction(self.documentation_action)
|
||
|
||
def _create_layouts(self):
|
||
"""Create layouts for the main window"""
|
||
# Create central widget
|
||
central_widget = QtWidgets.QWidget()
|
||
self.setCentralWidget(central_widget)
|
||
|
||
# Create main layout
|
||
main_layout = QtWidgets.QVBoxLayout(central_widget)
|
||
main_layout.setContentsMargins(5, 5, 5, 5)
|
||
main_layout.setSpacing(5)
|
||
|
||
# Add tab widget to main layout
|
||
main_layout.addWidget(self.tab_widget)
|
||
|
||
def _create_connections(self):
|
||
"""创建信号/槽连接"""
|
||
try:
|
||
# 文件菜单连接
|
||
self.load_dna_action.triggered.connect(self._on_load_dna)
|
||
self.save_dna_action.triggered.connect(self._on_save_dna)
|
||
self.export_fbx_action.triggered.connect(self._on_export_fbx)
|
||
self.exit_action.triggered.connect(self.close)
|
||
|
||
# 工具菜单连接
|
||
self.create_rig_action.triggered.connect(self._on_create_rig)
|
||
self.batch_export_blendshapes_action.triggered.connect(self._on_batch_export_blendshapes)
|
||
|
||
# 设置菜单连接
|
||
self.preferences_action.triggered.connect(self._on_preferences)
|
||
self.reset_settings_action.triggered.connect(self._on_reset_settings)
|
||
|
||
# Help menu connections
|
||
self.about_action.triggered.connect(self._on_about)
|
||
self.documentation_action.triggered.connect(self._on_documentation)
|
||
|
||
# Tab change connections
|
||
self.tab_widget.currentChanged.connect(self._on_tab_changed)
|
||
|
||
print("UI signal/slot connections created")
|
||
except Exception as e:
|
||
print(f"Error creating signal/slot connections: {str(e)}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def _load_stylesheet(self, stylesheet_name="dark_theme.qss"):
|
||
"""Load main window stylesheet"""
|
||
try:
|
||
# Try to load specified stylesheet from style directory
|
||
stylesheet_path = os.path.join(config.STYLE_PATH, stylesheet_name)
|
||
|
||
if os.path.exists(stylesheet_path):
|
||
with open(stylesheet_path, 'r', encoding='utf-8') as f:
|
||
stylesheet = f.read()
|
||
|
||
# Adjust stylesheet for Qt version
|
||
qt_version = None
|
||
if 'QT_VERSION' in globals():
|
||
qt_version = globals()['QT_VERSION']
|
||
|
||
if qt_version == "PySide6":
|
||
# Adjust stylesheet for PySide6
|
||
# Replace known incompatible selectors and properties
|
||
stylesheet = stylesheet.replace("::item:hover", ":item:hover")
|
||
stylesheet = stylesheet.replace("::item:selected", ":item:selected")
|
||
stylesheet = stylesheet.replace("QTabBar::tab:hover:!selected", "QTabBar::tab:hover")
|
||
|
||
# Add PySide6-specific selectors
|
||
stylesheet += "\nQTabBar::tab:selected:hover { background-color: #2D2D30; }\n"
|
||
|
||
self.setStyleSheet(stylesheet)
|
||
print(f"Stylesheet loaded: {stylesheet_path}")
|
||
|
||
# Update status bar message
|
||
if hasattr(self, "status_bar"):
|
||
theme_name = "Dark" if "dark" in stylesheet_name else "Light"
|
||
self.status_bar.showMessage(f"{theme_name} theme applied")
|
||
|
||
# If specified stylesheet does not exist, try to load default stylesheet
|
||
elif hasattr(config, 'TOOL_STYLE_FILE') and os.path.exists(config.TOOL_STYLE_FILE):
|
||
with open(config.TOOL_STYLE_FILE, 'r', encoding='utf-8') as f:
|
||
self.setStyleSheet(f.read())
|
||
print(f"Default stylesheet loaded: {config.TOOL_STYLE_FILE}")
|
||
else:
|
||
print("Stylesheet file not found, using built-in style")
|
||
except Exception as e:
|
||
print(f"Error loading stylesheet: {str(e)}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def _on_load_dna(self):
|
||
"""Handle load DNA action"""
|
||
file_path, _ = QtWidgets.QFileDialog.getOpenFileName(
|
||
self,
|
||
"Select DNA File",
|
||
config.DNA_FILE_PATH,
|
||
"DNA Files (*.dna);;All Files (*.*)"
|
||
)
|
||
|
||
if file_path:
|
||
# Import DNA utils here to avoid circular imports
|
||
from utils.dna_utils import DNAUtils
|
||
dna_utils = DNAUtils()
|
||
|
||
try:
|
||
result = dna_utils.load_dna(file_path)
|
||
if result:
|
||
self.status_bar.showMessage(f"DNA file loaded: {os.path.basename(file_path)}")
|
||
else:
|
||
self.status_bar.showMessage(f"Failed to load DNA file: {os.path.basename(file_path)}")
|
||
except Exception as e:
|
||
self.status_bar.showMessage(f"Error loading DNA file: {str(e)}")
|
||
|
||
def _on_save_dna(self):
|
||
"""Handle save DNA action"""
|
||
file_path, _ = QtWidgets.QFileDialog.getSaveFileName(
|
||
self,
|
||
"Save DNA File",
|
||
config.DNA_FILE_PATH,
|
||
"DNA Files (*.dna);;All Files (*.*)"
|
||
)
|
||
|
||
if file_path:
|
||
# Import DNA utils here to avoid circular imports
|
||
from utils.dna_utils import DNAUtils
|
||
dna_utils = DNAUtils()
|
||
|
||
try:
|
||
result = dna_utils.save_dna(file_path)
|
||
if result:
|
||
self.status_bar.showMessage(f"DNA file saved: {os.path.basename(file_path)}")
|
||
else:
|
||
self.status_bar.showMessage(f"Failed to save DNA file: {os.path.basename(file_path)}")
|
||
except Exception as e:
|
||
self.status_bar.showMessage(f"Error saving DNA file: {str(e)}")
|
||
|
||
def _on_export_fbx(self):
|
||
"""Handle export FBX action"""
|
||
file_path, _ = QtWidgets.QFileDialog.getSaveFileName(
|
||
self,
|
||
"Export FBX File",
|
||
"",
|
||
"FBX Files (*.fbx);;All Files (*.*)"
|
||
)
|
||
|
||
if file_path:
|
||
# Import Maya utils here to avoid circular imports
|
||
from utils.maya_utils import MayaUtils
|
||
maya_utils = MayaUtils()
|
||
|
||
try:
|
||
result = maya_utils.export_fbx(file_path)
|
||
if result:
|
||
self.status_bar.showMessage(f"FBX file exported: {os.path.basename(file_path)}")
|
||
else:
|
||
self.status_bar.showMessage(f"Failed to export FBX file: {os.path.basename(file_path)}")
|
||
except Exception as e:
|
||
self.status_bar.showMessage(f"Error exporting FBX file: {str(e)}")
|
||
|
||
def _on_create_rig(self):
|
||
"""Handle create MetaHuman rig action"""
|
||
# Import joint utils here to avoid circular imports
|
||
from utils.joint_utils import JointUtils
|
||
joint_utils = JointUtils()
|
||
|
||
try:
|
||
# Get selected mesh
|
||
selection = cmds.ls(selection=True)
|
||
if not selection:
|
||
QtWidgets.QMessageBox.warning(
|
||
self,
|
||
"Warning",
|
||
"Please select a mesh first"
|
||
)
|
||
return
|
||
|
||
mesh = selection[0]
|
||
|
||
# Create rig
|
||
result = joint_utils.create_metahuman_skeleton(mesh)
|
||
if result:
|
||
self.status_bar.showMessage(f"MetaHuman skeleton created for {mesh}")
|
||
else:
|
||
self.status_bar.showMessage(f"Failed to create MetaHuman skeleton for {mesh}")
|
||
except Exception as e:
|
||
self.status_bar.showMessage(f"Error creating MetaHuman skeleton: {str(e)}")
|
||
|
||
def _on_batch_export_blendshapes(self):
|
||
"""Handle batch export blendshapes action"""
|
||
dir_path = QtWidgets.QFileDialog.getExistingDirectory(
|
||
self,
|
||
"Select Export Directory",
|
||
""
|
||
)
|
||
|
||
if dir_path:
|
||
# Import blendshape utils here to avoid circular imports
|
||
from utils.blendshape_utils import BlendShapeUtils
|
||
blendshape_utils = BlendShapeUtils()
|
||
|
||
try:
|
||
result = blendshape_utils.batch_export(dir_path)
|
||
if result:
|
||
self.status_bar.showMessage(f"Blendshapes exported to: {dir_path}")
|
||
else:
|
||
self.status_bar.showMessage(f"Failed to export blendshapes to: {dir_path}")
|
||
except Exception as e:
|
||
self.status_bar.showMessage(f"Error exporting blendshapes: {str(e)}")
|
||
|
||
def _on_about(self):
|
||
"""Handle about action"""
|
||
try:
|
||
# Get Maya and Python version information
|
||
import maya.cmds as cmds
|
||
import platform
|
||
maya_version = cmds.about(version=True)
|
||
python_version = platform.python_version()
|
||
os_name = platform.system()
|
||
|
||
about_text = f"""
|
||
<h2>{config.TOOL_NAME} {config.TOOL_VERSION}</h2>
|
||
<p><b>Author:</b> {config.TOOL_AUTHOR}</p>
|
||
<p><b>Maya Version:</b> {maya_version}</p>
|
||
<p><b>Python Version:</b> {python_version}</p>
|
||
<p><b>Operating System:</b> {os_name}</p>
|
||
<p><b>Description:</b> A Maya plugin for working with MetaHuman models, providing DNA editing, skeleton calibration, custom binding, and blendshape editing features.</p>
|
||
<p><b>Copyright 2025</b></p>
|
||
"""
|
||
|
||
# Create custom about dialog
|
||
about_dialog = QtWidgets.QDialog(self)
|
||
about_dialog.setWindowTitle(f"About {config.TOOL_NAME}")
|
||
about_dialog.setMinimumSize(400, 300)
|
||
about_dialog.setWindowFlags(about_dialog.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint)
|
||
|
||
# Create layout
|
||
layout = QtWidgets.QVBoxLayout(about_dialog)
|
||
|
||
# Add logo
|
||
logo_label = QtWidgets.QLabel()
|
||
logo_label.setAlignment(QtCore.Qt.AlignCenter)
|
||
logo_path = os.path.join(config.ICONS_PATH, "Logo.png")
|
||
if os.path.exists(logo_path):
|
||
logo_pixmap = QtGui.QPixmap(logo_path).scaled(100, 100, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
|
||
logo_label.setPixmap(logo_pixmap)
|
||
layout.addWidget(logo_label)
|
||
|
||
# Add text information
|
||
info_label = QtWidgets.QLabel()
|
||
info_label.setText(about_text)
|
||
info_label.setTextFormat(QtCore.Qt.RichText)
|
||
info_label.setWordWrap(True)
|
||
info_label.setAlignment(QtCore.Qt.AlignCenter)
|
||
layout.addWidget(info_label)
|
||
|
||
# Add OK button
|
||
button_box = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok)
|
||
button_box.accepted.connect(about_dialog.accept)
|
||
layout.addWidget(button_box)
|
||
|
||
# Show dialog
|
||
about_dialog.exec_()
|
||
except Exception as e:
|
||
print(f"Error showing about dialog: {str(e)}")
|
||
# Fall back to simple message box
|
||
QtWidgets.QMessageBox.about(
|
||
self,
|
||
f"About {config.TOOL_NAME}",
|
||
f"{config.TOOL_NAME} {config.TOOL_VERSION}\nAuthor: {config.TOOL_AUTHOR}"
|
||
)
|
||
|
||
def _on_documentation(self):
|
||
"""Handle documentation action"""
|
||
import webbrowser
|
||
webbrowser.open("https://epicgames.github.io/MetaHuman-DNA-Calibration/index.html")
|
||
|
||
def _on_tab_changed(self, index):
|
||
"""Handle tab change"""
|
||
tab_name = self.tab_widget.tabText(index)
|
||
self.status_bar.showMessage(f"Current module: {tab_name}")
|
||
|
||
def _on_preferences(self):
|
||
"""Handle preferences action"""
|
||
try:
|
||
# Create settings dialog
|
||
settings_dialog = SettingsDialog(self)
|
||
settings_dialog.exec_()
|
||
except Exception as e:
|
||
print(f"Error opening settings dialog: {str(e)}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def _on_reset_settings(self):
|
||
"""Handle reset settings action"""
|
||
try:
|
||
# Ask user if they want to 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()
|
||
|
||
# Apply settings
|
||
self.apply_settings(self.settings)
|
||
|
||
# Show confirmation message
|
||
self.status_bar.showMessage("All settings have been reset to default values")
|
||
except Exception as e:
|
||
print(f"Error resetting settings: {str(e)}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def apply_settings(self, settings):
|
||
"""Apply settings"""
|
||
try:
|
||
self.settings = settings
|
||
|
||
# Apply UI settings
|
||
font_size = settings["ui"]["font_size"]
|
||
font = self.font()
|
||
font.setPointSize(font_size)
|
||
self.setFont(font)
|
||
|
||
# Apply theme
|
||
theme = settings["ui"]["theme"]
|
||
if theme == "dark":
|
||
self._load_stylesheet("dark_theme.qss")
|
||
else:
|
||
self._load_stylesheet("light_theme.qss")
|
||
|
||
# Apply exit confirmation setting
|
||
self.confirm_on_exit = settings["ui"]["confirm_on_exit"]
|
||
|
||
# Apply tooltip settings
|
||
QtWidgets.QToolTip.setEnabled(settings["ui"]["show_tooltips"])
|
||
|
||
# Update status bar message
|
||
self.status_bar.showMessage("Settings applied")
|
||
|
||
print("Settings applied")
|
||
except Exception as e:
|
||
print(f"Error applying settings: {str(e)}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def closeEvent(self, event):
|
||
"""Handle close event"""
|
||
try:
|
||
# Check if exit confirmation is needed
|
||
if hasattr(self, "confirm_on_exit") and self.confirm_on_exit:
|
||
# Ask user if they want to exit
|
||
reply = QtWidgets.QMessageBox.question(
|
||
self,
|
||
"Confirm Exit",
|
||
f"Are you sure you want to exit {config.TOOL_NAME}?",
|
||
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
|
||
QtWidgets.QMessageBox.No
|
||
)
|
||
|
||
if reply == QtWidgets.QMessageBox.Yes:
|
||
# Save settings (if needed)
|
||
print(f"{config.TOOL_NAME} 正在关闭...")
|
||
event.accept()
|
||
else:
|
||
event.ignore()
|
||
else:
|
||
# No confirmation needed, close directly
|
||
print(f"{config.TOOL_NAME} 正在关闭...")
|
||
event.accept()
|
||
except Exception as e:
|
||
print(f"Error handling close event: {str(e)}")
|
||
event.accept()
|