添加下载模板功能

This commit is contained in:
2025-06-29 00:06:47 +08:00
parent 9d703212ef
commit 375ea9a686
16 changed files with 143 additions and 20 deletions

View File

@@ -42,7 +42,7 @@ class DTGWorker(QObject):
max_question_count = math.ceil(len(students) * 3 / len(questions)) max_question_count = math.ceil(len(students) * 3 / len(questions))
d = DocPaper(self.output_filename, template_path=resource_path("template/template-2.docx")) d = DocPaper(self.output_filename, resource_path("template/template-defense-paper-paper.docx"))
for index, student in enumerate(students): for index, student in enumerate(students):
if (p := int((index + 1) / len(students) * 100)) != 100: if (p := int((index + 1) / len(students) * 100)) != 100:
self.progress[int].emit(p) self.progress[int].emit(p)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,8 +1,10 @@
import random import random
from typing import Union
from PySide6.QtCore import QTimer, Qt, Signal from PySide6.QtCore import QTimer, Qt, Signal
from PySide6.QtWidgets import QWidget, QVBoxLayout, QFrame from PySide6.QtGui import QIcon
from qfluentwidgets import DisplayLabel, LargeTitleLabel from PySide6.QtWidgets import QWidget, QVBoxLayout, QFrame, QLayout
from qfluentwidgets import DisplayLabel, LargeTitleLabel, GroupHeaderCardWidget, FluentIconBase, CardGroupWidget
from module.picker.schema import PickerStudent from module.picker.schema import PickerStudent
@@ -68,3 +70,27 @@ class RollingTextWidget(QWidget):
def clear_text(self): def clear_text(self):
self.soLabel.clear() self.soLabel.clear()
self.nameLabel.clear() self.nameLabel.clear()
class MyCardGroupWidget(CardGroupWidget):
def addLayout(self, layout: QLayout, stretch=0):
self.hBoxLayout.addLayout(layout, stretch=stretch)
class MyGroupHeaderCardWidget(GroupHeaderCardWidget):
def addGroup(self, icon: Union[str, FluentIconBase, QIcon], title: str, content: str,
object: Union[QWidget, QLayout],
stretch=0) -> CardGroupWidget:
group = MyCardGroupWidget(icon, title, content, self)
if isinstance(object, QWidget):
group.addWidget(object, stretch=stretch)
elif isinstance(object, QLayout):
group.addLayout(object, stretch=stretch)
if self.groupWidgets:
self.groupWidgets[-1].setSeparatorVisible(True)
self.groupLayout.addWidget(group)
self.groupWidgets.append(group)
return group

View File

@@ -5,16 +5,17 @@ from typing import Callable, Literal
from PySide6.QtCore import Qt, Signal, QThread from PySide6.QtCore import Qt, Signal, QThread
from PySide6.QtWidgets import QVBoxLayout, QFileDialog, QHBoxLayout from PySide6.QtWidgets import QVBoxLayout, QFileDialog, QHBoxLayout
from qfluentwidgets import GroupHeaderCardWidget, FluentIcon, PushButton, LineEdit, IconWidget, BodyLabel, \ from qfluentwidgets import GroupHeaderCardWidget, FluentIcon, PushButton, LineEdit, IconWidget, BodyLabel, \
PrimaryPushButton, SwitchButton PrimaryPushButton, SwitchButton, HyperlinkButton
from module import LOGLEVEL from module import LOGLEVEL
from module.worker import ARGWorker from module.worker import ARGWorker
from ui import MAIN_THEME_COLOR from ui import MAIN_THEME_COLOR
from ui.components.infobar import ProgressInfoBar from ui.components.infobar import ProgressInfoBar
from ui.components.widget import Widget from ui.components.widget import Widget, MyGroupHeaderCardWidget
from utils.function import open_template
class InputSettingCard(GroupHeaderCardWidget): class InputSettingCard(MyGroupHeaderCardWidget):
chooseSignal = Signal(str) chooseSignal = Signal(str)
def __init__(self, parent=None): def __init__(self, parent=None):
@@ -22,15 +23,20 @@ class InputSettingCard(GroupHeaderCardWidget):
self.setTitle("输入选项") self.setTitle("输入选项")
self.setBorderRadius(8) self.setBorderRadius(8)
self.btnHBoxLayout = QHBoxLayout(self)
self.openTemplateButton = HyperlinkButton("", "下载模板")
self.chooseFileButton = PushButton("打开") self.chooseFileButton = PushButton("打开")
self.chooseFileButton.setFixedWidth(120) self.chooseFileButton.setFixedWidth(120)
self.btnHBoxLayout.addWidget(self.openTemplateButton)
self.btnHBoxLayout.addWidget(self.chooseFileButton)
self.inputGroup = self.addGroup(FluentIcon.DOCUMENT, "目标文件", "选择达成度计算表", self.chooseFileButton) self.inputGroup = self.addGroup(FluentIcon.DOCUMENT, "目标文件", "选择达成度计算表", self.btnHBoxLayout)
# ============================ # ============================
self.chooseFileButton.clicked.connect(self.choose_file) self.chooseFileButton.clicked.connect(self.choose_file)
self.openTemplateButton.clicked.connect(lambda: open_template('template-achievement-file.xlsm', self))
def choose_file(self): def choose_file(self):
file_path, _ = QFileDialog.getOpenFileName(self, "选择文件", "", "Excel 文件 (*.xlsm);") file_path, _ = QFileDialog.getOpenFileName(self, "选择文件", "", "Excel 文件 (*.xlsm);")

