更新版本

This commit is contained in:
2026-06-12 16:01:53 +08:00
parent 62ebc2f589
commit d17a54eb73
23 changed files with 8448 additions and 52 deletions
+30 -2
View File
@@ -1,6 +1,34 @@
__pycache__/
*.pyc
*.pyo
*.pyd
.Python
*.so
*.egg
*.egg-info/
dist/
build/
*.spec
/output
.venv/
venv/
env/
.env
.env.local
.idea/
.vscode/
*.log
*.swp
*.swo
*~
.DS_Store
.pytest_cache/
.coverage
htmlcov/
.ipynb_checkpoints/
*.manifest
*.spec
config.json
*.qm
*.ts
*.trae/
-18
View File
@@ -1,18 +0,0 @@
MIT License
Copyright (c) 2026 feifei.xu
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
-1
View File
@@ -1,2 +1 @@
# Model_Team_Tools
+72
View File
@@ -0,0 +1,72 @@
import os
import sys
import glob
import platform
import subprocess
from pathlib import Path
from zipfile import ZIP_DEFLATED, ZipFile
from main import APP_NAME, APP_VERSION
COMPILER = "mingw64"
OUTPUT_PATH = Path('output')
RESOURCES_PATH = Path("resources")
RELEASE_PATH = OUTPUT_PATH / APP_NAME
BUILD_PATH = OUTPUT_PATH / f'{platform.system().lower()}-{platform.machine().lower()}'
def build_main():
nuitka_cmd = [
'python',
'-m',
'nuitka',
'--standalone',
'--show-memory',
'--show-progress',
'--plugin-enable=pyside6',
'--include-module=qt_material',
f"--include-data-dir={RESOURCES_PATH}=resources",
]
icon_path = str(RESOURCES_PATH / "main.ico")
if os.path.exists(icon_path):
nuitka_cmd.extend([
f'--windows-icon-from-ico={icon_path}',
])
nuitka_cmd.append(f'--output-dir={BUILD_PATH}')
if platform.system() == 'Windows':
nuitka_cmd.append('--windows-console-mode=disable')
nuitka_cmd.append(f'--{COMPILER}')
nuitka_cmd.append('./main.py')
result = subprocess.run(nuitka_cmd,shell=True)
if result.returncode != 0:
raise RuntimeError('Nuitka building failed.')
print('Building done.')
def create_zip():
file_list = glob.glob(f'{BUILD_PATH / APP_NAME / "dist"}', recursive=True)
file_list.sort()
if not os.path.exists(RELEASE_PATH):
os.mkdir(RELEASE_PATH)
portable_file = RELEASE_PATH / f'{APP_NAME}-{platform.system()}-{platform.machine()}-{APP_VERSION}.zip'
print('Creating portable package...')
with ZipFile(portable_file, 'w', compression=ZIP_DEFLATED) as zf:
for file in file_list:
file = Path(file)
name_in_zip = f'{APP_NAME}/{"/".join(file.parts[3:])}'
print(name_in_zip)
zf.write(file, name_in_zip)
print('Creating portable package done.')
if __name__ == '__main__':
build_main()
+18
View File
@@ -0,0 +1,18 @@
"""
Core 模块 - 核心功能模块包
本包包含应用程序的核心功能组件:
- 日志系统(logging 包)
- 工具箱系统(tools 包)
提供统一的接口导出,方便其他模块调用。
Author: Model Team
Version: 0.0.1
"""
from .logging.log import FrameLog
from .tools.tools import ToolsWindow # FromTools → ToolsWindow
__all__ = ["FrameLog", "ToolsWindow"]
+209
View File
@@ -0,0 +1,209 @@
"""
日志模块 - 提供统一的日志记录功能
功能说明:
- 日志写入文件 (logs/log_file.log)
- 日志输出到控制台
- 日志显示到 UI 的 TextEdit 组件
架构设计:
- 使用组合模式避免 PySide6 QObject.emit() 方法冲突
- LogHandler: 独立的日志处理器,继承自 logging.Handler
- FrameLog: UI 组件,负责日志配置和显示
日志格式:%(asctime)s [%(levelname)s] %(message)s
时间格式:%Y-%m-%d %H:%M:%S
"""
import logging
from pathlib import Path
from typing import Callable, Optional
from PySide6.QtCore import QObject, Signal
from PySide6.QtWidgets import QFrame, QTextEdit
from .log_ui import Ui_FrameLog
# 日志配置常量
LOG_FILE = "logs/log_file.log" # 日志文件路径
CONSOLE_OUTPUT = True # 是否输出到控制台
class LogHandler(logging.Handler):
"""
独立的日志处理器
继承自 logging.Handler,用于处理日志记录的格式化输出
使用回调函数机制避免与 PySide6 QObject.emit() 方法名冲突
设计原因:
- logging.Handler.emit() 和 QObject.emit() 方法签名冲突
- 使用组合模式,将回调函数作为日志传递的桥梁
"""
def __init__(self, callback: Callable[[str], None]) -> None:
"""
初始化日志处理器
Args:
callback: 回调函数,接收格式化后的日志消息字符串
"""
super().__init__()
self.callback = callback
def emit(self, record: logging.LogRecord) -> None:
"""
处理日志记录
当日志系统调用此方法时,将格式化后的日志消息传递给回调函数
Args:
record: 日志记录对象,包含级别、消息等信息
异常处理:
- RecursionError: 防止递归调用导致无限循环
- 其他异常: 使用 handleError 记录错误
"""
try:
msg = self.format(record)
self.callback(msg)
except RecursionError:
self.handleError(record)
except Exception:
self.handleError(record)
class FrameLog(QFrame, Ui_FrameLog):
"""
日志显示框架组件
继承自 QFrameQt 框架组件)和 Ui_FrameLog(自动生成的 UI 界面)
负责日志系统的配置和 UI 显示功能
功能:
- 初始化日志系统(文件、控制台、UI)
- 将日志消息显示到 TextEdit 组件
- 通过信号机制实现线程安全的 UI 更新
"""
# PySide6 信号定义,用于跨线程传递日志消息
log_signal = Signal(str)
def __init__(self, parent: Optional[QObject] = None) -> None:
"""
初始化日志框架
Args:
parent: 父组件对象,默认为 None
"""
QFrame.__init__(self, parent)
self.setupUi(self)
self._init_logging()
self._connect_signal()
def _init_logging(self) -> None:
"""
初始化日志系统
配置日志系统的各个组件:
- 设置日志级别为 DEBUG
- 配置日志格式化器
- 设置文件日志处理器(可选)
- 设置控制台日志处理器(可选)
- 配置 UI 日志处理器
"""
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
# 创建日志格式化器:时间 [级别] 消息
formatter = logging.Formatter(
"%(asctime)s [%(levelname)s] %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
# 配置文件日志处理器(如果启用)
if LOG_FILE:
self._setup_file_handler(logger, formatter)
# 配置控制台日志处理器(如果启用)
if CONSOLE_OUTPUT:
self._setup_console_handler(logger, formatter)
# 配置 UI 日志处理器
self._log_handler = LogHandler(self._update_log_display)
self._log_handler.setFormatter(formatter)
self._log_handler.setLevel(logging.DEBUG)
logger.addHandler(self._log_handler)
def _setup_file_handler(self, logger: logging.Logger, formatter: logging.Formatter) -> None:
"""
配置日志文件处理器
将日志写入指定文件,支持 UTF-8 编码
Args:
logger: 日志记录器实例
formatter: 日志格式化器
异常处理:
- OSError: 操作系统错误(如磁盘满)
- PermissionError: 权限不足
- 失败时降级到控制台输出
"""
try:
log_path = Path(LOG_FILE)
log_path.parent.mkdir(parents=True, exist_ok=True)
file_handler = logging.FileHandler(log_path, encoding='utf-8')
file_handler.setFormatter(formatter)
file_handler.setLevel(logging.DEBUG)
logger.addHandler(file_handler)
except (OSError, PermissionError) as e:
logging.warning(f"无法创建日志文件 {LOG_FILE}: {e}")
logging.warning("日志将仅输出到控制台")
def _setup_console_handler(self, logger: logging.Logger, formatter: logging.Formatter) -> None:
"""
配置日志控制台处理器
将日志输出到标准控制台(stdout)
Args:
logger: 日志记录器实例
formatter: 日志格式化器
注意:
- 控制台输出级别设置为 INFO,只显示重要信息
- DEBUG 级别的日志不会输出到控制台
"""
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
console_handler.setLevel(logging.INFO)
logger.addHandler(console_handler)
def _connect_signal(self) -> None:
"""
连接日志信号到 UI 更新方法
将 log_signal 信号连接到 _update_log_display 方法
实现日志消息的线程安全传递
"""
self.log_signal.connect(self._update_log_display)
def _update_log_display(self, message: str) -> None:
"""
更新 UI 的日志显示组件
将日志消息追加到 TextEdit 组件中显示
Args:
message: 格式化后的日志消息字符串
实现细节:
- 使用 findChild 查找 TextEdit 组件
- 使用 append 方法追加日志消息,自动换行
"""
self.textEdit.append(message)
# text_edit = self.findChild(QTextEdit, "textEdit")
# if text_edit:
# text_edit.append(message)
+24
View File
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FrameLog</class>
<widget class="QFrame" name="FrameLog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>982</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Frame</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QTextEdit" name="textEdit"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+42
View File
@@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'log.ui'
##
## Created by: Qt User Interface Compiler version 6.8.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QFrame, QGridLayout, QSizePolicy,
QTextEdit, QWidget)
class Ui_FrameLog(object):
def setupUi(self, FrameLog):
if not FrameLog.objectName():
FrameLog.setObjectName(u"FrameLog")
FrameLog.resize(982, 300)
self.gridLayout = QGridLayout(FrameLog)
self.gridLayout.setObjectName(u"gridLayout")
self.textEdit = QTextEdit(FrameLog)
self.textEdit.setObjectName(u"textEdit")
self.gridLayout.addWidget(self.textEdit, 0, 0, 1, 1)
self.retranslateUi(FrameLog)
QMetaObject.connectSlotsByName(FrameLog)
# setupUi
def retranslateUi(self, FrameLog):
FrameLog.setWindowTitle(QCoreApplication.translate("FrameLog", u"Frame", None))
# retranslateUi
+15
View File
@@ -0,0 +1,15 @@
from PySide6.QtWidgets import QFrame
from .tool_card_ui import Ui_FrameToolCard
class ToolCard(QFrame, Ui_FrameToolCard):
"""工具卡片类
继承自 QFrameQt 帧基类)和 Ui_FrameToolCardQt Designer 生成的 UI 界面)
负责显示工具的卡片界面,包含工具的图标、名称和描述。
"""
def __init__(self, parent = None) -> None:
super().__init__(parent)
self.setupUi(self)
+90
View File
@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FrameToolCard</class>
<widget class="QFrame" name="FrameToolCard">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>491</width>
<height>64</height>
</rect>
</property>
<property name="windowTitle">
<string>Frame</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="labelName">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="pushButtonInstall">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>安装</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="pushButtonUninstall">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>卸载</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="pushButtonUpdate">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>更新</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelInfo">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+91
View File
@@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'tool_card.ui'
##
## Created by: Qt User Interface Compiler version 6.11.1
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QFrame, QGridLayout, QLabel,
QPushButton, QSizePolicy, QWidget)
class Ui_FrameToolCard(object):
def setupUi(self, FrameToolCard):
if not FrameToolCard.objectName():
FrameToolCard.setObjectName(u"FrameToolCard")
FrameToolCard.resize(491, 64)
self.gridLayout_2 = QGridLayout(FrameToolCard)
self.gridLayout_2.setObjectName(u"gridLayout_2")
self.gridLayout = QGridLayout()
self.gridLayout.setObjectName(u"gridLayout")
self.labelName = QLabel(FrameToolCard)
self.labelName.setObjectName(u"labelName")
sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.labelName.sizePolicy().hasHeightForWidth())
self.labelName.setSizePolicy(sizePolicy)
self.gridLayout.addWidget(self.labelName, 0, 0, 1, 1)
self.pushButtonInstall = QPushButton(FrameToolCard)
self.pushButtonInstall.setObjectName(u"pushButtonInstall")
sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
sizePolicy1.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.pushButtonInstall.sizePolicy().hasHeightForWidth())
self.pushButtonInstall.setSizePolicy(sizePolicy1)
self.gridLayout.addWidget(self.pushButtonInstall, 0, 1, 1, 1)
self.pushButtonUninstall = QPushButton(FrameToolCard)
self.pushButtonUninstall.setObjectName(u"pushButtonUninstall")
sizePolicy1.setHeightForWidth(self.pushButtonUninstall.sizePolicy().hasHeightForWidth())
self.pushButtonUninstall.setSizePolicy(sizePolicy1)
self.gridLayout.addWidget(self.pushButtonUninstall, 0, 2, 1, 1)
self.pushButtonUpdate = QPushButton(FrameToolCard)
self.pushButtonUpdate.setObjectName(u"pushButtonUpdate")
sizePolicy1.setHeightForWidth(self.pushButtonUpdate.sizePolicy().hasHeightForWidth())
self.pushButtonUpdate.setSizePolicy(sizePolicy1)
self.gridLayout.addWidget(self.pushButtonUpdate, 0, 3, 1, 1)
self.gridLayout_2.addLayout(self.gridLayout, 0, 0, 1, 1)
self.labelInfo = QLabel(FrameToolCard)
self.labelInfo.setObjectName(u"labelInfo")
sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding)
sizePolicy2.setHorizontalStretch(0)
sizePolicy2.setVerticalStretch(0)
sizePolicy2.setHeightForWidth(self.labelInfo.sizePolicy().hasHeightForWidth())
self.labelInfo.setSizePolicy(sizePolicy2)
self.gridLayout_2.addWidget(self.labelInfo, 1, 0, 1, 1)
self.retranslateUi(FrameToolCard)
QMetaObject.connectSlotsByName(FrameToolCard)
# setupUi
def retranslateUi(self, FrameToolCard):
FrameToolCard.setWindowTitle(QCoreApplication.translate("FrameToolCard", u"Frame", None))
self.labelName.setText(QCoreApplication.translate("FrameToolCard", u"Name", None))
self.pushButtonInstall.setText(QCoreApplication.translate("FrameToolCard", u"\u5b89\u88c5", None))
self.pushButtonUninstall.setText(QCoreApplication.translate("FrameToolCard", u"\u5378\u8f7d", None))
self.pushButtonUpdate.setText(QCoreApplication.translate("FrameToolCard", u"\u66f4\u65b0", None))
self.labelInfo.setText(QCoreApplication.translate("FrameToolCard", u"TextLabel", None))
# retranslateUi
+86
View File
@@ -0,0 +1,86 @@
"""
工具箱模块
提供工具箱主窗口,包含各种实用工具的入口。
使用 PySide6 构建的窗口框架。
主要功能:
- 工具箱主窗口界面
- 窗口居中显示功能
- 工具分类和导航
Author: Model Team
Version: 0.0.1
"""
import logging
from qt_material import apply_stylesheet
from pathlib import Path
from .tools_ui import Ui_FormTools
from PySide6.QtGui import QIcon
from PySide6.QtWidgets import QWidget, QApplication
logger = logging.getLogger(__name__)
PLUGINS_PATH = Path('Y:/SE/xufeifei/plugins')
class ToolsWindow(QWidget, Ui_FormTools):
"""工具箱主窗口类
继承自 QWidgetQt 窗口基类)和 Ui_FormToolsQt Designer 生成的 UI 界面)
负责显示工具箱的主界面,提供工具的分类和导航功能。
窗口特性:
- 默认尺寸: 400x293 像素
- 支持窗口居中显示
- 可作为独立窗口或嵌入其他窗口使用
Attributes:
parent: 父窗口对象,默认为 None(顶级窗口)
"""
def __init__(self, parent: QWidget = None) -> None:
"""初始化工具箱窗口
创建工具箱窗口实例,初始化 UI 界面。
Args:
parent: 父窗口对象,用于建立父子关系。
默认为 None,表示这是顶级窗口。
"""
super().__init__(parent)
self.setupUi(self)
self.initUI()
self.load_stylesheet()
def initUI(self) -> None:
"""
初始化用户界面
配置窗口标题、图标、大小和居中显示。
"""
self.setWindowTitle("工具箱")
self.setWindowIcon(QIcon('resources/tools.ico'))
def load_stylesheet(self) -> None:
"""
加载 Qt 样式表和自定义样式文件
使用 qt_material 库加载暗色主题,并应用自定义 QSS 样式表
失败时记录错误日志,但不影响应用继续运行(降级策略)
异常处理:
- FileNotFoundError: 样式文件不存在
- RuntimeError: 主题加载失败
- 其他异常: 记录警告但不中断应用
"""
try:
apply_stylesheet(self, style=None,theme='dark_teal.xml', css_file='resources/my.qss')
except FileNotFoundError as e:
logger.warning(f"样式文件未找到,使用默认主题: {e}")
except RuntimeError as e:
logger.warning(f"主题加载失败,使用默认主题: {e}")
except Exception as e:
logger.warning(f"样式加载失败,使用默认主题: {e}")
+30
View File
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FormTools</class>
<widget class="QWidget" name="FormTools">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>810</width>
<height>423</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QListWidget" name="listWidget"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+47
View File
@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'tools.ui'
##
## Created by: Qt User Interface Compiler version 6.11.1
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QGridLayout, QListWidget, QListWidgetItem,
QSizePolicy, QWidget)
class Ui_FormTools(object):
def setupUi(self, FormTools):
if not FormTools.objectName():
FormTools.setObjectName(u"FormTools")
FormTools.resize(810, 423)
sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(FormTools.sizePolicy().hasHeightForWidth())
FormTools.setSizePolicy(sizePolicy)
self.gridLayout = QGridLayout(FormTools)
self.gridLayout.setObjectName(u"gridLayout")
self.listWidget = QListWidget(FormTools)
self.listWidget.setObjectName(u"listWidget")
self.gridLayout.addWidget(self.listWidget, 0, 0, 1, 1)
self.retranslateUi(FormTools)
QMetaObject.connectSlotsByName(FormTools)
# setupUi
def retranslateUi(self, FormTools):
FormTools.setWindowTitle(QCoreApplication.translate("FormTools", u"Form", None))
# retranslateUi
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

