Python 登记积分脚本

Python 登记积分脚本

前置知识 openpyxl:

openpyxl 是一个强大的 Python 库,用于处理 Excel 文件,允许读取、编辑和创建 Excel 工作簿和工作表。无论是需要自动化处理大量数据,还是创建漂亮的报告,openpyxl 都是一个强大的工具。

摘自 优快云,有删改

安装方式:

python -m pip install openpyxl

前置知识 pathlib

pathlib 是一个对文件路径、操作进行支持的库。

具体介绍

前置知识 tkinter

重点:ttk

本文的代码中使用 ttk 的 ProgressBar

StringVar 可变的字符串

filedialog 是一个选择文件的类,用于弹出窗口,让用户选择;

simpledialog 是用来弹出窗口,让用户输入数据的;

messagebox 是用来弹出选择(确定、取消等)窗口。

代码实现

import os
import time
import json
from pathlib import Path
from openpyxl import load_workbook
from tkinter import *
from tkinter import messagebox, simpledialog, filedialog, ttk

# 默认配置文件路径
CONFIG_FILE = "settings.json"


# 加载配置
def load_config():
    try:
        with open(CONFIG_FILE, "r") as f:
            return json.load(f)
    except FileNotFoundError:
        return {
            "settings": {
                "sr": 2,
                "sc": 2,
                "mw": 4,
                "file_path": "",
                "wn": "积分2024.9",
            },
            "exclude": [],
        }


# 保存配置
def save_config(config):
    with open(CONFIG_FILE, "w") as f:
        json.dump(config, f, indent=4)


# 主界面
class MainApp:
    def __init__(self):
        self.config = load_config()
        self.root = Tk()
        self.root.geometry("400x200")
        self.root.title("积分登记系统")

        Label(self.root, text="积分登记系统", font=("Arial", 16)).pack(pady=20)

        Button(self.root, text="登记积分", command=self.open_join).pack(pady=5)
        Button(self.root, text="设置", command=self.open_settings).pack(pady=5)
        Button(self.root, text="计算月份总分", command=self.open_sum_month).pack(pady=5)
        Button(self.root, text="帮助", command=self.show_help).pack(pady=5)

        self.root.protocol("WM_DELETE_WINDOW", self.exit_program)
        self.root.mainloop()

    def open_join(self):
        if not Path(self.config["settings"]["file_path"]).is_file():
            messagebox.showerror("错误", "未找到表格文件,请先在设置中配置表格路径!")
            return
        week = simpledialog.askinteger("登记积分", "请输入周数:")
        if week is not None:
            JoinWindow(self.config, week)

    def open_settings(self):
        SettingsWindow(self.config)

    def open_sum_month(self):
        month = simpledialog.askinteger("计算月份总分", "请输入月份:")
        if month is not None:
            SumMonthWindow(self.config, month)

    def show_help(self):
        messagebox.showinfo("帮助", "输入格式为:号数 积分\n例如:1 2\n按回车键发送,按Delete键删除")

    def exit_program(self):
        if messagebox.askokcancel("退出", "确认退出程序吗?"):
            self.root.destroy()
            os._exit(0)


# 设置窗口
class SettingsWindow:
    def __init__(self, config):
        self.config = config
        self.root = Tk()
        self.root.geometry("400x300")
        self.root.title("设置")

        Label(self.root, text="起始行:").grid(row=0, column=0, padx=10, pady=5)
        self.sr = Entry(self.root)
        self.sr.grid(row=0, column=1, pady=5)
        self.sr.insert(0, str(config["settings"]["sr"]))

        Label(self.root, text="起始列:").grid(row=1, column=0, padx=10, pady=5)
        self.sc = Entry(self.root)
        self.sc.grid(row=1, column=1, pady=5)
        self.sc.insert(0, str(config["settings"]["sc"]))

        Label(self.root, text="月份求和周数:").grid(row=2, column=0, padx=10, pady=5)
        self.mw = Entry(self.root)
        self.mw.grid(row=2, column=1, pady=5)
        self.mw.insert(0, str(config["settings"]["mw"]))

        Label(self.root, text="表格文件:").grid(row=3, column=0, padx=10, pady=5)
        Button(self.root, text="选择文件", command=self.choose_file).grid(row=3, column=1)
        self.file_label = Label(self.root, text=os.path.basename(config["settings"]["file_path"]))
        self.file_label.grid(row=3, column=2)

        Label(self.root, text="工作簿名称:").grid(row=4, column=0, padx=10, pady=5)
        self.wn = Entry(self.root)
        self.wn.grid(row=4, column=1, pady=5)
        self.wn.insert(0, config["settings"]["wn"])

        Label(self.root, text="排除号数 (用空格分隔):").grid(row=5, column=0, padx=10, pady=5)
        self.exclude = Entry(self.root)
        self.exclude.grid(row=5, column=1, pady=5)
        self.exclude.insert(0, " ".join(map(str, config["exclude"])))

        Button(self.root, text="保存", command=self.save_settings).grid(row=6, column=1, pady=20)

    def choose_file(self):
        file_path = filedialog.askopenfilename(filetypes=[("Excel files", "*.xlsx;*.xlsm")])
        if file_path:
            self.config["settings"]["file_path"] = file_path
            self.file_label.config(text=os.path.basename(file_path))

    def save_settings(self):
        try:
            self.config["settings"]["sr"] = int(self.sr.get())
            self.config["settings"]["sc"] = int(self.sc.get())
            self.config["settings"]["mw"] = int(self.mw.get())
            self.config["settings"]["wn"] = self.wn.get()
            self.config["exclude"] = list(map(int, self.exclude.get().split()))

            save_config(self.config)
            messagebox.showinfo("成功", "设置已保存!")
            self.root.destroy()
        except ValueError:
            messagebox.showerror("错误", "输入格式错误,请检查!")


