完善提问模块

This commit is contained in:
2025-06-25 23:18:32 +08:00
parent 64f226c714
commit b845b5994a
6 changed files with 354 additions and 22 deletions

View File

View File

@@ -1,4 +1,11 @@
from PySide6.QtWidgets import QFrame
from PySide6.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QVBoxLayout, QFrame
from PySide6.QtCore import QTimer, Qt, Signal
import sys
import random
from qfluentwidgets import DisplayLabel
from module.picker.schema import PickerStudent
class Widget(QFrame):
@@ -7,3 +14,44 @@ class Widget(QFrame):
super().__init__(parent=parent)
# 必须给子界面设置全局唯一的对象名
self.setObjectName(key.replace(' ', '-'))
class RollingTextWidget(QWidget):
finishSignal = Signal()
def __init__(self, parent=None):
super().__init__(parent)
self.current_index = 0
self.items = []
self.label = DisplayLabel("", self)
self.label.setAlignment(Qt.AlignCenter)
self.layout = QVBoxLayout()
self.layout.addWidget(self.label)
self.setLayout(self.layout)
self.rolling_timer = QTimer(self)
self.rolling_timer.setInterval(50) # 滚动速度(毫秒)
self.rolling_timer.timeout.connect(self.update_text)
self.stop_timer = QTimer(self)
self.stop_timer.setSingleShot(True)
self.stop_timer.timeout.connect(self.stop_rolling)
def update_text(self):
# 每次显示下一个字符
self.current_index = (self.current_index + 1) % len(self.items)
self.label.setText(self.items[self.current_index].name)
def start_rolling(self):
if not self.rolling_timer.isActive():
self.rolling_timer.start()
self.stop_timer.start(2000) # 2秒后停止滚动
def stop_rolling(self):
self.rolling_timer.stop()
self.finishSignal.emit()
def set_items(self, items: list[PickerStudent]):
self.items = items

16
ui/components/window.py Normal file
View File

@@ -0,0 +1,16 @@
from PySide6.QtWidgets import QHBoxLayout
from qfluentwidgets import FluentTitleBar
from qfluentwidgets.window.fluent_window import FluentWindowBase
class MyWindow(FluentWindowBase):
def __init__(self, parent=None):
super().__init__(parent)
self.setTitleBar(FluentTitleBar(self))
self.widgetLayout = QHBoxLayout(self)
self.widgetLayout.setContentsMargins(0, 48, 0, 0)
self.hBoxLayout.addLayout(self.widgetLayout)
self.titleBar.raise_()
self.showMaximized()

View File

@@ -20,21 +20,22 @@ class MainWindow(MSFluentWindow):
self.achievementInterface = AchievementWidget('Achievement Interface', self)
self.defenseInterface = DefenseWidget('Defense Interface', self)
self.aboutInterface = AboutWidget('About Interface', self)
self.pickerInterface = PickerWidget('Picker Interface', self)
if not DEVELOPMENT_ENV:
self.pickerInterface = PickerWidget('Picker Interface', self)
self.testInterface = TestWidget('Test Interface', self)
self.achievementInterface.error.connect(self.showError)
self.defenseInterface.errorSignal.connect(self.showError)
self.pickerInterface.errorSignal.connect(self.showError)
self.initNavigation()
self.initWindow()
def initNavigation(self):
self.addSubInterface(self.achievementInterface, FluentIcon.SPEED_HIGH, '达成度')
self.addSubInterface(self.defenseInterface, FluentIcon.FEEDBACK, '答辩')
self.addSubInterface(self.defenseInterface, FluentIcon.FEEDBACK, '答辩题目')
self.addSubInterface(self.pickerInterface, FluentIcon.PEOPLE, '提问')
if not DEVELOPMENT_ENV:
self.addSubInterface(self.pickerInterface, FluentIcon.PEOPLE, '抽答')
self.addSubInterface(self.testInterface, FluentIcon.VIEW, '测试')
self.addSubInterface(self.aboutInterface, FluentIcon.INFO, '关于', position=NavigationItemPosition.BOTTOM)
@@ -45,7 +46,10 @@ class MainWindow(MSFluentWindow):
self.setWindowIcon(QIcon(':/images/logo.png'))
def showError(self, title: str, message: str):
MessageBox(title, message, self).exec()
box = MessageBox(title, message, self)
box.yesButton.setText("关闭")
box.cancelButton.hide()
box.exec()
def showEvent(self, event: QShowEvent):
super().showEvent(event)

View File

