import tkinter as tk
from tkinter import messagebox, filedialog
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure

# 设置中文字体，解决中文显示问题
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False


class ScrollableScoreTrendApp:
    def __init__(self, root):
        self.root = root
        self.root.title("无限次数+横向滑动 成绩走向图工具")
        self.root.geometry("1000x700")

        # 核心数据存储（无限追加）
        self.exam_times = []  # 考试名称/时间列表
        self.scores = []      # 对应分数列表

        # ---------------------- 1. 单次添加成绩区域 ----------------------
        add_frame = tk.Frame(root)
        add_frame.grid(row=0, column=0, columnspan=2,
                       padx=10, pady=10, sticky="w")

        # 考试名称输入
        tk.Label(add_frame, text="考试名称/时间：", font=("宋体", 10)
                 ).grid(row=0, column=0, padx=5)
        self.single_time_entry = tk.Entry(add_frame, width=20, font=("宋体", 10))
        self.single_time_entry.grid(row=0, column=1, padx=5)
        self.single_time_entry.insert(0, "第1次月考")

        # 成绩输入
        tk.Label(add_frame, text="成绩（0-100）：", font=("宋体", 10)
                 ).grid(row=0, column=2, padx=5)
        self.single_score_entry = tk.Entry(
            add_frame, width=10, font=("宋体", 10))
        self.single_score_entry.grid(row=0, column=3, padx=5)
        self.single_score_entry.insert(0, "85")

        # 添加按钮
        tk.Button(
            add_frame, text="添加成绩", command=self.add_single_score,
            font=("宋体", 10), bg="#4CAF50", fg="white", padx=15, pady=2
        ).grid(row=0, column=4, padx=10)

        # ---------------------- 2. 操作按钮区域 ----------------------
        btn_frame = tk.Frame(root)
        btn_frame.grid(row=1, column=0, columnspan=2, padx=10, pady=5)

        tk.Button(
            btn_frame, text="生成/刷新成绩图", command=self.generate_chart,
            font=("宋体", 10), bg="#2196F3", fg="white", padx=20, pady=5
        ).pack(side="left", padx=10)

        tk.Button(
            btn_frame, text="清空所有成绩", command=self.clear_all_data,
            font=("宋体", 10), bg="#f44336", fg="white", padx=20, pady=5
        ).pack(side="left", padx=10)

        tk.Button(
            btn_frame, text="保存图表", command=self.save_chart,
            font=("宋体", 10), bg="#FF9800", fg="white", padx=20, pady=5
        ).pack(side="left", padx=10)

        # ---------------------- 3. 已添加数据展示区域 ----------------------
        data_frame = tk.Frame(root)
        data_frame.grid(row=2, column=0, columnspan=2,
                        padx=10, pady=5, sticky="w")

        tk.Label(data_frame, text="已记录的成绩（共0条）：", font=(
            "宋体", 10)).grid(row=0, column=0, sticky="w")
        self.data_label = tk.Label(
            data_frame, text="", font=("宋体", 9), fg="#666")
        self.data_label.grid(row=1, column=0, columnspan=2, sticky="w")

        # ---------------------- 4. 带横向滚动的图表区域 ----------------------
        # 创建滚动容器
        scroll_frame = tk.Frame(root)
        scroll_frame.grid(row=3, column=0, columnspan=2,
                          padx=10, pady=10, sticky="nsew")

        # 横向滚动条
        self.x_scrollbar = tk.Scrollbar(scroll_frame, orient=tk.HORIZONTAL)
        self.x_scrollbar.pack(side=tk.BOTTOM, fill=tk.X)

        # 画布（承载图表）
        self.chart_canvas = tk.Canvas(
            scroll_frame, xscrollcommand=self.x_scrollbar.set)
        self.chart_canvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True)

        # 绑定滚动条
        self.x_scrollbar.config(command=self.chart_canvas.xview)

        # 内部框架（放matplotlib图表）
        self.inner_frame = tk.Frame(self.chart_canvas)
        self.chart_canvas.create_window(
            (0, 0), window=self.inner_frame, anchor="nw")

        # 初始化matplotlib图表
        self.fig = Figure(figsize=(8, 4), dpi=100)
        self.ax = self.fig.add_subplot(111)
        self.canvas = FigureCanvasTkAgg(self.fig, master=self.inner_frame)
        self.canvas_widget = self.canvas.get_tk_widget()
        self.canvas_widget.pack(fill=tk.BOTH, expand=True)

        # 初始化空图表
        self.init_empty_chart()

        # 配置窗口自适应
        root.grid_rowconfigure(3, weight=1)
        root.grid_columnconfigure(0, weight=1)

    # 初始化空图表
    def init_empty_chart(self):
        self.ax.clear()
        self.ax.set_title("成绩走向图", fontsize=14)
        self.ax.set_xlabel("考试次数/时间", fontsize=12)
        self.ax.set_ylabel("分数", fontsize=12)
        self.ax.set_ylim(0, 100)
        self.ax.grid(True, alpha=0.3)
        self.canvas.draw()
        # 更新滚动区域
        self.inner_frame.update_idletasks()
        self.chart_canvas.config(scrollregion=self.chart_canvas.bbox(tk.ALL))

    # 添加单次成绩
    def add_single_score(self):
        # 获取输入
        exam_time = self.single_time_entry.get().strip()
        score_str = self.single_score_entry.get().strip()

        # 校验输入
        if not exam_time:
            messagebox.showwarning("输入错误", "考试名称/时间不能为空！")
            return
        if not score_str:
            messagebox.showwarning("输入错误", "成绩不能为空！")
            return

        # 校验成绩是否为数字且在0-100之间
        try:
            score = float(score_str)
            if score < 0 or score > 100:
                messagebox.showwarning("输入错误", "成绩必须在0-100之间！")
                return
        except ValueError:
            messagebox.showwarning("输入错误", "成绩必须是数字（如85、90.5）！")
            return

        # 添加到数据列表
        self.exam_times.append(exam_time)
        self.scores.append(score)

        # 清空当前输入框，方便继续添加
        self.single_time_entry.delete(0, tk.END)
        self.single_score_entry.delete(0, tk.END)

        # 更新已记录数据展示
        self.update_data_label()

        # 提示添加成功
        messagebox.showinfo("添加成功", f"已添加：{exam_time} - {score}分")

    # 更新已记录数据标签
    def update_data_label(self):
        count = len(self.exam_times)
        # 更新标题的数量
        self.data_label.master.winfo_children()[0].config(
            text=f"已记录的成绩（共{count}条）：")
        # 拼接所有数据展示
        data_text = ""
        for i, (time, score) in enumerate(zip(self.exam_times, self.scores), 1):
            data_text += f"{i}. {time}：{score}分  "
        self.data_label.config(text=data_text)

    # 生成/刷新成绩图
    def generate_chart(self):
        if not self.exam_times or not self.scores:
            messagebox.showwarning("数据为空", "请先添加成绩数据！")
            self.init_empty_chart()
            return

        # 清空图表
        self.ax.clear()

        # 绘制折线图（适配大量数据）
        self.ax.plot(self.exam_times, self.scores,
                     color="#2E86AB", linewidth=2,
                     marker="o", markersize=6,  # 缩小标记点避免重叠
                     markerfacecolor="#A23B72", label="成绩走势")

        # 标注分数（仅当数据量≤20时显示，避免拥挤）
        if len(self.exam_times) <= 20:
            for x, y in zip(self.exam_times, self.scores):
                self.ax.text(x, y + 0.5, f"{y}分", ha="center", fontsize=8)

        # 设置图表样式
        self.ax.set_title("成绩走向图", fontsize=14, pad=10)
        self.ax.set_xlabel("考试次数/时间", fontsize=12)
        self.ax.set_ylabel("分数", fontsize=12)
        self.ax.set_ylim(0, 100)
        self.ax.grid(True, alpha=0.3)
        self.ax.legend()

        # 旋转x轴标签，避免重叠
        self.ax.tick_params(axis='x', rotation=45)

        # 调整图表布局，防止标签被截断
        self.fig.tight_layout()

        # 刷新画布
        self.canvas.draw()

        # 更新滚动区域（关键：让滚动条适配图表宽度）
        self.inner_frame.update_idletasks()
        self.chart_canvas.config(scrollregion=self.chart_canvas.bbox(tk.ALL))

    # 清空所有成绩数据
    def clear_all_data(self):
        if messagebox.askyesno("确认清空", "是否确定清空所有成绩数据？"):
            self.exam_times.clear()
            self.scores.clear()
            self.update_data_label()
            self.init_empty_chart()

    # 保存图表
    def save_chart(self):
        if not self.exam_times or not self.scores:
            messagebox.showwarning("数据为空", "请先添加并生成成绩图！")
            return

        file_path = filedialog.asksaveasfilename(
            defaultextension=".png",
            filetypes=[("PNG图片", "*.png"), ("JPG图片", "*.jpg"),
                       ("PDF文件", "*.pdf")],
            title="保存成绩走向图"
        )
        if file_path:
            self.fig.savefig(file_path, dpi=300, bbox_inches="tight")
            messagebox.showinfo("保存成功", f"图表已保存到：{file_path}")


# 运行程序
if __name__ == "__main__":
    root = tk.Tk()
    app = ScrollableScoreTrendApp(root)
    root.mainloop()
