值日表自动排班生成器

2次阅读
没有评论

由于经常需要排值日表用Python做了一个值班表,
考虑到很多设备没有运行环境,已经转换为exe格式,并且在最后给出了源代码
值班表能够按顺序排列值日生,周六日自动跳过,节假日和轮休日可以手动修改,能够导出excel格式文件方便打印

链接: https://pan.baidu.com/s/1iu8EoHedtI5Cc1fmkA2HtA?pwd=52pj
提取码: 52pj

代码:
import tkinter as tk
from tkinter import ttk, messagebox
from datetime import datetime, timedelta
import calendar
from openpyxl import Workbook
from openpyxl.styles import Border, Side, Font, Alignment
from openpyxl.utils import get_column_letter

class CleaningScheduleApp:
def __init__(self, root):
self.root = root
self.root.title("值日表")
self.create_widgets()

def create_widgets(self):
# 值日生姓名输入
ttk.Label(self.root, text="值日生名单(每行一个):").grid(row=0, column=0, padx=10, pady=5, sticky="w")
self.name_text = tk.Text(self.root, height=5, width=30)
self.name_text.grid(row=1, column=0, padx=10, pady=5, sticky="we")

# 日期选择
ttk.Label(self.root, text="选择年份:").grid(row=2, column=0, padx=10, pady=5, sticky="w")
self.year_var = tk.IntVar(value=datetime.now().year)
self.year_spin = ttk.Spinbox(self.root, from_=2020, to=2030, textvariable=self.year_var)
self.year_spin.grid(row=3, column=0, padx=10, pady=5, sticky="w")

ttk.Label(self.root, text="选择月份:").grid(row=2, column=1, padx=10, pady=5, sticky="w")
self.month_var = tk.IntVar(value=datetime.now().month)
self.month_combo = ttk.Combobox(self.root, textvariable=self.month_var,
values=list(range(1, 13)), state="readonly")
self.month_combo.grid(row=3, column=1, padx=10, pady=5, sticky="w")

# 手动输入
ttk.Label(self.root, text="手动输入休息日(空格分隔,如1 2):").grid(row=4, column=0, padx=10, pady=5, sticky="w")
self.custom_holIDAys_entry = ttk.Entry(self.root)
self.custom_holidays_entry.grid(row=5, column=0, padx=10, pady=5, sticky="we")

ttk.Label(self.root, text="调休工作日(空格分隔,如5 6):").grid(row=4, column=1, padx=10, pady=5, sticky="w")
self.workdays_entry = ttk.Entry(self.root)
self.workdays_entry.grid(row=5, column=1, padx=10, pady=5, sticky="we")

# 按钮
ttk.Button(self.root, text="生成值日表", command=self.generate_schedule).grid(
row=6, column=0, columnspan=2, pady=10)
ttk.Button(self.root, text="导出 Excel", command=self.export_to_excel).grid(
row=7, column=0, columnspan=2, pady=10)

def get_names(self):
names = self.name_text.get("1.0", tk.END).strip().split("\n")
return [name.strip() for name in names if name.strip()]

def parse_dates(self, input_str, year, month):
try:
return {datetime(year, month, int(day.strip())) for day in input_str.split() if day.strip()}
except:
return set()

def generate_schedule(self):
names = self.get_names()
if not names:
messagebox.showerror("错误", "请输入至少一个值日生姓名")
return

try:
year = self.year_var.get()
month = self.month_var.get()
_, num_days = calendar.monthrange(year, month)

# 处理手动输入
manual_holidays = self.parse_dates(self.custom_holidays_entry.get(), year, month)
workdays = self.parse_dates(self.workdays_entry.get(), year, month)

schedule = []
name_index = 0
chinese_weekdays = {
"Monday": "星期一", "Tuesday": "星期二", "Wednesday": "星期三",
"Thursday": "星期四", "Friday": "星期五",
"Saturday": "星期六", "Sunday": "星期日"
}

for day in range(1, num_days + 1):
date = datetime(year, month, day)
weekday = date.strftime("%A")
weekday_chinese = chinese_weekdays[weekday]

# 判断逻辑
is_manual_holiday = date in manual_holidays
is_weekend = weekday_chinese in ["星期六", "星期日"]
is_workday = date in workdays

if is_manual_holiday or (is_weekend and not is_workday):
duty_student = ""
else:
duty_student = names[name_index]
name_index = (name_index + 1) % len(names)

schedule.append({
"日期": f"{day}号",
"星期": weekday_chinese,
"值日生": duty_student
})

self.display_schedule(schedule)