@@ -1,27 +1,132 @@
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout
from qfluentwidgets import GroupHeaderCardWidget, PushButton, FluentIcon, PrimaryPushButton, IconWidget, BodyLabel
from PySide6.QtCore import Qt, Signal, QTimer
from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout, QWidget, QStackedWidget, QGridLayout, QFileDialog
from qfluentwidgets import GroupHeaderCardWidget, PushButton, FluentIcon, PrimaryPushButton, IconWidget, BodyLabel, \
SegmentedWidget, SpinBox, LargeTitleLabel, DisplayLabel, SubtitleLabel
from module.picker.schema import PickerExcel, PickerStudent
from ui.components.widget import RollingTextWidget
from ui import MAIN_THEME_COLOR
from ui.components.widget import Widget
from ui.components.window import MyWindow
class PickerWidget(Widget):
def __init__(self, key: str, parent=None):
super().__init__(key, parent)
class PSMMain(MyWindow):
finishSignal = Signal()
def __init__(self, student: PickerStudent, parent=None):
super().__init__(parent)
self.student = student
self.grade_edit = SpinBox(self)
self.init_layout()
def init_layout(self):
main_layout = QVBoxLayout()
hLayout = QHBoxLayout()
name_layout = QVBoxLayout()
date_layout = QVBoxLayout()
hLayout.addStretch()
hLayout.addLayout(name_layout)
hLayout.addLayout(date_layout)
hLayout.addStretch()
hLayout.setSpacing(20)
so = LargeTitleLabel(f"{self.student.so}", self)
so.setAlignment(Qt.AlignCenter)
sname = DisplayLabel(
f"{self.student.name[0] + ' ' + self.student.name[1] if len(self.student.name) == 2 else self.student.name}",
self)
sname.setAlignment(Qt.AlignCenter)
name_layout.addWidget(so)
name_layout.addWidget(sname)
self.grade_edit.setFixedWidth(260)
self.grade_edit.setRange(0, 100)
grade_layout = QHBoxLayout()
grade_layout.setContentsMargins(0, 0, 0, 48)
grade_layout.setSpacing(20)
submit_button = PrimaryPushButton("确定", self)
cancel_button = PushButton("重置", self)
submit_button.clicked.connect(self.close_modal)
cancel_button.clicked.connect(lambda: self.grade_edit.clear())
submit_button.setFixedWidth(120)
cancel_button.setFixedWidth(120)
grade_layout.addWidget(submit_button)
grade_layout.addWidget(cancel_button)
date_layout.addWidget(self.grade_edit)
date_layout.addLayout(grade_layout)
main_layout.addStretch()
main_layout.addLayout(hLayout)
main_layout.addStretch()
# 快速打分
quick_grade_layout = QVBoxLayout()
quick_grade_layout.setContentsMargins(0, 0, 0, 0)
quick_grade_layout.setSpacing(20)
quick_grade_title = SubtitleLabel("快速打分", self)
quick_grade_title.setAlignment(Qt.AlignCenter)
quick_grade_layout.addWidget(quick_grade_title)
grid_wrapper_layout = QHBoxLayout()
grid_layout = QGridLayout()
buttons = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
for i, num in enumerate(buttons):
row = i // 3
col = i % 3
btn = PushButton(str(num))
btn.setFixedSize(80, 80)
# 使用lambda绑定事件
btn.clicked.connect(lambda _, n=num: self.grade_edit.setValue(n))
grid_layout.addWidget(btn, row, col)
grid_wrapper_layout.addStretch()
grid_wrapper_layout.addLayout(grid_layout)
grid_wrapper_layout.addStretch()
quick_grade_layout.addLayout(grid_layout)
quick_grade_layout.addLayout(grid_wrapper_layout)
main_layout.addLayout(quick_grade_layout)
self.widgetLayout.addLayout(main_layout)
def close_modal(self):
self.student.append_score(self.grade_edit.value())
self.finishSignal.emit()
self.close()
class PickStudentMode(QWidget):
errorSignal = Signal(str)
def __init__(self, parent=None):
super().__init__(parent)
self.card = GroupHeaderCardWidget(self)
self.vbox = QVBoxLayout(self)
self.vbox.setContentsMargins(0, 0, 0, 0)
self.chooseBtn = PushButton("打开")
self.startButton = PrimaryPushButton(FluentIcon.PLAY_SOLID, "抽签")
self.startButton = PrimaryPushButton(FluentIcon.PLAY_SOLID, "开始")
self.bottomLayout = QHBoxLayout()
self.hintIcon = IconWidget(FluentIcon.INFO.icon(color=MAIN_THEME_COLOR))
self.hintLabel = BodyLabel("点击抽签按钮以开始抽签 👉")
self.hintLabel = BodyLabel("点击开始按钮以开始抽签 👉")
self.spinbox = SpinBox()
self.rollingText = RollingTextWidget(self)
self.card.setTitle("输入选项")
self.chooseBtn.setFixedWidth(120)
self.startButton.setFixedWidth(120)
self.startButton.setEnabled(False)
self.spinbox.setRange(0, 6)
self.spinbox.setFixedWidth(120)
self.spinbox.setEnabled(False)
self.hintIcon.setFixedSize(16, 16)
self.hintIcon.autoFillBackground()
@@ -33,9 +138,111 @@ class PickerWidget(Widget):
self.bottomLayout.addWidget(self.startButton, 0, Qt.AlignRight)
self.bottomLayout.setAlignment(Qt.AlignVCenter)
self.group = self.card.addGroup(FluentIcon.DOCUMENT, "抽答名单", "选择抽答名单", self.chooseBtn)
self.group.setSeparatorVisible(True)
self.group = self.card.addGroup(FluentIcon.DOCUMENT, "提问名单", "选择提问名单", self.chooseBtn)
self.spinGroup = self.card.addGroup(FluentIcon.SETTING, "提问次数", "设置提问的最大次数", self.spinbox)
self.spinGroup.setSeparatorVisible(True)
self.card.vBoxLayout.addLayout(self.bottomLayout)
self.vbox.addWidget(self.card)
self.vbox.addWidget(self.rollingText)
self.vbox.addStretch(1)
# ==============================
self.chooseBtn.clicked.connect(self.choose_file)
self.spinbox.valueChanged.connect(lambda: PickerExcel.save_total_time(value=self.spinbox.value()))
self.startButton.clicked.connect(self.start_rolling)
self.rollingText.finishSignal.connect(self.finish_rolling)
# ==============================
self.filepath = ""
self.students = []
def choose_file(self):
file_path, _ = QFileDialog.getOpenFileName(self, "选择文件", "", "Excel 文件 (*.xlsm);")
if file_path:
self.group.setContent("已选择文件:" + file_path)
self.filepath = file_path
self.startButton.setEnabled(True)
self.init_spinbox_value()
def init_spinbox_value(self):
if not self.filepath:
return
try:
PickerExcel.open(self.filepath)
self.spinbox.setValue(PickerExcel.read_total_time())
self.spinbox.setEnabled(True)
except Exception as e:
self.errorSignal.emit(str(e))
self.spinbox.setEnabled(False)
self.startButton.setEnabled(False)
def start_rolling(self):
self.students = PickerExcel.read_student()
self.rollingText.set_items(self.students)
self.rollingText.start_rolling()
self.startButton.setEnabled(False)
def finish_rolling(self):
stu = PickerStudent.pick(self.students)
if not (stu.so and stu.name):
self.errorSignal.emit("学生信息读取失败")
self.rollingText.label.setText(stu.name)
timer = QTimer(self)
timer.setSingleShot(True)
timer.timeout.connect(lambda: self.show_screen(stu))
timer.timeout.connect(lambda: self.startButton.setEnabled(True))
timer.start(1000)
def show_screen(self, stu: PickerStudent):
screen = PSMMain(stu)
screen.finishSignal.connect(lambda: PickerExcel.write_back(stu))
screen.finishSignal.connect(lambda: self.rollingText.label.clear())
class PickQuestionMode(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
lable = DisplayLabel('🚧', self)
lable.setAlignment(Qt.AlignCenter)
self.layout = QHBoxLayout(self)
self.layout.addStretch()
self.layout.addWidget(lable)
self.layout.addStretch()
class PickerWidget(Widget):
errorSignal = Signal(str, str)
def __init__(self, key: str, parent=None):
super().__init__(key, parent)
self.vbox = QVBoxLayout(self)
self.menu = SegmentedWidget(self)
self.stack = QStackedWidget(self)
self.psm = PickStudentMode(self)
self.pqm = PickQuestionMode(self)
self.add_sub_interface(self.psm, "pickStudentMode", "选人模式")
self.add_sub_interface(self.pqm, "pickQuestionMode", "选题模式")
self.menu.setCurrentItem("pickStudentMode")
self.vbox.addWidget(self.menu)
self.vbox.addWidget(self.stack)
self.vbox.addStretch(1)
# ===========================
self.stack.currentChanged.connect(self.on_stack_index_changed)
self.psm.errorSignal.connect(lambda n: self.errorSignal.emit("😢 不好出错了", n))
def add_sub_interface(self, widget: QWidget, obj_name: str, text: str):
widget.setObjectName(obj_name)
self.stack.addWidget(widget)
self.menu.addItem(routeKey=obj_name, text=text, onClick=lambda: self.stack.setCurrentWidget(widget))
def on_stack_index_changed(self, index: int):
widget = self.stack.widget(index)
self.stack.setCurrentWidget(widget)