From 8a07bb728e0d55ac80054e0d80327cb048de30b9 Mon Sep 17 00:00:00 2001 From: Jeffrey Hsu Date: Sat, 28 Jun 2025 18:43:36 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=8F=90=E9=97=AE=E6=A8=A1?= =?UTF-8?q?=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- module/picker/schema.py | 2 +- ui/components/widget.py | 31 +++++--- ui/pyui/picker_ui.py | 158 +++++----------------------------------- ui/pyui/sub/picker.py | 95 ++++++++++++++++++++++++ 4 files changed, 138 insertions(+), 148 deletions(-) create mode 100644 ui/pyui/sub/picker.py diff --git a/module/picker/schema.py b/module/picker/schema.py index 0bff17a..77d5002 100644 --- a/module/picker/schema.py +++ b/module/picker/schema.py @@ -11,7 +11,7 @@ class PickerStudent: def __init__(self, name: str, so: str, position: int, scores: list[int]): self._name = name - self._so = so + self._so = str(so) self._position = position self._scores = scores self._modify = False diff --git a/ui/components/widget.py b/ui/components/widget.py index 61e20c0..ebec839 100644 --- a/ui/components/widget.py +++ b/ui/components/widget.py @@ -1,9 +1,8 @@ -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 PySide6.QtCore import QTimer, Qt, Signal +from PySide6.QtWidgets import QWidget, QVBoxLayout, QFrame +from qfluentwidgets import DisplayLabel, LargeTitleLabel from module.picker.schema import PickerStudent @@ -24,11 +23,14 @@ class RollingTextWidget(QWidget): self.current_index = 0 self.items = [] - self.label = DisplayLabel("", self) - self.label.setAlignment(Qt.AlignCenter) + self.soLabel = LargeTitleLabel("", self) + self.nameLabel = DisplayLabel("", self) + self.soLabel.setAlignment(Qt.AlignCenter) + self.nameLabel.setAlignment(Qt.AlignCenter) self.layout = QVBoxLayout() - self.layout.addWidget(self.label) + self.layout.addWidget(self.soLabel) + self.layout.addWidget(self.nameLabel) self.setLayout(self.layout) self.rolling_timer = QTimer(self) @@ -42,7 +44,9 @@ class RollingTextWidget(QWidget): def update_text(self): # 每次显示下一个字符 self.current_index = (self.current_index + 1) % len(self.items) - self.label.setText(self.items[self.current_index].name) + stu = self.items[self.current_index] + self.soLabel.setText(stu.so) + self.nameLabel.setText(stu.name) def start_rolling(self): if not self.rolling_timer.isActive(): @@ -54,4 +58,13 @@ class RollingTextWidget(QWidget): self.finishSignal.emit() def set_items(self, items: list[PickerStudent]): - self.items = items + self.items = items[:] + random.shuffle(self.items) + + def show_result(self, student: PickerStudent): + self.soLabel.setText(student.so) + self.nameLabel.setText(student.name) + + def clear_text(self): + self.soLabel.clear() + self.nameLabel.clear() diff --git a/ui/pyui/picker_ui.py b/ui/pyui/picker_ui.py index ae5c20d..f63e81f 100644 --- a/ui/pyui/picker_ui.py +++ b/ui/pyui/picker_ui.py @@ -1,105 +1,12 @@ from PySide6.QtCore import Qt, Signal, QTimer -from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout, QWidget, QStackedWidget, QGridLayout, QFileDialog +from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout, QWidget, QFileDialog from qfluentwidgets import GroupHeaderCardWidget, PushButton, FluentIcon, PrimaryPushButton, IconWidget, BodyLabel, \ - SegmentedWidget, SpinBox, LargeTitleLabel, DisplayLabel, SubtitleLabel + SpinBox 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 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() +from ui.pyui.sub.picker import PickStudentLabelUi class PickStudentMode(QWidget): @@ -118,7 +25,7 @@ class PickStudentMode(QWidget): self.hintIcon = IconWidget(FluentIcon.INFO.icon(color=MAIN_THEME_COLOR)) self.hintLabel = BodyLabel("点击开始按钮以开始抽签 👉") self.spinbox = SpinBox() - self.rollingText = RollingTextWidget(self) + self.psui = PickStudentLabelUi(self) self.card.setTitle("输入选项") self.chooseBtn.setFixedWidth(120) @@ -127,6 +34,7 @@ class PickStudentMode(QWidget): self.spinbox.setRange(0, 6) self.spinbox.setFixedWidth(120) self.spinbox.setEnabled(False) + self.psui.hide() self.hintIcon.setFixedSize(16, 16) self.hintIcon.autoFillBackground() @@ -138,20 +46,20 @@ class PickStudentMode(QWidget): self.bottomLayout.addWidget(self.startButton, 0, Qt.AlignRight) self.bottomLayout.setAlignment(Qt.AlignVCenter) - self.group = self.card.addGroup(FluentIcon.DOCUMENT, "提问名单", "选择提问名单", self.chooseBtn) + 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.addWidget(self.psui) 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.psui.rollingText.finishSignal.connect(self.finish_rolling) # ============================== self.filepath = "" self.students = [] @@ -179,38 +87,31 @@ class PickStudentMode(QWidget): def start_rolling(self): self.students = PickerExcel.read_student() - self.rollingText.set_items(self.students) - self.rollingText.start_rolling() + self.psui.show() + self.psui.rollingText.set_items(self.students) + self.psui.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) + self.psui.rollingText.show_result(stu) 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()) + self.psui.show_scoring() + self.psui.scoring.submitSignal.connect(lambda score: self.scoring_finished(score, stu)) - -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() + def scoring_finished(self, score: int, student: PickerStudent): + student.append_score(score) + PickerExcel.write_back(student) + self.psui.hide() + self.startButton.setEnabled(True) class PickerWidget(Widget): @@ -220,29 +121,10 @@ class PickerWidget(Widget): 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.addWidget(self.psm) 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) diff --git a/ui/pyui/sub/picker.py b/ui/pyui/sub/picker.py new file mode 100644 index 0000000..c868153 --- /dev/null +++ b/ui/pyui/sub/picker.py @@ -0,0 +1,95 @@ +import sys + +from PySide6.QtCore import Signal +from PySide6.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QApplication, QGridLayout +from qfluentwidgets import PushButton, SpinBox, PrimaryPushButton, \ + BodyLabel + +from ui.components.widget import RollingTextWidget + + +class QuickScoringKeyBoard(QWidget): + scoringSignal = Signal(int) + + def __init__(self, parent=None): + super().__init__(parent) + self.vBoxLayout = QVBoxLayout(self) + self.keyBoardLayout = QGridLayout() + buttons = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 100] + + for i, num in enumerate(buttons): + row = i // 3 + col = i % 3 + btn = PushButton(str(num)) + btn.setFixedWidth(120) + btn.clicked.connect(lambda _, n=num: self.scoringSignal.emit(n)) + self.keyBoardLayout.addWidget(btn, row, col) + + self.vBoxLayout.addLayout(self.keyBoardLayout) + + +class QuickScoring(QWidget): + submitSignal = Signal(int) + + def __init__(self, parent=None): + super().__init__(parent) + + self.hBoxLayout = QHBoxLayout(self) + self.btnVBoxLayout = QVBoxLayout(self) + self.spinboxLayout = QHBoxLayout(self) + self.spinboxLabel = BodyLabel("得分", self) + self.gradeSpinBox = SpinBox(self) + self.submitButton = PrimaryPushButton("确定", self) + self.resetButton = PushButton("重置", self) + self.keyboard = QuickScoringKeyBoard(self) + + self.gradeSpinBox.setRange(0, 100) + self.submitButton.setFixedWidth(120) + self.resetButton.setFixedWidth(120) + + self.spinboxLayout.addWidget(self.spinboxLabel) + self.spinboxLayout.addWidget(self.gradeSpinBox) + + self.btnVBoxLayout.addStretch() + self.btnVBoxLayout.addWidget(self.submitButton) + self.btnVBoxLayout.addWidget(self.resetButton) + self.btnVBoxLayout.addStretch() + + self.hBoxLayout.addStretch() + self.hBoxLayout.addLayout(self.spinboxLayout) + self.hBoxLayout.addLayout(self.btnVBoxLayout) + self.hBoxLayout.addWidget(self.keyboard) + self.hBoxLayout.addStretch() + + self.keyboard.scoringSignal.connect(lambda x: self.gradeSpinBox.setValue(x)) + self.resetButton.clicked.connect(lambda: self.gradeSpinBox.clear()) + self.submitButton.clicked.connect(lambda: self.submitSignal.emit(self.gradeSpinBox.value())) + + +class PickStudentLabelUi(QWidget): + def __init__(self, parent=None): + super().__init__(parent) + self.vBoxLayout = QVBoxLayout(self) + self.rollingText = RollingTextWidget(self) + self.scoring = QuickScoring(self) + + self.scoring.hide() + + self.vBoxLayout.addWidget(self.rollingText) + self.vBoxLayout.addWidget(self.scoring) + self.vBoxLayout.addStretch() + + def show_scoring(self): + self.scoring.show() + + def hideEvent(self, event, /): + super().hideEvent(event) + self.scoring.gradeSpinBox.clear() + self.scoring.hide() + + +if __name__ == '__main__': + app = QApplication(sys.argv) + window = PickStudentLabelUi() + window.show() + sys.exit(app.exec())