except Exception as e:
messagebox.showerror("错误", f"生成值日表时出错: {str(e)}")

def display_schedule(self, schedule):
# 清空之前的显示
for widget in self.root.grid_slaves():
if int(widget.grid_info()['row']) >= 8:
widget.destroy()

# 创建表格
tree = ttk.Treeview(self.root, columns=('Date', 'Weekday', 'Name'), show='headings', height=15)
tree.heading('Date', text='日期')
tree.heading('Weekday', text='星期')
tree.heading('Name', text='值日生')

for item in schedule:
tree.insert('', 'end', values=(item['日期'], item['星期'], item['值日生']))

tree.grid(row=8, column=0, columnspan=2, padx=10, pady=5, sticky="nsew")

# 滚动条
scrollbar = ttk.Scrollbar(self.root, orient="vertical", command=tree.yview)
scrollbar.grid(row=8, column=2, sticky='ns')
tree.configure(yscrollcommand=scrollbar.set)

def export_to_excel(self):
names = self.get_names()
if not names:
messagebox.showerror("错误", "请输入至少一个值日生姓名")
return

try:
year = self.year_var.get()
month = self.month_var.get()
month_name = datetime(year, month, 1).strftime("%B")
chinese_month_name = {
"January": "一月", "February": "二月", "March": "三月",
"April": "四月", "May": "五月", "June": "六月",
"July": "七月", "August": "八月", "September": "九月",
"October": "十月", "November": "十一月", "December": "十二月"
}[month_name]

# 生成数据
_, num_days = calendar.monthrange(year, month)
manual_holidays = self.parse_dates(self.custom_holidays_entry.get(), year, month)
workdays = self.parse_dates(self.workdays_entry.get(), year, month)

data = []
name_index = 0
chinese_weekdays = {
"Monday": "星期一", "Tuesday": "星期二", "Wednesday": "星期三",
"Thursday": "星期四", "Friday": "星期五",
"Saturday": "星期六", "Sunday": "星期日"
}

for day in range(1, num_days + 1):
date = datetime(year, month, day)
weekday = date.strftime("%A")
weekday_chinese = chinese_weekdays[weekday]

is_manual_holiday = date in manual_holidays
is_weekend = weekday_chinese in ["星期六", "星期日"]
is_workday = date in workdays

if is_manual_holiday or (is_weekend and not is_workday):
duty_student = ""
else:
duty_student = names[name_index]
name_index = (name_index + 1) % len(names)

data.append({
"日期": f"{day}号",
"星期": weekday_chinese,
"值日生": duty_student
})

# 创建Excel文件
wb = Workbook()
ws = wb.active
ws.insert_cols(1, 2)

# 设置标题
title = f"{chinese_month_name}值日表"
ws.merge_cells("C1:E1")
ws["C1"] = title
ws["C1"].font = Font(size=24, bold=True)
ws["C1"].alignment = Alignment(horizontal="center", vertical="center")

# 表头
headers = ["日期", "星期", "值日生"]
for col_idx, header in enumerate(headers, start=3):
cell = ws.cell(row=2, column=col_idx)
cell.value = header
cell.font = Font(bold=True)
cell.alignment = Alignment(horizontal="center", vertical="center")

# 数据
for row_idx, row_data in enumerate(data, start=3):
ws.cell(row=row_idx, column=3).value = row_data["日期"]
ws.cell(row=row_idx, column=4).value = row_data["星期"]
ws.cell(row=row_idx, column=5).value = row_data["值日生"]

# 格式设置
column_widths = [15, 18, 20]
for col_idx, width in enumerate(column_widths, start=3):
ws.column_dimensions[get_column_letter(col_idx)].width = width

ws.row_dimensions[1].height = 30
for row_idx in range(2, len(data) + 3):
ws.row_dimensions[row_idx].height = 20

border = Border(left=Side(style='thin'), right=Side(style='thin'),
top=Side(style='thin'), bottom=Side(style='thin'))
for row in ws.iter_rows(min_row=2, max_row=len(data)+2):
for cell in row[2:]:
cell.border = border
cell.alignment = Alignment(horizontal="center", vertical="center")

file_name = f"{year}_{month}_值日表.xlsx"
wb.save(file_name)
messagebox.showinfo("成功", f"值日表已导出为:{file_name}")

except Exception as e:
messagebox.showerror("错误", f"导出Excel时出错 {str(e)}")

if __name__ == "__main__":
root = tk.Tk()
app = CleaningScheduleApp(root)
root.mainloop()

正文完
 0
116博客
版权声明:本篇文章由 116博客 于2025-02-13发表,共计6475字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)
验证码