# 计算月份总分窗口
class SumMonthWindow:
    def __init__(self, config, month):
        self.config = config
        self.month = month
        self.root = Tk()
        self.root.geometry("400x150")
        self.root.title("计算月份总分")

        Label(self.root, text="正在计算月份总分...", font=("Arial", 14)).pack(pady=10)
        self.progress = ttk.Progressbar(self.root, mode="determinate")
        self.progress.pack(pady=10)
        Button(self.root, text="开始", command=self.start_calculation).pack(pady=5)

    def start_calculation(self):
        try:
            wb = load_workbook(self.config["settings"]["file_path"])
            ws = wb[self.config["settings"]["wn"]]
            sr, sc, mw = self.config["settings"]["sr"], self.config["settings"]["sc"], self.config["settings"]["mw"]

            week_col = sc + self.month * mw
            ws.insert_cols(week_col + 1)

            for i in range(54):
                self.progress["value"] = (i + 1) / 54 * 100
                ws.cell(row=sr + i, column=week_col + 1).value = f"=SUM({ws.cell(row=sr + i, column=week_col - mw + 1).coordinate}:{ws.cell(row=sr + i, column=week_col).coordinate})"
                self.root.update_idletasks()
                time.sleep(0.05)

            wb.save(self.config["settings"]["file_path"])
            wb.close()
            messagebox.showinfo("成功", "月份总分计算完成!")
        except Exception as e:
            messagebox.showerror("错误", f"计算失败:{e}")
        finally:
            self.root.destroy()


# 登记积分窗口
class JoinWindow:
    def __init__(self, config, week):
        self.config = config
        self.week = week
        self.file_path = config["settings"]["file_path"]
        self.sheet_name = config["settings"]["wn"]
        self.exclude = config["exclude"]
        self.start_row = config["settings"]["sr"]
        self.start_col = config["settings"]["sc"] + week - 1
        self.same_score_count = 0

        # 加载 Excel 工作簿和工作表
        self.wb = load_workbook(self.file_path)
        self.ws = self.wb[self.sheet_name]

        self.root = Tk()
        self.root.geometry("400x400")
        self.root.title("登记积分")

        Label(self.root, text=f"当前周:{week}", font=("Arial", 14)).pack(pady=10)
        self.entry = Entry(self.root)
        self.entry.pack(pady=10)
        self.listbox = Listbox(self.root)
        self.listbox.pack(pady=10)

        Button(self.root, text="添加", command=self.add_score).pack(side=LEFT, padx=10)
        Button(self.root, text="删除", command=self.delete_score).pack(side=RIGHT, padx=10)

        self.root.protocol("WM_DELETE_WINDOW", self.save_and_exit)
        self.root.mainloop()

    def add_score(self):
        try:
            input_text = self.entry.get().strip()
            if not input_text:
                raise ValueError("输入不能为空!")

            # 检查输入格式
            parts = input_text.split()
            if len(parts) != 2 or not parts[0].isdigit() or not parts[1].isdigit():
                raise ValueError("输入格式错误,请输入:号数 积分\n例如:1 2")

            number, score = map(int, parts)
            if number < 1 or number > 55:
                raise ValueError("号数必须在 1 到 55 之间!")
            if number in self.exclude:
                if not messagebox.askyesno("提示", f"号数 {number} 在排除列表中,是否继续登记?"):
                    return

            # 检查是否覆盖已有值
            cell = self.ws.cell(row=self.start_row + number - 1, column=self.start_col)
            if cell.value is not None:
                if not messagebox.askyesno("提示", f"号数 {number} 已有积分,是否覆盖?"):
                    return

            # 添加积分
            cell.value = score
            self.listbox.insert(END, f"{number} {score}")
            self.entry.delete(0, END)

            # 检查当前周与前几周的积分是否有超过7个相同
            for week_offset in range(self.config["settings"]["sc"], self.start_col):
                if self.ws.cell(row=self.start_row + number - 1, column=week_offset).value == score:
                    same_score_count += 1

            if same_score_count > 7:
                messagebox.showinfo("提示", f"当前周与前几周有超过7个积分为 {score},请检查!")

        except ValueError as e:
            messagebox.showerror("输入错误", str(e))
        except Exception as e:
            messagebox.showerror("错误", f"无法添加积分:{e}")

    def delete_score(self):
        try:
            selected = self.listbox.curselection()
            if not selected:
                raise ValueError("请先选择要删除的记录!")

            # 从列表框中删除选中记录
            record = self.listbox.get(selected[0])
            number, _ = map(int, record.split())
            self.ws.cell(row=self.start_row + number - 1, column=self.start_col).value = None
            self.listbox.delete(selected[0])
        except ValueError as e:
            messagebox.showerror("错误", str(e))
        except Exception as e:
            messagebox.showerror("错误", f"无法删除积分:{e}")

    def save_and_exit(self):
        if messagebox.askyesno("提示", "是否保存更改?"):
            try:
                self.wb.save(self.file_path)
            except Exception as e:
                messagebox.showerror("错误", f"保存文件失败:{e}")
        self.wb.close()
        self.root.destroy()


if __name__ == "__main__":
    MainApp()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值