+106 -11
View File
@@ -1,27 +1,122 @@
"""
Model Team Tools - 主程序入口
应用程序主窗口的初始化和配置模块
使用 PySide6 构建的桌面应用程序框架
主要功能:
- 初始化 Qt 主窗口
- 加载 Qt Material 样式表
- 配置菜单栏和状态栏
- 提供统一的日志记录接口
"""
import sys
import importlib
import logging
import core
import traceback
from PySide6.QtGui import *
from PySide6.QtCore import *
from PySide6.QtWidgets import *
from pathlib import Path
from typing import Optional
from PySide6.QtGui import QIcon
from PySide6.QtWidgets import QApplication, QMainWindow, QMenuBar, QStatusBar,QFileDialog
from qt_material import apply_stylesheet
from main_ui import Ui_MainWindow
# 获取模块日志记录器
logger = logging.getLogger(__name__)
# 版本号常量
MAJOR_VER: int = 0 # 主版本号
MINOR_VER: int = 0 # 次版本号
MICRO_VER: int = 1 # 修订版本号
APP_NAME: str = "Model Team Tools"
APP_VERSION: str = f"{MAJOR_VER}.{MINOR_VER}.{MICRO_VER}"
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self):
"""
主窗口类
继承自 QMainWindowQt 主窗口基类)和 Ui_MainWindowQt Designer 生成的 UI 界面)
负责应用程序主窗口的初始化、样式配置和界面布局
"""
def __init__(self) -> None:
super().__init__()
self.setupUi(self)
self.initUI()
def initUI(self):
self.setWindowIcon(QIcon("main.ico"))
self.setWindowTitle("Model Team Tools")
def initUI(self) -> None:
"""
初始化用户界面组件
执行以下初始化操作:
- 加载 Qt 样式表和自定义样式
- 设置窗口标题
- 配置菜单栏
- 配置状态栏
- 记录初始化日志
注意:此方法在主窗口创建后调用
"""
self.load_stylesheet()
self.setWindowTitle(APP_NAME)
self.setWindowIcon(QIcon('resources/main.ico'))
self.menubar: Optional[QMenuBar] = self.menuBar()
self.statusbar: QStatusBar = self.statusBar()
self.statusbar.showMessage(f"Version: {APP_VERSION}")
self.action_tools.triggered.connect(self.on_tools)
def load_stylesheet(self) -> None:
"""
加载 Qt 样式表和自定义样式文件
使用 qt_material 库加载暗色主题,并应用自定义 QSS 样式表
失败时记录错误日志,但不影响应用继续运行(降级策略)
异常处理:
- FileNotFoundError: 样式文件不存在
- RuntimeError: 主题加载失败
- 其他异常: 记录警告但不中断应用
"""
try:
apply_stylesheet(self, style=None,theme='dark_teal.xml', css_file='resources/my.qss')
except FileNotFoundError as e:
logger.warning(f"样式文件未找到,使用默认主题: {e}")
except RuntimeError as e:
logger.warning(f"主题加载失败,使用默认主题: {e}")
except Exception as e:
logger.warning(f"样式加载失败,使用默认主题: {e}")
def on_tools(self) -> None:
"""打开工具箱窗口
创建并显示工具箱窗口实例。包含完整的错误处理机制,
确保任何异常都不会导致主程序崩溃。
异常处理:
- 捕获所有异常并记录到日志
- 显示详细的错误堆栈信息
- 不影响主窗口的正常使用
日志记录:
- 成功打开工具箱时记录 INFO 级别日志
- 打开失败时记录 ERROR 级别日志
"""
try:
self.tools = core.ToolsWindow()
self.tools.show()
logger.info("工具箱窗口已打开")
except Exception as e:
logger.error(f"无法打开工具箱窗口: {e}")
logger.error(traceback.format_exc())
if __name__ == '__main__':
app = QApplication([])
main = MainWindow()
app = QApplication(sys.argv)
main: MainWindow = MainWindow()
main.show()
sys.exit(app.exec())
+51 -7
View File
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
<width>1200</width>
<height>900</height>
</rect>
</property>
<property name="windowTitle">
@@ -18,6 +18,28 @@
<item row="0" column="0">
<widget class="QStackedWidget" name="stackedWidget"/>
</item>
<item row="1" column="0">
<widget class="FrameLog" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>250</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
@@ -25,25 +47,47 @@
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<width>1200</width>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menu">
<property name="title">
<string>File</string>
<string>文件</string>
</property>
<addaction name="actionconfigs"/>
<addaction name="action_tools"/>
</widget>
<addaction name="menu"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionconfigs">
<widget class="QToolBar" name="toolBar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<action name="action_tools">
<property name="text">
<string>Config</string>
<string>工具箱</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>FrameLog</class>
<extends>QFrame</extends>
<header location="global">core.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
+31 -11
View File
@@ -3,7 +3,7 @@
################################################################################
## Form generated from reading UI file 'main.ui'
##
## Created by: Qt User Interface Compiler version 6.8.2
## Created by: Qt User Interface Compiler version 6.11.1
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
@@ -16,17 +16,20 @@ from PySide6.QtGui import (QAction, QBrush, QColor, QConicalGradient,
QIcon, QImage, QKeySequence, QLinearGradient,
QPainter, QPalette, QPixmap, QRadialGradient,
QTransform)
from PySide6.QtWidgets import (QApplication, QGridLayout, QMainWindow, QMenu,
QMenuBar, QSizePolicy, QStackedWidget, QStatusBar,
QWidget)
from PySide6.QtWidgets import (QApplication, QFrame, QGridLayout, QMainWindow,
QMenu, QMenuBar, QSizePolicy, QStackedWidget,
QStatusBar, QToolBar, QWidget)
from core import FrameLog
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
if not MainWindow.objectName():
MainWindow.setObjectName(u"MainWindow")
MainWindow.resize(800, 600)
self.actionconfigs = QAction(MainWindow)
self.actionconfigs.setObjectName(u"actionconfigs")
MainWindow.resize(1200, 900)
self.action_tools = QAction(MainWindow)
self.action_tools.setObjectName(u"action_tools")
self.action_tools.setMenuRole(QAction.MenuRole.NoRole)
self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName(u"centralwidget")
self.gridLayout = QGridLayout(self.centralwidget)
@@ -36,19 +39,35 @@ class Ui_MainWindow(object):
self.gridLayout.addWidget(self.stackedWidget, 0, 0, 1, 1)
self.frame = FrameLog(self.centralwidget)
self.frame.setObjectName(u"frame")
sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.frame.sizePolicy().hasHeightForWidth())
self.frame.setSizePolicy(sizePolicy)
self.frame.setMaximumSize(QSize(16777215, 250))
self.frame.setFrameShape(QFrame.Shape.StyledPanel)
self.frame.setFrameShadow(QFrame.Shadow.Raised)
self.gridLayout.addWidget(self.frame, 1, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QMenuBar(MainWindow)
self.menubar.setObjectName(u"menubar")
self.menubar.setGeometry(QRect(0, 0, 800, 21))
self.menubar.setGeometry(QRect(0, 0, 1200, 21))
self.menu = QMenu(self.menubar)
self.menu.setObjectName(u"menu")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QStatusBar(MainWindow)
self.statusbar.setObjectName(u"statusbar")
MainWindow.setStatusBar(self.statusbar)
self.toolBar = QToolBar(MainWindow)
self.toolBar.setObjectName(u"toolBar")
MainWindow.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.toolBar)
self.menubar.addAction(self.menu.menuAction())
self.menu.addAction(self.actionconfigs)
self.menu.addAction(self.action_tools)
self.retranslateUi(MainWindow)
@@ -57,7 +76,8 @@ class Ui_MainWindow(object):
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None))
self.actionconfigs.setText(QCoreApplication.translate("MainWindow", u"Config", None))
self.menu.setTitle(QCoreApplication.translate("MainWindow", u"File", None))
self.action_tools.setText(QCoreApplication.translate("MainWindow", u"\u5de5\u5177\u7bb1", None))
self.menu.setTitle(QCoreApplication.translate("MainWindow", u"\u6587\u4ef6", None))
self.toolBar.setWindowTitle(QCoreApplication.translate("MainWindow", u"toolBar", None))
# retranslateUi
File diff suppressed because it is too large Load Diff
+4
View File
@@ -0,0 +1,4 @@
PySide6>=6.0.0
Nuitka==2.6.6
qt_material>=0.1.1
logging>=0.1.0
Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

+19
View File
@@ -0,0 +1,19 @@
* {{
color: #ffffffff !important;
font: 16px "SimSun" !important;
text-transform: none;
}}
QPushButton {{
text-transform: none;
}}
QTabBar::tab {{
text-transform: none;
}}
QTextEdit {{
color: #ffffffff !important;
font: 16px "SimSun" !important;
text-transform: none;
}}
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB