赞
踩
接上两篇gpt-4o考场安排-CSDN博客,考场分层次安排,最终exe版-CSDN博客
当然你也可以只看这一篇。
1、自动分页,每个考场打印一页
2、添加了打印试场单页眉
3、添加了页脚 第X页,其X页, 打印时间
-
- import sys
-
- import pandas as pd
-
- import random
-
- import openpyxl
-
- from openpyxl.worksheet.pagebreak import Break
-
- from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
-
- from openpyxl.worksheet.page import PageMargins
-
- from datetime import datetime
-
- from PyQt5.QtWidgets import (
-
- QApplication, QLabel, QLineEdit, QPushButton, QFileDialog,
-
- QVBoxLayout, QWidget, QMessageBox
-
- )
-
- from PyQt5.QtGui import QFont as QPyQtFont
-
-
- def load_data(filename):
- return pd.read_excel(filename)
-
-
- def save_data(df, filename, exam_name):
- df.to_excel(filename, index=False)
-
- format_excel(filename, df, exam_name)
-
-
- def assign_seats(df, total_halls, start_hall=1):
- grouped = df.groupby('班级')
-
- groups = [group.sample(frac=1).reset_index(drop=True) for _, group in grouped]
-
- groups.sort(key=lambda x: len(x), reverse=True)
-
- iterators = [iter(group.iterrows()) for group in groups]
-
- arranged_data = []
-
- hall_number = start_hall
-
- seat_number = 1
-
- total_students = len(df)
-
- students_per_hall = total_students // total_halls
-
- extra_students = total_students % total_halls
-
- while iterators:
-
- random.shuffle(iterators)
-
- for it in iterators[:]:
-
- try:
-
- _, student = next(it)
-
- student_data = student.to_dict()
-
- student_data["考场"] = hall_number
-
- student_data["考号"] = f"{seat_number:02d}"
-
- arranged_data.append(student_data)
-
- seat_number += 1
-
- if seat_number > students_per_hall + (1 if hall_number - start_hall + 1 <= extra_students else 0):
- hall_number += 1
-
- seat_number = 1
-
- if hall_number >= start_hall + total_halls:
- hall_number = start_hall
-
- except StopIteration:
-
- iterators.remove(it)
-
- return arranged_data
-
-
- def check_and_adjust_seating(arranged_data):
- def has_adjacent_same_class(data):
-
- for i in range(len(data) - 1):
-
- if data[i]['班级'] == data[i + 1]['班级']:
- return i
-
- return -1
-
- def find_valid_swap(index, data):
-
- current_class = data[index]['班级']
-
- for j in range(len(data)):
-
- if j != index and data[j]['班级'] != current_class:
-
- if (j == 0 or data[j - 1]['班级'] != current_class) and (
-
- j == len(data) - 1 or data[j + 1]['班级'] != current_class):
- return j
-
- return -1
-
- swap_operations = []
-
- while True:
-
- index = has_adjacent_same_class(arranged_data)
-
- if index == -1:
- break
-
- swap_index = find_valid_swap(index + 1, arranged_data)
-
- if swap_index == -1:
- raise ValueError("Cannot find a valid swap to adjust the seating arrangement.")
-
- swap_operations.append((index + 1, swap_index))
-
- arranged_data[index + 1], arranged_data[swap_index] = arranged_data[swap_index], arranged_data[index + 1]
-
- return arranged_data, swap_operations
-
-
- def reassign_seats(arranged_data, total_halls, start_hall=1):
- hall_number = start_hall
-
- seat_number = 1
-
- total_students = len(arranged_data)
-
- students_per_hall = total_students // total_halls
-
- extra_students = total_students % total_halls
-
- for i, student in enumerate(arranged_data):
-
- student['考场'] = hall_number
-
- student['考号'] = f"{seat_number:02d}"
-
- seat_number += 1
-
- if seat_number > students_per_hall + (1 if hall_number - start_hall + 1 <= extra_students else 0):
- hall_number += 1
-
- seat_number = 1
-
- if hall_number >= start_hall + total_halls:
- hall_number = start_hall
-
- return arranged_data
-
-
- def format_excel(filename, df, exam_name):
-
- if '层次' in df.columns:
-
- df = df.drop(columns=['层次'])
-
-
-
- wb = openpyxl.Workbook()
-
- ws = wb.active
-
- ws.title = "考场安排结果"
-
-
-
- # 将标题从第一行开始写入
-
- for col_num, column_title in enumerate(df.columns, 1):
-
- cell = ws.cell(row=1, column=col_num, value=column_title)
-
- cell.font = Font(bold=True, color="FFFFFF", size=16)
-
- cell.fill = PatternFill(start_color="4F81BD", end_color="4F81BD", fill_type="solid")
-
- cell.alignment = Alignment(horizontal="center", vertical="center")
-
-
-
- for row_num, row_data in enumerate(df.itertuples(index=False, name=None), 2):
-
- for col_num, cell_value in enumerate(row_data, 1):
-
- ws.cell(row=row_num, column=col_num, value=cell_value)
-
-
-
- for col in ws.columns:
-
- max_length = 0
-
- column = col[0].column_letter
-
- # Check if the column header is "考号"
-
- if ws.cell(row=1, column=col[0].col_idx).value == "考号":
-
- adjusted_width = 20 # 设置考号列的宽度为20
-
- else:
-
- for cell in col:
-
- if cell.value is not None:
-
- max_length = max(max_length, len(str(cell.value)))
-
- adjusted_width = max_length + 10
-
- ws.column_dimensions[column].width = adjusted_width
-
-
-
- # 调整页面边距
-
- ws.page_margins = PageMargins(left=1.5, right=0.75, top=1.2, bottom=0.5)
-
-
-
- ws.print_title_rows = '1:1' # 标题行在第一行开始
-
- ws.page_setup.orientation = 'portrait'
-
- ws.page_setup.paperSize = ws.PAPERSIZE_A4
-
- ws.page_setup.fitToPage = True
-
- ws.page_setup.fitToWidth = 1
-
- ws.page_setup.fitToHeight = False
-
- ws.page_setup.horizontalCentered = True
-
- ws.page_setup.verticalCentered = True
-
-
-
- page_height_in_inches = 11.69 - ws.page_margins.top - ws.page_margins.bottom
-
- page_height_in_points = page_height_in_inches * 72
-
- header_height_in_points = 50
-
- available_row_height_in_points = page_height_in_points - header_height_in_points
-
-
-
- hall_groups = df.groupby('考场')
-
- previous_max_row = 1 # 前一个最大行号调整为1以考虑空行
-
- thin_border = Border(left=Side(style='thin'), right=Side(style='thin'), top=Side(style='thin'), bottom=Side(style='thin'))
-
- for hall_number, (hall_id, hall_df) in enumerate(hall_groups):
-
- row_count = len(hall_df) + 1
-
- max_row = hall_df.index[-1] + 2 # 从第二行开始每个考场的数据
-
- row_height = available_row_height_in_points / row_count
-
- row_height = max(row_height, 15)
-
- for row in range(previous_max_row, max_row + 1):
-
- ws.row_dimensions[row].height = row_height
-
- for cell in ws[row]:
-
- cell.border = thin_border
-
- cell.font = Font(size=15)
-
- cell.alignment = Alignment(horizontal="center", vertical="center")
-
- if max_row < ws.max_row:
-
- ws.row_breaks.append(Break(max_row))
-
- previous_max_row = max_row + 1
-
-
-
- # 添加页眉和页脚,并使用制表符来向左移动页眉
-
- ws.oddHeader.center.text = f"&\"微软雅黑,Bold\"&20\t{exam_name}" # 添加制表符以向左移动
-
- ws.oddFooter.center.text = "第 &P 页,共 &N 页"
-
- ws.oddFooter.right.text = f"&D &T"
-
-
-
- wb.save(filename)
-
-
- def dataframe_to_rows(df, index=True, header=True):
- if header:
-
- rows = [list(df.columns)]
-
- else:
-
- rows = [[]]
-
- for row in df.itertuples(index=index, name=None):
- rows.append(list(row)[1:])
-
- return rows
-
-
- def run_allocation(input_filename, a_total_halls, b_total_halls, start_level, exam_name):
- df = load_data(input_filename)
-
- if start_level == 'b':
-
- other_level = 'a'
-
- first_total_halls = b_total_halls
-
- second_total_halls = a_total_halls
-
- else:
-
- other_level = 'b'
-
- first_total_halls = a_total_halls
-
- second_total_halls = b_total_halls
-
- first_level_students = df[df['层次'] == start_level]
-
- arranged_first_students = assign_seats(first_level_students, first_total_halls, start_hall=1)
-
- adjusted_first_students, _ = check_and_adjust_seating(arranged_first_students)
-
- final_first_students = reassign_seats(adjusted_first_students, first_total_halls, start_hall=1)
-
- second_level_students = df[df['层次'] == other_level]
-
- arranged_second_students = assign_seats(second_level_students, second_total_halls, start_hall=first_total_halls + 1)
-
- adjusted_second_students, _ = check_and_adjust_seating(arranged_second_students)
-
- final_second_students = reassign_seats(adjusted_second_students, second_total_halls,
- start_hall=first_total_halls + 1)
-
- combined_students = final_first_students + final_second_students
-
- arranged_df = pd.DataFrame(combined_students)
-
- current_time = datetime.now().strftime("%Y%m%d_%H%M%S")
-
- output_filename = f"考场安排结果_{current_time}.xlsx"
-
- save_data(arranged_df, output_filename, exam_name)
-
- return output_filename
-
-
- class ExamArrangementApp(QWidget):
-
- def __init__(self):
-
- super().__init__()
-
- self.initUI()
-
- def initUI(self):
-
- layout = QVBoxLayout()
-
- font = QPyQtFont("Arial", 14)
-
- self.file_label = QLabel('选择文件:')
-
- self.file_label.setFont(font)
-
- layout.addWidget(self.file_label)
-
- self.file_btn = QPushButton('选择文件')
-
- self.file_btn.setFont(font)
-
- self.file_btn.clicked.connect(self.select_file)
-
- layout.addWidget(self.file_btn)
-
- self.exam_name_label = QLabel('考试名称:')
-
- self.exam_name_label.setFont(font)
-
- layout.addWidget(self.exam_name_label)
-
- self.exam_name_input = QLineEdit()
-
- self.exam_name_input.setFont(font)
-
- layout.addWidget(self.exam_name_input)
-
- self.a_halls_label = QLabel('A层次考场数:')
-
- self.a_halls_label.setFont(font)
-
- layout.addWidget(self.a_halls_label)
-
- self.a_halls_input = QLineEdit()
-
- self.a_halls_input.setFont(font)
-
- layout.addWidget(self.a_halls_input)
-
- self.b_halls_label = QLabel('B层次考场数:')
-
- self.b_halls_label.setFont(font)
-
- layout.addWidget(self.b_halls_label)
-
- self.b_halls_input = QLineEdit()
-
- self.b_halls_input.setFont(font)
-
- layout.addWidget(self.b_halls_input)
-
- self.start_level_label = QLabel('首先开始编号的层次 (a/b):')
-
- self.start_level_label.setFont(font)
-
- layout.addWidget(self.start_level_label)
-
- self.start_level_input = QLineEdit()
-
- self.start_level_input.setFont(font)
-
- layout.addWidget(self.start_level_input)
-
- self.run_btn = QPushButton('运行')
-
- self.run_btn.setFont(font)
-
- self.run_btn.clicked.connect(self.run)
-
- layout.addWidget(self.run_btn)
-
- self.setLayout(layout)
-
- self.setWindowTitle('考场安排工具,By Bobo googaobo@gmail.com')
-
- self.resize(900, 630)
-
- self.center()
-
- def select_file(self):
-
- options = QFileDialog.Options()
-
- options |= QFileDialog.DontUseNativeDialog
-
- file_name, _ = QFileDialog.getOpenFileName(self, "选择Excel文件", "", "Excel Files (*.xlsx);;All Files (*)",
- options=options)
-
- if file_name:
- self.file_label.setText(f'文件: {file_name}')
-
- self.input_filename = file_name
-
- def run(self):
-
- try:
-
- a_total_halls = int(self.a_halls_input.text())
-
- b_total_halls = int(self.b_halls_input.text())
-
- start_level = self.start_level_input.text()
-
- exam_name = self.exam_name_input.text()
-
- output_filename = run_allocation(self.input_filename, a_total_halls, b_total_halls, start_level, exam_name)
-
- QMessageBox.information(self, "成功", f"已成功生成文件:{output_filename}", QMessageBox.Ok)
-
- except Exception as e:
-
- QMessageBox.critical(self, "错误", str(e), QMessageBox.Ok)
-
- def center(self):
-
- qr = self.frameGeometry()
-
- cp = self.screen().availableGeometry().center()
-
- qr.moveCenter(cp)
-
- self.move(qr.topLeft())
-
-
- if __name__ == '__main__':
- app = QApplication(sys.argv)
-
- ex = ExamArrangementApp()
-
- ex.show()
-
- sys.exit(app.exec_())
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。