import sys from PySide6 import QtCore, QtWidgets, QtGui from PySide6.QtGui import QFont from utils import read_excel, save_to_excel from solve import solve_program from datetime import datetime import traceback class MyWidget(QtWidgets.QWidget): def __init__(self): super().__init__() self.main_layout = QtWidgets.QVBoxLayout() self.setLayout(self.main_layout) bold_font = QFont() bold_font.setBold(True) # 设置字体加粗 thin_font = QFont() thin_font.setBold(False) # 设置字体不加粗 # 选择文件部分 self._excel_dir = None self.group_box_1 = QtWidgets.QGroupBox("Step 1. 选择问卷星导出的表格文件") self.group_box_1.setFont(bold_font) self.openfile_layout = QtWidgets.QVBoxLayout() self.button_openfile = QtWidgets.QPushButton("选择文件") self.button_openfile.setFont(thin_font) self.button_openfile.clicked.connect(self.open_file) self.label_openfile = QtWidgets.QLabel("未选择文件") self.label_openfile.setFont(thin_font) self.openfile_layout.addWidget(self.button_openfile) self.openfile_layout.addWidget(self.label_openfile) self.group_box_1.setLayout(self.openfile_layout) self.main_layout.addWidget(self.group_box_1) # 限制条件部分 self.group_box_2 = QtWidgets.QGroupBox("Step 2. 输入限制条件") self.group_box_2.setFont(bold_font) self.cond_layout_overall = QtWidgets.QVBoxLayout() self.group_box_2.setLayout(self.cond_layout_overall) # 自动模式开关 self._auto_mode = True self.cond_layout_0 = QtWidgets.QVBoxLayout() self.auto_switch = QtWidgets.QCheckBox("自动调参模式") self.auto_switch.setTristate(False) # 禁用三态模式 self.auto_switch.setChecked(True) self.auto_switch.stateChanged.connect(self.on_switch_toggled) self.auto_switch.setFont(thin_font) self.cond_layout_0.addWidget(self.auto_switch) self.cond_layout_overall.addLayout(self.cond_layout_0) # 限制条件1 self.cond_layout_1 = QtWidgets.QHBoxLayout() # 文字1 self.label_cond_1_1 = QtWidgets.QLabel("每班次人数:", self) self.label_cond_1_1.setFont(thin_font) # 数字输入框1 self.line_edit_1_1 = QtWidgets.QLineEdit(self) self.line_edit_1_1.setFont(thin_font) self.line_edit_1_1.setValidator(QtGui.QIntValidator()) # 设置只接受整数 self.line_edit_1_1.setPlaceholderText("无限制") self.line_edit_1_1.setText("4") self.line_edit_1_1.setEnabled(False) # 文字2 self.label_cond_1_2 = QtWidgets.QLabel("到", self) self.label_cond_1_2.setFont(thin_font) # 数字输入框2 self.line_edit_1_2 = QtWidgets.QLineEdit(self) self.line_edit_1_2.setFont(thin_font) self.line_edit_1_2.setValidator(QtGui.QIntValidator()) # 设置只接受整数 self.line_edit_1_2.setPlaceholderText("无限制") self.line_edit_1_2.setText("8") self.line_edit_1_2.setEnabled(False) self.cond_layout_1.addWidget(self.label_cond_1_1) self.cond_layout_1.addWidget(self.line_edit_1_1) self.cond_layout_1.addWidget(self.label_cond_1_2) self.cond_layout_1.addWidget(self.line_edit_1_2) self.cond_layout_overall.addLayout(self.cond_layout_1) # 限制条件2 self.cond_layout_2 = QtWidgets.QHBoxLayout() # 文字1 self.label_cond_2_1 = QtWidgets.QLabel("每班次电脑或电器的老人数:", self) self.label_cond_2_1.setFont(thin_font) # 数字输入框1 self.line_edit_2_1 = QtWidgets.QLineEdit(self) self.line_edit_2_1.setFont(thin_font) self.line_edit_2_1.setValidator(QtGui.QIntValidator()) # 设置只接受整数 self.line_edit_2_1.setPlaceholderText("无限制") self.line_edit_2_1.setEnabled(False) # 文字2 self.label_cond_2_2 = QtWidgets.QLabel("到", self) self.label_cond_2_2.setFont(thin_font) # 数字输入框2 self.line_edit_2_2 = QtWidgets.QLineEdit(self) self.line_edit_2_2.setFont(thin_font) self.line_edit_2_2.setValidator(QtGui.QIntValidator()) # 设置只接受整数 self.line_edit_2_2.setPlaceholderText("无限制") self.line_edit_2_2.setText("2") self.line_edit_2_2.setEnabled(False) self.cond_layout_2.addWidget(self.label_cond_2_1) self.cond_layout_2.addWidget(self.line_edit_2_1) self.cond_layout_2.addWidget(self.label_cond_2_2) self.cond_layout_2.addWidget(self.line_edit_2_2) self.cond_layout_overall.addLayout(self.cond_layout_2) # 限制条件3 self.cond_layout_3 = QtWidgets.QHBoxLayout() # 文字1 self.label_cond_3_1 = QtWidgets.QLabel("每班次老人数:", self) self.label_cond_3_1.setFont(thin_font) # 数字输入框1 self.line_edit_3_1 = QtWidgets.QLineEdit(self) self.line_edit_3_1.setFont(thin_font) self.line_edit_3_1.setValidator(QtGui.QIntValidator()) self.line_edit_3_1.setPlaceholderText("无限制") self.line_edit_3_1.setText("1") self.line_edit_3_1.setEnabled(False) # 文字2 self.label_cond_3_2 = QtWidgets.QLabel("到", self) self.label_cond_3_2.setFont(thin_font) # 数字输入框2 self.line_edit_3_2 = QtWidgets.QLineEdit(self) self.line_edit_3_2.setFont(thin_font) self.line_edit_3_2.setValidator(QtGui.QIntValidator()) self.line_edit_3_2.setPlaceholderText("无限制") self.line_edit_3_2.setEnabled(False) self.cond_layout_3.addWidget(self.label_cond_3_1) self.cond_layout_3.addWidget(self.line_edit_3_1) self.cond_layout_3.addWidget(self.label_cond_3_2) self.cond_layout_3.addWidget(self.line_edit_3_2) self.cond_layout_overall.addLayout(self.cond_layout_3) # 限制条件4 self.cond_layout_4 = QtWidgets.QHBoxLayout() # 文字1 self.label_cond_4_1 = QtWidgets.QLabel("每班次人资小朋友数:", self) self.label_cond_4_1.setFont(thin_font) # 数字输入框1 self.line_edit_4_1 = QtWidgets.QLineEdit(self) self.line_edit_4_1.setFont(thin_font) self.line_edit_4_1.setValidator(QtGui.QIntValidator()) self.line_edit_4_1.setPlaceholderText("无限制") self.line_edit_4_1.setText("1") self.line_edit_4_1.setEnabled(False) # 文字2 self.label_cond_4_2 = QtWidgets.QLabel("到", self) self.label_cond_4_2.setFont(thin_font) # 数字输入框2 self.line_edit_4_2 = QtWidgets.QLineEdit(self) self.line_edit_4_2.setFont(thin_font) self.line_edit_4_2.setValidator(QtGui.QIntValidator()) self.line_edit_4_2.setPlaceholderText("无限制") self.line_edit_4_2.setEnabled(False) self.cond_layout_4.addWidget(self.label_cond_4_1) self.cond_layout_4.addWidget(self.line_edit_4_1) self.cond_layout_4.addWidget(self.label_cond_4_2) self.cond_layout_4.addWidget(self.line_edit_4_2) self.cond_layout_overall.addLayout(self.cond_layout_4) # 限制条件5 self.cond_layout_5 = QtWidgets.QHBoxLayout() # 文字1 self.label_cond_5_1 = QtWidgets.QLabel("每班次小朋友数:", self) self.label_cond_5_1.setFont(thin_font) # 数字输入框1 self.line_edit_5_1 = QtWidgets.QLineEdit(self) self.line_edit_5_1.setFont(thin_font) self.line_edit_5_1.setValidator(QtGui.QIntValidator()) self.line_edit_5_1.setPlaceholderText("无限制") self.line_edit_5_1.setText("2") self.line_edit_5_1.setEnabled(False) # 文字2 self.label_cond_5_2 = QtWidgets.QLabel("到", self) self.label_cond_5_2.setFont(thin_font) # 数字输入框2 self.line_edit_5_2 = QtWidgets.QLineEdit(self) self.line_edit_5_2.setFont(thin_font) self.line_edit_5_2.setValidator(QtGui.QIntValidator()) self.line_edit_5_2.setPlaceholderText("无限制") self.line_edit_5_2.setEnabled(False) self.cond_layout_5.addWidget(self.label_cond_5_1) self.cond_layout_5.addWidget(self.line_edit_5_1) self.cond_layout_5.addWidget(self.label_cond_5_2) self.cond_layout_5.addWidget(self.line_edit_5_2) self.cond_layout_overall.addLayout(self.cond_layout_5) self.main_layout.addWidget(self.group_box_2) # 处理文件部分 self.group_box_3 = QtWidgets.QGroupBox("Step 3. 获取最优结果") self.group_box_3.setFont(bold_font) self.button_solve = QtWidgets.QPushButton("开始排班!") self.button_solve.setFont(thin_font) self.text_solve = QtWidgets.QTextEdit(self) self.text_solve.setFont(thin_font) self.text_solve.setReadOnly(True) # 设置为只读模式 # 创建 QScrollArea 并将 QTextEdit 添加进去 self.scroll_area = QtWidgets.QScrollArea(self) self.scroll_area.setWidget(self.text_solve) # 将 QTextEdit 设置为 QScrollArea 的内容 self.scroll_area.setWidgetResizable(True) # 允许 QTextEdit 根据 QScrollArea 的大小自动调整 self.button_solve.clicked.connect(self.magic) self.solve_layout = QtWidgets.QVBoxLayout() self.solve_layout.addWidget(self.scroll_area) self.solve_layout.addWidget(self.button_solve) self.group_box_3.setLayout(self.solve_layout) self.main_layout.addWidget(self.group_box_3) def open_file(self): # 弹出文件选择对话框 file_path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "打开文件", "", "所有文件 (*.*);;文本文件 (*.txt)") # 如果用户选择了文件,则更新标签显示文件路径 if file_path: self.label_openfile.setText(f"选中文件: {file_path}") self._excel_dir = file_path def on_switch_toggled(self, state): """处理开关状态的变化事件""" if state == 0: # 关闭 self._auto_mode = False self.line_edit_1_1.setEnabled(True) self.line_edit_1_2.setEnabled(True) self.line_edit_2_1.setEnabled(True) self.line_edit_2_2.setEnabled(True) self.line_edit_3_1.setEnabled(True) self.line_edit_3_2.setEnabled(True) self.line_edit_4_1.setEnabled(True) self.line_edit_4_2.setEnabled(True) self.line_edit_5_1.setEnabled(True) self.line_edit_5_2.setEnabled(True) elif state == 2: # 打开 self._auto_mode = True self.line_edit_1_1.setEnabled(False) self.line_edit_1_2.setEnabled(False) self.line_edit_2_1.setEnabled(False) self.line_edit_2_2.setEnabled(False) self.line_edit_3_1.setEnabled(False) self.line_edit_3_2.setEnabled(False) self.line_edit_4_1.setEnabled(False) self.line_edit_4_2.setEnabled(False) self.line_edit_5_1.setEnabled(False) self.line_edit_5_2.setEnabled(False) @QtCore.Slot() def magic(self): try: self.text_solve.append(f"******** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} ********") # 读取文件 if self._excel_dir is None: self.text_solve.append("请先选择文件!") return self.text_solve.append("开始排班...") self.text_solve.append("读取文件中...") all_data, index_to_name_dict, preference_mat, want_num_array, is_new_array, is_tech_array, is_hr_array = read_excel(self._excel_dir) self.text_solve.append("读取文件成功!") # 计算并打印一些统计信息 self.text_solve.append("信息统计:") stu_num = len(index_to_name_dict) self.text_solve.append(f"\t学生总人数:{stu_num}") class_num = len(preference_mat[0]) self.text_solve.append(f"\t班次总数:{class_num}") tech_sum = 0 for i in range(len(preference_mat)): if not is_new_array[i] and is_tech_array[i]: tech_sum += want_num_array[i] self.text_solve.append(f"\t电脑部或电气部的所有老人的意愿班次之和:{tech_sum}") all_sum = sum(want_num_array) self.text_solve.append(f"\t所有人的意愿班次之和:{all_sum}") self.text_solve.append(f"\t平均每班人数:{all_sum/20}") min_prefer = 100 for j in range(len(preference_mat[0])): prefer_num = sum([preference_mat[i][j] for i in range(len(preference_mat))]) min_prefer = min(min_prefer, prefer_num) self.text_solve.append(f"\t所有班次中最少拥有意愿数:{min_prefer}") vars = [] if self._auto_mode: self.text_solve.append(f"自动调参开始...") # 至少?个电脑或电器的老人 tech_old_min_try_list = [1,0] tech_old_min_num = None for _tech_min in tech_old_min_try_list: vars = solve_program(preference_mat=preference_mat, want_num_array=want_num_array, is_new_array=is_new_array, is_tech_array=is_tech_array, is_hr_array=is_hr_array, num_min=None, num_max=None, num_tech_min=_tech_min, num_tech_max=None, num_old_min=None, num_old_max=None, num_hr_min=None, num_hr_max=None, num_new_min=None, num_new_max=None) if vars: tech_old_min_num = _tech_min break # 至多?个电脑或电器的老人 tech_old_max_try_list = [1,2,3,4,5,6,7,8] tech_old_max_num = None for _tech_max in tech_old_max_try_list: vars = solve_program(preference_mat=preference_mat, want_num_array=want_num_array, is_new_array=is_new_array, is_tech_array=is_tech_array, is_hr_array=is_hr_array, num_min=None, num_max=None, num_tech_min=tech_old_min_num, num_tech_max=_tech_max, num_old_min=None, num_old_max=None, num_hr_min=None, num_hr_max=None, num_new_min=None, num_new_max=None) if vars: tech_old_max_num = _tech_max break # 每班至少?个老人 num_old_min_try_list = [2,1,0] num_old_min = None for _num_old_min in num_old_min_try_list: vars = solve_program(preference_mat=preference_mat, want_num_array=want_num_array, is_new_array=is_new_array, is_tech_array=is_tech_array, is_hr_array=is_hr_array, num_min=None, num_max=None, num_tech_min=tech_old_min_num, num_tech_max=tech_old_max_num, num_old_min=_num_old_min, num_old_max=None, num_hr_min=None, num_hr_max=None, num_new_min=None, num_new_max=None) if vars: num_old_min = _num_old_min break # 每班至多?个老人 num_old_max_try_list = [1,2,3,4,5,6,7,8] num_old_max = None for _num_old_max in num_old_max_try_list: vars = solve_program(preference_mat=preference_mat, want_num_array=want_num_array, is_new_array=is_new_array, is_tech_array=is_tech_array, is_hr_array=is_hr_array, num_min=None, num_max=None, num_tech_min=tech_old_min_num, num_tech_max=tech_old_max_num, num_old_min=num_old_min, num_old_max=_num_old_max, num_hr_min=None, num_hr_max=None, num_new_min=None, num_new_max=None) if vars: num_old_max = _num_old_max break # 每班至少?人 num_min_try_list = [4,3,2,1] num_min = None for _num_min in num_min_try_list: vars = solve_program(preference_mat=preference_mat, want_num_array=want_num_array, is_new_array=is_new_array, is_tech_array=is_tech_array, is_hr_array=is_hr_array, num_min=_num_min, num_max=None, num_tech_min=tech_old_min_num, num_tech_max=tech_old_max_num, num_old_min=num_old_min, num_old_max=num_old_max, num_hr_min=None, num_hr_max=None, num_new_min=None, num_new_max=None) if vars: num_min = _num_min break # 每班至多?人 num_max_try_list = [8,9,10,11,12,13,14,15] num_max = None for _num_max in num_max_try_list: vars = solve_program(preference_mat=preference_mat, want_num_array=want_num_array, is_new_array=is_new_array, is_tech_array=is_tech_array, is_hr_array=is_hr_array, num_min=num_min, num_max=_num_max, num_tech_min=tech_old_min_num, num_tech_max=tech_old_max_num, num_old_min=num_old_min, num_old_max=num_old_max, num_hr_min=None, num_hr_max=None, num_new_min=None, num_new_max=None) if vars: num_max = _num_max break # 每班至少?个小朋友 num_new_min_try_list = [1,0] num_new_min = None for _num_new_min in num_new_min_try_list: vars = solve_program(preference_mat=preference_mat, want_num_array=want_num_array, is_new_array=is_new_array, is_tech_array=is_tech_array, is_hr_array=is_hr_array, num_min=num_min, num_max=num_max, num_tech_min=tech_old_min_num, num_tech_max=tech_old_max_num, num_old_min=num_old_min, num_old_max=num_old_max, num_hr_min=None, num_hr_max=None, num_new_min=_num_new_min, num_new_max=None) if vars: num_new_min = _num_new_min break # 每班至少?个人资小朋友 num_hr_min_try_list = [1,0] num_hr_min = None for _num_hr_min in num_hr_min_try_list: vars = solve_program(preference_mat=preference_mat, want_num_array=want_num_array, is_new_array=is_new_array, is_tech_array=is_tech_array, is_hr_array=is_hr_array, num_min=num_min, num_max=num_max, num_tech_min=tech_old_min_num, num_tech_max=tech_old_max_num, num_old_min=num_old_min, num_old_max=num_old_max, num_hr_min=_num_hr_min, num_hr_max=None, num_new_min=num_new_min, num_new_max=None) if vars: num_hr_min = _num_hr_min break # 每班至多?个人资小朋友 num_hr_max_try_list = [1,2,3,4,5,6,7,8] num_hr_max = None for _num_hr_max in num_hr_max_try_list: vars = solve_program(preference_mat=preference_mat, want_num_array=want_num_array, is_new_array=is_new_array, is_tech_array=is_tech_array, is_hr_array=is_hr_array, num_min=num_min, num_max=num_max, num_tech_min=tech_old_min_num, num_tech_max=tech_old_max_num, num_old_min=num_old_min, num_old_max=num_old_max, num_hr_min=num_hr_min, num_hr_max=_num_hr_max, num_new_min=num_new_min, num_new_max=None) if vars: num_hr_max = _num_hr_max break if vars is None: self.text_solve.append("自动求解失败!请尝试手动调参!") return else: self.text_solve.append("自动计算成功!") self.text_solve.append("参数:") self.text_solve.append(f"\t每班人数:[{num_min}, {num_max}]") self.text_solve.append(f"\t每班电脑或电器的老人数:[{tech_old_min_num}, {tech_old_max_num}]") self.text_solve.append(f"\t每班老人数:[{num_old_min}, {num_old_max}]") self.text_solve.append(f"\t每班人资部小朋友数:[{num_hr_min}, {num_hr_max}]") self.text_solve.append(f"\t每班小朋友数:[{num_new_min}, inf]") else: # 读取限制条件 num_min = int(self.line_edit_1_1.text()) if self.line_edit_1_1.text() else None num_max = int(self.line_edit_1_2.text()) if self.line_edit_1_2.text() else None num_tech_min = int(self.line_edit_2_1.text()) if self.line_edit_2_1.text() else None num_tech_max = int(self.line_edit_2_2.text()) if self.line_edit_2_2.text() else None num_old_min = int(self.line_edit_3_1.text()) if self.line_edit_3_1.text() else None num_old_max = int(self.line_edit_3_2.text()) if self.line_edit_3_2.text() else None num_hr_min = int(self.line_edit_4_1.text()) if self.line_edit_4_1.text() else None num_hr_max = int(self.line_edit_4_2.text()) if self.line_edit_4_2.text() else None num_new_min = int(self.line_edit_5_1.text()) if self.line_edit_5_1.text() else None num_new_max = int(self.line_edit_5_2.text()) if self.line_edit_5_2.text() else None self.text_solve.append("计算最优解中...") vars = solve_program(preference_mat=preference_mat, want_num_array=want_num_array, is_new_array=is_new_array, is_tech_array=is_tech_array, is_hr_array=is_hr_array, num_min=num_min, num_max=num_max, num_tech_min=num_tech_min, num_tech_max=num_tech_max, num_old_min=num_old_min, num_old_max=num_old_max, num_hr_min=num_hr_min, num_hr_max=num_hr_max, num_new_min=num_new_min, num_new_max=num_new_max) if vars is None: self.text_solve.append("在目前限制条件下无解!请尝试更改限制条件!") return else: self.text_solve.append("计算最优解成功!") # 保存结果到 excel self.text_solve.append("保存结果中...") time_str = datetime.now().strftime('%Y%m%d_%H%M%S') save_dir = f"result_{time_str}.xlsx" if vars is not None: save_to_excel(vars, all_data, index_to_name_dict, save_dir) self.text_solve.append(f"保存结果成功!保存路径:{save_dir}") except Exception as e: self.text_solve.append("程序出现严重错误,请联系开发者解决问题!!!") self.text_solve.append(f"Error Details:\n{traceback.format_exc()}") if __name__ == "__main__": app = QtWidgets.QApplication([]) widget = MyWidget() widget.setWindowTitle("EVA 值班排班软件") widget.resize(600, 600) widget.show() sys.exit(app.exec())