合并达成度功能

This commit is contained in:
2025-05-20 18:30:00 +08:00
parent 8d063fd08a
commit 0a9bd74d8e
20 changed files with 14934 additions and 47 deletions

View File

@@ -1,6 +1,6 @@
from PySide6.QtGui import QDesktopServices
from PySide6.QtWidgets import QVBoxLayout
from qfluentwidgets import PrimaryPushSettingCard, FluentIcon, GroupHeaderCardWidget, PushButton
from PySide6.QtGui import QDesktopServices, Qt
from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout
from qfluentwidgets import PrimaryPushSettingCard, FluentIcon, GroupHeaderCardWidget, PushButton, ImageLabel, TitleLabel
from ui.components.widget import Widget
@@ -9,11 +9,19 @@ class AboutWidget(Widget):
def __init__(self, key: str, parent=None):
super().__init__(key, parent)
self.logoImage = ImageLabel(':/images/logo.png')
self.logoImage.scaledToHeight(100)
self.appNameLabel = TitleLabel('建工工具箱🛠️')
self.hBox = QHBoxLayout()
self.hBox.addWidget(self.logoImage, 0, Qt.AlignLeft)
self.hBox.addWidget(self.appNameLabel, 1, Qt.AlignLeft)
self.version_card = PrimaryPushSettingCard(
text="获取源码",
icon=FluentIcon.INFO,
title="关于",
content="作者许方杰。当前版本1.0.0\n使用 GPLv3 开源协议,作者不对使用本软件造成的任何损失负责。"
content="作者许方杰。当前版本1.0.0\n本软件使用 GPLv3 开源协议进行分发,作者不对使用本软件造成的任何损失负责。"
)
self.button_list = [
PushButton("访问网站"),
@@ -31,6 +39,7 @@ class AboutWidget(Widget):
self.group_card.setTitle("第三方框架")
self.vbox = QVBoxLayout(self)
self.vbox.addLayout(self.hBox)
self.vbox.addWidget(self.version_card)
self.vbox.addWidget(self.group_card)
self.vbox.addStretch(1)

243
ui/pyui/achievement_ui.py Normal file
View File

@@ -0,0 +1,243 @@
import os
from functools import wraps
from typing import Callable, Literal
from PySide6.QtCore import Qt, Signal, QThread
from PySide6.QtWidgets import QVBoxLayout, QFileDialog, QHBoxLayout, QProgressBar
from qfluentwidgets import GroupHeaderCardWidget, FluentIcon, PushButton, LineEdit, IconWidget, InfoBarIcon, BodyLabel, \
PrimaryPushButton, SwitchButton, MessageBox, InfoBar, InfoBarPosition, IndeterminateProgressBar
from module.worker import ARGWorker
from ui.components.widget import Widget
class InputSettingCard(GroupHeaderCardWidget):
chooseSignal = Signal(str)
def __init__(self, parent=None):
super().__init__(parent)
self.setTitle("输入选项")
self.setBorderRadius(8)
self.chooseFileButton = PushButton("打开")
self.chooseFileButton.setFixedWidth(120)
self.inputGroup = self.addGroup(FluentIcon.DOCUMENT, "目标文件", "选择达成度计算表", self.chooseFileButton)
# ============================
self.chooseFileButton.clicked.connect(self.choose_file)
def choose_file(self):
file_path, _ = QFileDialog.getOpenFileName(self, "选择文件", "", "Excel 文件 (*.xlsm);")
if file_path:
self.inputGroup.setContent("已选择文件:" + file_path)
self.chooseSignal.emit(file_path)
class OutputSettingCard(GroupHeaderCardWidget):
updateSignal = Signal(str, str)
startSignal = Signal()
def __init__(self, parent=None):
super().__init__(parent)
self.setTitle("输出选项")
self.setBorderRadius(8)
self.chooseExportDirectoryButton = PushButton("选择")
self.exportFileNameLineEdit = LineEdit()
self.startButton = PrimaryPushButton(FluentIcon.PLAY_SOLID, "开始")
self.autoOpenSwitch = SwitchButton()
self.disableCompatibilityCheckSwitch = SwitchButton()
self.autoOpenSwitch.setChecked(True)
self.bottomLayout = QHBoxLayout()
self.hintIcon = IconWidget(InfoBarIcon.INFORMATION)
self.hintLabel = BodyLabel("点击开始按钮以开始生成 👉")
self.startButton.setEnabled(False)
# 设置底部工具栏布局
self.hintIcon.setFixedSize(16, 16)
self.bottomLayout.setSpacing(10)
self.bottomLayout.setContentsMargins(24, 15, 24, 20)
self.bottomLayout.addWidget(self.hintIcon, 0, Qt.AlignLeft)
self.bottomLayout.addWidget(self.hintLabel, 0, Qt.AlignLeft)
self.bottomLayout.addStretch(1)
self.bottomLayout.addWidget(self.startButton, 0, Qt.AlignRight)
self.bottomLayout.setAlignment(Qt.AlignVCenter)
self.chooseExportDirectoryButton.setFixedWidth(120)
self.exportFileNameLineEdit.setPlaceholderText("请输入文件名")
self.startButton.setFixedWidth(120)
self.exportFileNameLineEdit.setFixedWidth(360)
self.exportFileNameLineEdit.setPlaceholderText("输入导出文件名例如21工程管理1达成度报告")
self.dirGroup = self.addGroup(FluentIcon.FOLDER, "导出目录", "选择导出文件的目录",
self.chooseExportDirectoryButton)
self.fnGroup = self.addGroup(FluentIcon.DOCUMENT, "导出文件名", "输入导出文件名", self.exportFileNameLineEdit)
self.aoGroup = self.addGroup(FluentIcon.VIEW, "自动打开", "生成完成后自动打开文件", self.autoOpenSwitch)
self.ccGroup = self.addGroup(FluentIcon.DEVELOPER_TOOLS, "关闭兼容性检查",
"⚠ 注意:该功能为实验性内容,仅当你明确了解其影响并知道自己在做什么时,才建议关闭兼容性检查!",
self.disableCompatibilityCheckSwitch)
self.ccGroup.setSeparatorVisible(True)
self.vBoxLayout.addLayout(self.bottomLayout)
# =============================
self.chooseExportDirectoryButton.clicked.connect(self.choose_dir)
self.startButton.clicked.connect(self.startSignal)
def choose_dir(self) -> None:
dir_path = QFileDialog.getExistingDirectory(self, "选择文件夹", "")
if dir_path:
self.dirGroup.setContent(f"当前保存的文件目录:{dir_path}")
self.updateSignal.emit('d', dir_path)
def update_path_and_filename_by_input_path(self, path: str):
dir_path = path[:path.rfind("/")]
self.dirGroup.setContent(f"已选择目录:{dir_path}")
self.updateSignal.emit('d', dir_path)
file_name = path[path.rfind("/") + 1:path.rfind(".")] + "-达成度报告"
self.exportFileNameLineEdit.setText(file_name)
self.updateSignal.emit('f', file_name)
class AchievementWidget(Widget):
error = Signal(str, str)
def __init__(self, key: str, parent=None):
super().__init__(key, parent)
self.inputGroup = InputSettingCard(self)
self.outputGroup = OutputSettingCard(self)
self.vbox = QVBoxLayout(self)
self.vbox.addWidget(self.inputGroup)
self.vbox.addWidget(self.outputGroup)
self.vbox.addStretch(1)
# =================================
self.infoBar = None
# =================================
self.thread = None
self.worker = None
# ==================================
self.input_file_path = ""
self.output_file_path = ""
self.output_file_name = ""
# ==================================
self.inputGroup.chooseSignal.connect(self.input_signal_receive)
self.outputGroup.updateSignal.connect(self.export_signal_receive)
self.outputGroup.startSignal.connect(self.start_generate)
def enable_start_check(func: Callable):
@wraps(func)
def wrapper(self, *args, **kwargs):
result = func(self, *args, **kwargs)
fields = [
self.input_file_path,
self.output_file_path,
self.output_file_name,
]
if all(fields):
self.outputGroup.startButton.setEnabled(True)
else:
self.outputGroup.startButton.setEnabled(False)
return result
return wrapper
@enable_start_check
def set_value(
self,
key: Literal['input_file_path', 'output_file_path', 'output_file_name'],
value: str
) -> None:
setattr(self, key, value)
def input_signal_receive(self, file_path: str) -> None:
self.set_value('input_file_path', file_path)
self.outputGroup.update_path_and_filename_by_input_path(file_path)
def export_signal_receive(self, s_type: Literal['d', 'f'], value: str) -> None:
if s_type == 'd':
self.set_value('output_file_path', value)
elif s_type == 'f':
self.set_value('output_file_name', value)
def start_generate(self):
self.thread = QThread()
self.worker = ARGWorker(
self.input_file_path,
self.output_file_path,
self.output_file_name,
disable_cc=self.outputGroup.disableCompatibilityCheckSwitch.isChecked()
)
self.worker.moveToThread(self.thread)
self.outputGroup.startButton.setEnabled(False)
# 线程启动与信号连接
self.thread.started.connect(self.worker.run)
self.show_info_bar('')
self.worker.error.connect(self.show_error)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.thread.finished.connect(self.thread.deleteLater)
self.thread.finished.connect(self.clear_thread_worker_refs)
self.thread.finished.connect(self.after_generate)
# 启动线程
self.thread.start()
def clear_thread_worker_refs(self):
self.thread = None
self.worker = None
def after_generate(self):
self.outputGroup.startButton.setEnabled(True)
if self.outputGroup.autoOpenSwitch.isChecked():
try:
os.startfile(self.output_file_path + "/" + self.output_file_name + ".docx")
except Exception as e:
self.show_error("?? 不好出错了", str(e))
if self.infoBar:
self.infoBar.close()
self.infoBar = InfoBar.success(
title='成功!',
content="正在打开文件" if self.outputGroup.autoOpenSwitch.isChecked() else "文件已保存",
orient=Qt.Horizontal,
isClosable=True,
position=InfoBarPosition.BOTTOM,
duration=5000,
parent=self
)
def show_error(self, title: str, content: str):
self.error.emit(title, content)
def show_info_bar(self, info: str):
self.infoBar = InfoBar(
icon=InfoBarIcon.INFORMATION,
title='请稍后',
content=info,
orient=Qt.Horizontal,
isClosable=False,
position=InfoBarPosition.BOTTOM,
duration=-1,
parent=self
)
self.infoBar.addWidget(IndeterminateProgressBar(start=True))
self.infoBar.show()

View File

@@ -4,7 +4,7 @@ from functools import wraps
from PySide6.QtCore import Qt, Signal, QThread
from PySide6.QtWidgets import QHBoxLayout, QVBoxLayout, QFileDialog
from qfluentwidgets import GroupHeaderCardWidget, PushButton, IconWidget, InfoBarIcon, \
BodyLabel, PrimaryPushButton, FluentIcon, LineEdit, InfoBar, InfoBarPosition, ProgressBar
BodyLabel, PrimaryPushButton, FluentIcon, LineEdit, InfoBar, InfoBarPosition, ProgressBar, IndeterminateProgressBar
from module.worker import DTGWorker
from ui.components.widget import Widget
@@ -16,7 +16,7 @@ class InitSettingCard(GroupHeaderCardWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setTitle("初始化")
self.setTitle("输入选项")
self.setBorderRadius(8)
self.chooseStudentButton = PushButton("打开")
@@ -53,7 +53,7 @@ class ExportSettingsCard(GroupHeaderCardWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setTitle("导出")
self.setTitle("输入选项")
self.setBorderRadius(8)
self.chooseExportDirectoryButton = PushButton("选择")
@@ -100,14 +100,16 @@ class ExportSettingsCard(GroupHeaderCardWidget):
def update_export_setting_by_signal(self, path: str) -> None:
f_dir = path[:path.rfind('/')]
f_name = path[path.rfind('/') + 1:path.rfind('.')]
f_name = path[path.rfind('/') + 1:path.rfind('.')] + '-答辩题目'
self.dirGroup.setContent(f"当前保存的文件目录:{f_dir}")
self.updateSignal.emit('d', f_dir)
self.exportFileNameLineEdit.setText(f"{f_name}-答辩题目")
self.exportFileNameLineEdit.setText(f_name)
self.updateSignal.emit('n', f_name)
class DefenseWidget(Widget):
errorSignal = Signal(str, str)
def __init__(self, key: str, parent=None):
super().__init__(key, parent)
@@ -125,6 +127,8 @@ class DefenseWidget(Widget):
self.thread = None
self.worker = None
self.successFlag = True
# ===================================
self.input_student_filepath = None
@@ -157,17 +161,21 @@ class DefenseWidget(Widget):
self.pb.setValue(value)
if value == 100:
self.infoBar.close()
self.infoBar = InfoBar.success(
title='成功!',
content="正在打开文件...",
self.infoBar = InfoBar(
icon=InfoBarIcon.INFORMATION,
title='正在转换文件',
content="",
orient=Qt.Horizontal,
isClosable=True,
isClosable=False,
position=InfoBarPosition.BOTTOM,
duration=5000,
duration=-1,
parent=self
)
self.infoBar.addWidget(IndeterminateProgressBar(start=True))
self.infoBar.show()
self.pb = None
self.exportCard.startButton.setEnabled(True)
elif value == -1:
self.successFlag = False
def enable_start_check(func: Callable):
@wraps(func)
@@ -198,7 +206,6 @@ class DefenseWidget(Widget):
self.output_filepath = value
elif key == "output_filename":
self.output_filename = value
setattr(self, key, value)
def input_signal_receive(self, s_type: Literal['s', 'q'], value: str):
if s_type == "s":
@@ -224,16 +231,19 @@ class DefenseWidget(Widget):
self.worker.moveToThread(self.thread)
self.show_info_bar()
self.successFlag = True
self.exportCard.startButton.setEnabled(False)
# 线程启动与信号连接
self.thread.started.connect(self.worker.run)
self.worker.progress.connect(self.set_pb_value)
self.worker.error.connect(self.show_error)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.thread.finished.connect(self.thread.deleteLater)
self.thread.finished.connect(self.clear_thread_worker_refs)
self.thread.finished.connect(self.after_generate)
# 启动线程
self.thread.start()
@@ -241,3 +251,30 @@ class DefenseWidget(Widget):
def clear_thread_worker_refs(self):
self.thread = None
self.worker = None
def after_generate(self):
self.exportCard.startButton.setEnabled(True)
self.infoBar.close()
if self.successFlag:
self.infoBar = InfoBar.success(
title='成功!',
content="正在打开文件...",
orient=Qt.Horizontal,
isClosable=True,
position=InfoBarPosition.BOTTOM,
duration=5000,
parent=self
)
else:
self.infoBar = InfoBar.error(
title='失败!',
content="",
orient=Qt.Horizontal,
isClosable=True,
position=InfoBarPosition.BOTTOM,
duration=5000,
parent=self
)
def show_error(self, title: str, content: str):
self.errorSignal.emit(title, content)