View File

@@ -1,18 +1,43 @@
from functools import wraps from functools import wraps
from typing import Literal, Callable from typing import Literal, Callable
from PySide6.QtCore import Qt, Signal, QThread from PySide6.QtCore import Qt, Signal, QThread, QEvent
from PySide6.QtWidgets import QHBoxLayout, QVBoxLayout, QFileDialog, QButtonGroup, QWidget from PySide6.QtWidgets import QHBoxLayout, QVBoxLayout, QFileDialog, QButtonGroup, QWidget, QApplication
from qfluentwidgets import GroupHeaderCardWidget, PushButton, IconWidget, BodyLabel, PrimaryPushButton, FluentIcon, \ from qfluentwidgets import GroupHeaderCardWidget, PushButton, IconWidget, BodyLabel, PrimaryPushButton, FluentIcon, \
LineEdit, RadioButton LineEdit, RadioButton, HyperlinkButton, FlyoutViewBase, TeachingTipView, TeachingTip, TeachingTipTailPosition
from module.worker import DTGWorker from module.worker import DTGWorker
from ui import MAIN_THEME_COLOR from ui import MAIN_THEME_COLOR
from ui.components.infobar import ProgressInfoBar from ui.components.infobar import ProgressInfoBar
from ui.components.widget import Widget from ui.components.widget import Widget, MyGroupHeaderCardWidget
from utils.function import open_template
class InitSettingCard(GroupHeaderCardWidget): class ChooseTemplateView(FlyoutViewBase):
closed = Signal()
def __init__(self, parent=None):
super().__init__(parent)
self.vBoxLayout = QVBoxLayout(self)
QApplication.instance().installEventFilter(self)
def paintEvent(self, e):
...
def eventFilter(self, watched, event):
if event.type() == QEvent.MouseButtonPress:
if not self.rect().contains(self.mapFromGlobal(event.globalPosition().toPoint())):
self.closed.emit()
return super().eventFilter(watched, event)
def addTemplate(self, content: str, cb: Callable[[], None]):
label = HyperlinkButton("", content)
self.vBoxLayout.addWidget(label)
label.clicked.connect(cb)
label.clicked.connect(self.closed.emit)
class InitSettingCard(MyGroupHeaderCardWidget):
chooseSignal = Signal(str, str) chooseSignal = Signal(str, str)
def __init__(self, parent=None): def __init__(self, parent=None):
@@ -21,19 +46,29 @@ class InitSettingCard(GroupHeaderCardWidget):
self.setTitle("输入选项") self.setTitle("输入选项")
self.setBorderRadius(8) self.setBorderRadius(8)
self.sBtnHBoxLayout = QHBoxLayout(self)
self.qBtnHBoxLayout = QHBoxLayout(self)
self.sTemplateButton = HyperlinkButton("", "下载模板")
self.chooseStudentButton = PushButton("打开") self.chooseStudentButton = PushButton("打开")
self.qTemplateButton = HyperlinkButton("", "下载模板")
self.chooseQuestionButton = PushButton("打开") self.chooseQuestionButton = PushButton("打开")
self.chooseStudentButton.setFixedWidth(120) self.chooseStudentButton.setFixedWidth(120)
self.chooseQuestionButton.setFixedWidth(120) self.chooseQuestionButton.setFixedWidth(120)
self.sBtnHBoxLayout.addWidget(self.sTemplateButton)
self.sBtnHBoxLayout.addWidget(self.chooseStudentButton)
self.qBtnHBoxLayout.addWidget(self.qTemplateButton)
self.qBtnHBoxLayout.addWidget(self.chooseQuestionButton)
self.stuGroup = self.addGroup(FluentIcon.DOCUMENT, "学生名单", "选择学生名单文件", self.chooseStudentButton) self.stuGroup = self.addGroup(FluentIcon.DOCUMENT, "学生名单", "选择学生名单文件", self.sBtnHBoxLayout)
self.QueGroup = self.addGroup(FluentIcon.DOCUMENT, "题库", "选择题库文件", self.chooseQuestionButton) self.QueGroup = self.addGroup(FluentIcon.DOCUMENT, "题库", "选择题库文件", self.qBtnHBoxLayout)
self.chooseStudentButton.clicked.connect( self.chooseStudentButton.clicked.connect(
lambda: self.choose_file(self.stuGroup.setContent, "已选择文件:", lambda x: self.chooseSignal.emit('s', x))) lambda: self.choose_file(self.stuGroup.setContent, "已选择文件:", lambda x: self.chooseSignal.emit('s', x)))
self.chooseQuestionButton.clicked.connect( self.chooseQuestionButton.clicked.connect(
lambda: self.choose_file(self.QueGroup.setContent, "已选择文件:", lambda x: self.chooseSignal.emit('q', x))) lambda: self.choose_file(self.QueGroup.setContent, "已选择文件:", lambda x: self.chooseSignal.emit('q', x)))
self.qTemplateButton.clicked.connect(lambda: open_template('template-defense-paper-questions.xlsm', self))
self.sTemplateButton.clicked.connect(self.show_template_list_view)
def choose_file( def choose_file(
self, self,
@@ -47,6 +82,19 @@ class InitSettingCard(GroupHeaderCardWidget):
if cb: if cb:
cb(file_path) cb(file_path)
def show_template_list_view(self):
view = ChooseTemplateView(self)
view.addTemplate("普通模板", lambda: open_template("template-defense-paper-student-1.xlsm", self))
view.addTemplate("达成度模板", lambda: open_template("template-defense-paper-student-2.xlsm", self))
w = TeachingTip.make(
target=self.sTemplateButton,
view=view,
tailPosition=TeachingTipTailPosition.TOP,
duration=-1,
parent=self
)
view.closed.connect(w.close)
class ExportSettingsCard(GroupHeaderCardWidget): class ExportSettingsCard(GroupHeaderCardWidget):
startSignal = Signal() startSignal = Signal()

View File

@@ -1,12 +1,13 @@
from PySide6.QtCore import Qt, Signal, QTimer from PySide6.QtCore import Qt, Signal, QTimer
from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout, QWidget, QFileDialog from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout, QWidget, QFileDialog
from qfluentwidgets import GroupHeaderCardWidget, PushButton, FluentIcon, PrimaryPushButton, IconWidget, BodyLabel, \ from qfluentwidgets import PushButton, FluentIcon, PrimaryPushButton, IconWidget, BodyLabel, \
SpinBox SpinBox, HyperlinkButton
from module.picker.schema import PickerExcel, PickerStudent from module.picker.schema import PickerExcel, PickerStudent
from ui import MAIN_THEME_COLOR from ui import MAIN_THEME_COLOR
from ui.components.widget import Widget from ui.components.widget import Widget, MyGroupHeaderCardWidget
from ui.pyui.sub.picker import PickStudentLabelUi from ui.pyui.sub.picker import PickStudentLabelUi
from utils.function import open_template
class PickStudentMode(QWidget): class PickStudentMode(QWidget):
@@ -15,10 +16,12 @@ class PickStudentMode(QWidget):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
self.card = GroupHeaderCardWidget(self) self.card = MyGroupHeaderCardWidget(self)
self.vbox = QVBoxLayout(self) self.vbox = QVBoxLayout(self)
self.vbox.setContentsMargins(0, 0, 0, 0) self.vbox.setContentsMargins(0, 0, 0, 0)
self.btnHBox = QHBoxLayout(self)
self.openTemplateBtn = HyperlinkButton("", "下载模板")
self.chooseBtn = PushButton("打开") self.chooseBtn = PushButton("打开")
self.startButton = PrimaryPushButton(FluentIcon.PLAY_SOLID, "开始") self.startButton = PrimaryPushButton(FluentIcon.PLAY_SOLID, "开始")
self.bottomLayout = QHBoxLayout() self.bottomLayout = QHBoxLayout()
@@ -45,8 +48,10 @@ class PickStudentMode(QWidget):
self.bottomLayout.addStretch(1) self.bottomLayout.addStretch(1)
self.bottomLayout.addWidget(self.startButton, 0, Qt.AlignRight) self.bottomLayout.addWidget(self.startButton, 0, Qt.AlignRight)
self.bottomLayout.setAlignment(Qt.AlignVCenter) self.bottomLayout.setAlignment(Qt.AlignVCenter)
self.btnHBox.addWidget(self.openTemplateBtn)
self.btnHBox.addWidget(self.chooseBtn)
self.group = self.card.addGroup(FluentIcon.DOCUMENT, "学生名单", "选择学生名单", self.chooseBtn) self.group = self.card.addGroup(FluentIcon.DOCUMENT, "学生名单", "选择学生名单", self.btnHBox)
self.spinGroup = self.card.addGroup(FluentIcon.SETTING, "提问次数", "设置提问的最大次数", self.spinbox) self.spinGroup = self.card.addGroup(FluentIcon.SETTING, "提问次数", "设置提问的最大次数", self.spinbox)
self.spinGroup.setSeparatorVisible(True) self.spinGroup.setSeparatorVisible(True)
self.card.vBoxLayout.addLayout(self.bottomLayout) self.card.vBoxLayout.addLayout(self.bottomLayout)
@@ -60,6 +65,7 @@ class PickStudentMode(QWidget):
self.spinbox.valueChanged.connect(lambda: PickerExcel.save_total_time(value=self.spinbox.value())) self.spinbox.valueChanged.connect(lambda: PickerExcel.save_total_time(value=self.spinbox.value()))
self.startButton.clicked.connect(self.start_rolling) self.startButton.clicked.connect(self.start_rolling)
self.psui.rollingText.finishSignal.connect(self.finish_rolling) self.psui.rollingText.finishSignal.connect(self.finish_rolling)
self.openTemplateBtn.clicked.connect(lambda: open_template("template-pick-student.xlsm", self))
# ============================== # ==============================
self.filepath = "" self.filepath = ""
self.students = [] self.students = []

View File

@@ -3,7 +3,7 @@ import sys
from PySide6.QtCore import Signal from PySide6.QtCore import Signal
from PySide6.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QApplication, QGridLayout from PySide6.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QApplication, QGridLayout
from qfluentwidgets import PushButton, SpinBox, PrimaryPushButton, \ from qfluentwidgets import PushButton, SpinBox, PrimaryPushButton, \
BodyLabel BodyLabel, CommandBarView, Action, FluentIcon, Flyout, FlyoutAnimationType
from ui.components.widget import RollingTextWidget from ui.components.widget import RollingTextWidget

View File

@@ -1,10 +1,16 @@
import io import io
import os import os
import re import re
import shutil
import sys import sys
import tempfile
from typing import Optional
import matplotlib import matplotlib
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QWidget
from matplotlib import pyplot as plt from matplotlib import pyplot as plt
from qfluentwidgets import InfoBar, InfoBarPosition
def format_ranges(nums): def format_ranges(nums):
@@ -185,4 +191,35 @@ def resource_path(relative_path: str) -> str:
return os.path.join(base_path, relative_path) return os.path.join(base_path, relative_path)
def open_template(file_name: str, widget: QWidget) -> None:
"""将模板文件复制到临时目录并打开"""
file_path = resource_path("template/" + file_name)
if not os.path.exists(file_path):
raise FileNotFoundError(f"Template file '{file_name}' not found.")
# 复制到临时目录
tmp_dir = tempfile.gettempdir()
tmp_file_path = os.path.join(tmp_dir, file_name)
shutil.copy(file_path, tmp_file_path)
# 打开文件
if sys.platform.startswith('win'):
os.startfile(tmp_file_path)
elif sys.platform.startswith('darwin'):
os.system(f'open "{tmp_file_path}"')
else:
os.system(f'xdg-open "{tmp_file_path}"')
InfoBar.info(
title='已打开文件',
content="编辑后请将文件另存为,保存至其他目录",
orient=Qt.Horizontal,
isClosable=True,
position=InfoBarPosition.TOP_RIGHT,
duration=2000,
parent=widget
)
DEVELOPMENT_ENV = getattr(sys, 'frozen', False) DEVELOPMENT_ENV = getattr(sys, 'frozen', False)