Python+Tkinter实现高颜值桌面便签工具

Written by

in

文章目录
  • 在快节奏的现代生活中,高效的任务管理成为提升生产力的关键。市面上的待办事项应用虽然功能丰富,但往往存在以下痛点: 需要联网使用,隐私性差 界面复杂,启动缓慢 无法深度自定义功能 本文将带你用Python的Tkinter库开发一款轻量级桌面便签工具,具备以下亮点功能: 日期分组管理 智能提醒系统 任务完成状态追踪 简洁美观的UI设计 本地化数据存储 (开发环境:Python 3.8+,无需额外安装库)
  • 1.云同步功能 添加Dropbox/Google Drive API支持 实现多设备同步 2.分类标签系统 def add_tag(self, task_id, tag_name): # 为任务添加彩色标签 pass 3.数据统计分析 使用matplotlib生成任务完成率图表 导出周报/月报 4.快捷键支持 self.root.bind(‘<Control-n>’, lambda e: self.add_task()) self.root.bind(‘<Control-s>’, lambda e: self.save_tasks())
  • import tkinter as tk from tkinter import messagebox import csv import os from datetime import datetime class TodoApp: def __init__(self, root): self.root = root self.root.title(“桌面便签工具”) self.root.geometry(“390×600″) self.root.configure(bg=”#f0f0f0″) self.tasks_by_date = {} self.group_states = {} self.create_widgets() self.load_tasks() self.check_alarms_on_startup() self.update_time() # 启动时间更新 def create_widgets(self): # 标题 title_label = tk.Label(self.root, text=”待办事项”, font=(“SimSun”, 16, “bold”), bg=”#f0f0f0″, fg=”#333333″) title_label.pack(pady=10) # 输入框架 input_frame = tk.Frame(self.root, bg=”#f0f0f0″) input_frame.pack(pady=5) # 当前时间显示 self.time_label = tk.Label(input_frame, text=””, font=(“SimSun”, 12), bg=”#f0f0f0″, fg=”#333333″) self.time_label.pack(pady=(0, 5)) # 输入框 self.entry = tk.Entry(input_frame, width=40, font=(“SimSun”, 12), bd=2, relief=”flat”) self.entry.pack(pady=(0, 5)) # 修改为垂直布局 self.entry.focus_set() # 添加按钮 add_button = tk.Button(input_frame, text=”添加”, command=self.add_task, font=(“SimSun”, 10), bg=”#4CAF50″, fg=”white”, bd=0, padx=10, pady=5) add_button.pack() # 放在输入框下方 # 任务显示框架 self.task_frame = tk.Frame(self.root, bg=”#ffffff”, bd=1, relief=”solid”) self.task_frame.pack(pady=10, padx=10, fill=tk.BOTH, expand=True) self.canvas = tk.Canvas(self.task_frame, bg=”#ffffff”, highlightthickness=0) self.scrollbar = tk.Scrollbar(self.task_frame, orient=”vertical”, command=self.canvas.yview) self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) self.canvas.configure(yscrollcommand=self.scrollbar.set) self.inner_frame = tk.Frame(self.canvas, bg=”#ffffff”) self.canvas.create_window((0, 0), window=self.inner_frame, anchor=”nw”) self.inner_frame.bind(“<Configure>”, lambda e: self.canvas.configure(scrollregion=self.canvas.bbox(“all”))) self.canvas.bind_all(“<MouseWheel>”, self._on_mousewheel) def update_time(self): “””更新当前时间显示””” current_time = datetime.now().strftime(“%Y-%m-%d %H:%M:%S”) self.time_label.config(text=f”当前时间: {current_time}”) self.root.after(1000, self.update_time) # 每秒更新一次 def _on_mousewheel(self, event): self.canvas.yview_scroll(int(-1 * (event.delta / 120)), “units”) def toggle_group(self, date, button): is_expanded = self.group_states.get(date, True) self.group_states[date] = not is_expanded button.config(text=f”{‘▼’ if not is_expanded else ‘▶’} {date}”) self.refresh_display() self.save_tasks(silent=True) def update_completion_time(self, var, time_label, task_text, add_time, date): alarm_time_str = “无” alarm_message = “无” for task in self.tasks_by_date[date]: if task[0] == task_text and task[2] == add_time: alarm_time_str = task[4] alarm_message = task[5] break if var.get(): completion_time = datetime.now().strftime(“%Y-%m-%d %H:%M:%S”) if alarm_time_str != “无”: try: alarm_time = datetime.strptime(alarm_time_str, “%Y-%m-%d %H:%M:%S”) completion_datetime = datetime.strptime(completion_time, “%Y-%m-%d %H:%M:%S”) if alarm_time > completion_datetime: var.set(False) messagebox.showinfo(“提示”, f”任务 ‘{task_text}’ 的提醒时间晚于完成时间,已取消完成状态并保留提醒”) completion_time = “未完成” time_label.config(text=f”添加时间: {add_time}n完成时间: {completion_time}”, fg=”#757575″) for i, (t_text, _, a_time, _, at, am) in enumerate(self.tasks_by_date[date]): if t_text == task_text and a_time == add_time: self.tasks_by_date[date][i] = (task_text, False, add_time, completion_time, at, am) self.set_alarm_timer(alarm_time, alarm_message, task_text, add_time, date) break self.save_tasks(silent=True) self.refresh_display() return except ValueError: pass time_label.config(text=f”添加时间: {add_time}n完成时间: {completion_time}”, fg=”#2E7D32″) for i, (t_text, _, a_time, _, at, am) in enumerate(self.tasks_by_date[date]): if t_text == task_text and a_time == add_time: self.tasks_by_date[date][i] = (task_text, True, add_time, completion_time, at, am) break else: time_label.config(text=f”添加时间: {add_time}n完成时间: 未完成”, fg=”#757575″) for i, (t_text, _, a_time, _, at, am) in enumerate(self.tasks_by_date[date]): if t_text == task_text and a_time == add_time: self.tasks_by_date[date][i] = (task_text, False, add_time, “未完成”, at, am) if alarm_time_str != “无”: try: alarm_time = datetime.strptime(alarm_time_str, “%Y-%m-%d %H:%M:%S”) if alarm_time > datetime.now(): self.set_alarm_timer(alarm_time, alarm_message, task_text, add_time, date) except ValueError: pass break self.save_tasks(silent=True) self.refresh_display() def edit_task(self, task_text, add_time, date, label): edit_window = tk.Toplevel(self.root) edit_window.title(“编辑任务”) edit_window.geometry(“300×100″) edit_window.configure(bg=”#f0f0f0″) tk.Label(edit_window, text=”任务内容:”, bg=”#f0f0f0″).pack(pady=5) entry = tk.Entry(edit_window, width=40, font=(“SimSun”, 11)) entry.pack(pady=5) entry.insert(0, task_text) def save_edit(): new_text = entry.get().strip() if new_text: for i, (t_text, completed, a_time, c_time, at, am) in enumerate(self.tasks_by_date[date]): if t_text == task_text and a_time == add_time: self.tasks_by_date[date][i] = (new_text, completed, a_time, c_time, at, am) break self.refresh_display() self.save_tasks(silent=True) edit_window.destroy() else: messagebox.showwarning(“警告”, “任务内容不能为空!”) tk.Button(edit_window, text=”保存”, command=save_edit, bg=”#4CAF50″, fg=”white”, bd=0).pack(pady=5) # 删除功能2025.4.2已修改 def delete_task(self, task_text, add_time, date): if messagebox.askyesno(“确认”, “确定删除此任务吗?”): for i, (t_text, _, a_time, _, _, _) in enumerate(self.tasks_by_date[date]): if t_text == task_text and a_time == add_time: del self.tasks_by_date[date][i] break if not self.tasks_by_date[date]: del self.tasks_by_date[date] del self.group_states[date] self.refresh_display() self.save_tasks(silent=True) def add_task(self): task_text = self.entry.get().strip() if task_text: add_time = datetime.now().strftime(“%Y-%m-%d %H:%M:%S”) date = add_time.split()[0] if date not in self.tasks_by_date: self.tasks_by_date[date] = [] self.group_states[date] = True self.tasks_by_date[date].insert(0, (task_text, False, add_time, “未完成”, “无”, “无”)) self.refresh_display() self.entry.delete(0, tk.END) self.save_tasks(silent=True) else: messagebox.showwarning(“警告”, “请输入任务内容!”) def set_alarm(self, task_text, add_time, date): alarm_window = tk.Toplevel(self.root) alarm_window.title(“设置提醒”) alarm_window.geometry(“400×300″) alarm_window.configure(bg=”#f0f0f0″) tk.Label(alarm_window, text=”提醒日期 (格式: YYYYMMDD, 留空默认当天):”, bg=”#f0f0f0″).pack(pady=5) date_entry = tk.Entry(alarm_window, width=40, font=(“SimSun”, 11)) date_entry.pack(pady=5) tk.Label(alarm_window, text=”提醒时间 (格式: HHMMSS或HHMM, 无秒默认为00):”, bg=”#f0f0f0″).pack(pady=5) time_entry = tk.Entry(alarm_window, width=40, font=(“SimSun”, 11)) time_entry.pack(pady=5) tk.Label(alarm_window, text=”提醒信息:”, bg=”#f0f0f0″).pack(pady=5) message_entry = tk.Entry(alarm_window, width=40, font=(“SimSun”, 11)) message_entry.pack(pady=5) def save_alarm(): alarm_date_str = date_entry.get().strip() alarm_time_str = time_entry.get().strip() alarm_message = message_entry.get().strip() if not alarm_date_str: alarm_date_str = datetime.now().strftime(“%Y%m%d”) try: if len(alarm_time_str) == 4: alarm_time_str += “00” alarm_datetime_str = f”{alarm_date_str} {alarm_time_str}” alarm_time = datetime.strptime(alarm_datetime_str, “%Y%m%d %H%M%S”) if alarm_time < datetime.now(): messagebox.showwarning(“警告”, “提醒时间不能早于当前时间!”) return for i, (t_text, completed, a_time, c_time, _, _) in enumerate(self.tasks_by_date[date]): if t_text == task_text and a_time == add_time: if completed and c_time != “未完成”: completion_datetime = datetime.strptime(c_time, “%Y-%m-%d %H:%M:%S”) if alarm_time > completion_datetime: completed = False c_time = “未完成” messagebox.showinfo(“提示”, f”任务 ‘{task_text}’ 的提醒时间晚于完成时间,已取消完成状态”) self.tasks_by_date[date][i] = (task_text, completed, add_time, c_time, alarm_time.strftime(“%Y-%m-%d %H:%M:%S”), alarm_message) break if not completed: self.set_alarm_timer(alarm_time, alarm_message, task_text, add_time, date) self.save_tasks(silent=True) self.refresh_display() alarm_window.destroy() except ValueError: messagebox.showwarning(“警告”, “时间格式不正确,请使用 YYYYMMDD HHMMSS 或 YYYYMMDD HHMM 格式!”) tk.Button(alarm_window, text=”保存”, command=save_alarm, bg=”#4CAF50″, fg=”white”, bd=0).pack(pady=5) def set_alarm_timer(self, alarm_time, alarm_message, task_text, add_time, date): delay = (alarm_time – datetime.now()).total_seconds() * 1000 self.root.after(int(delay), lambda: self.show_alarm(alarm_message, task_text, add_time, date)) def show_alarm(self, alarm_message, task_text, add_time, date): for task in self.tasks_by_date[date]: if task[0] == task_text and task[2] == add_time and not task[1]: messagebox.showinfo(“提醒”, f”任务: {task_text}n信息: {alarm_message}”) break def check_alarms_on_startup(self): current_time = datetime.now() for date in self.tasks_by_date: for i, (task_text, completed, add_time, completion_time, alarm_time_str, alarm_message) in enumerate(self.tasks_by_date[date]): if alarm_time_str != “无”: try: alarm_time = datetime.strptime(alarm_time_str, “%Y-%m-%d %H:%M:%S”) if completed and completion_time != “未完成”: completion_datetime = datetime.strptime(completion_time, “%Y-%m-%d %H:%M:%S”) if alarm_time > completion_datetime: self.tasks_by_date[date][i] = (task_text, False, add_time, “未完成”, alarm_time_str, alarm_message) messagebox.showinfo(“提示”, f”任务 ‘{task_text}’ 的提醒时间晚于完成时间,已取消完成状态”) if alarm_time <= current_time: self.show_alarm(alarm_message, task_text, add_time, date) else: self.set_alarm_timer(alarm_time, alarm_message, task_text, add_time, date) elif not completed: if alarm_time <= current_time: self.show_alarm(alarm_message, task_text, add_time, date) else: self.set_alarm_timer(alarm_time, alarm_message, task_text, add_time, date) except ValueError: continue self.refresh_display() self.save_tasks(silent=True) def create_task_frame(self, task_text, completed, add_time, completion_time, date): task_frame = tk.Frame(self.inner_frame, bg=”#fafafa”, bd=1, relief=”solid”, padx=8, pady=8) var = tk.BooleanVar(value=completed) checkbox = tk.Checkbutton(task_frame, variable=var, bg=”#fafafa”) checkbox.pack(side=tk.LEFT, padx=(0, 5)) content_frame = tk.Frame(task_frame, bg=”#fafafa”) content_frame.pack(side=tk.LEFT, fill=tk.X, expand=True) label = tk.Label(content_frame, text=task_text, font=(“SimSun”, 11, “bold”), bg=”#fafafa”, fg=”#333333″, wraplength=300, justify=”left”) label.pack(anchor=”w”) alarm_time = “无” alarm_message = “无” for t in self.tasks_by_date[date]: if t[0] == task_text and t[2] == add_time and len(t) >= 6: alarm_time = t[4] alarm_message = t[5] break time_text = f”添加时间: {add_time}n完成时间: {completion_time if completed else ‘未完成’}” if alarm_time != “无” and not completed: time_text += f”n提醒时间: {alarm_time}n提醒信息: {alarm_message}” time_label = tk.Label(content_frame, text=time_text, font=(“SimSun”, 9), bg=”#fafafa”, fg=”#2E7D32″ if completed else “#757575″, height=4, justify=”left”) time_label.pack(anchor=”w”, pady=2) button_frame = tk.Frame(task_frame, bg=”#fafafa”) button_frame.pack(side=tk.RIGHT, padx=5) edit_button = tk.Button(button_frame, text=”编辑”, font=(“SimSun”, 11), bg=”#2196F3″, fg=”white”, bd=0, command=lambda: self.edit_task(task_text, add_time, date, label)) edit_button.pack(side=tk.TOP, pady=2) delete_button = tk.Button(button_frame, text=”删除”, font=(“SimSun”, 11), bg=”#F44336″, fg=”white”, bd=0, command=lambda: self.delete_task(task_text, add_time, date)) delete_button.pack(side=tk.TOP, pady=2) alarm_button = tk.Button(button_frame, text=”提醒”, font=(“SimSun”, 11), bg=”#FF9800″, fg=”white”, bd=0, command=lambda: self.set_alarm(task_text, add_time, date)) alarm_button.pack(side=tk.TOP, pady=2) var.trace(“w”, lambda *args: self.update_completion_time(var, time_label, task_text, add_time, date)) return task_frame def refresh_display(self): for widget in self.inner_frame.winfo_children(): widget.destroy() for date in sorted(self.tasks_by_date.keys(), reverse=True): group_frame = tk.Frame(self.inner_frame, bg=”#e0e0e0″) task_count = len(self.tasks_by_date[date]) toggle_button = tk.Button(group_frame, text=f”{‘▼’ if self.group_states.get(date, True) else ‘▶’} {date} ({task_count} 项)”, font=(“SimSun”, 10, “bold”), bg=”#e0e0e0″, fg=”#333333″, bd=0, command=lambda d=date, btn=group_frame: self.toggle_group(d, btn.winfo_children()[0])) toggle_button.pack(side=tk.LEFT, padx=5, pady=2) group_frame.pack(fill=tk.X, pady=5) if self.group_states.get(date, True): for i, (task_text, completed, add_time, completion_time, _, _) in enumerate(self.tasks_by_date[date]): task_frame = self.create_task_frame(task_text, completed, add_time, completion_time, date) task_frame.pack(fill=tk.X, pady=2 if i > 0 else (5, 2)) self.canvas.configure(scrollregion=self.canvas.bbox(“all”)) def load_tasks(self): if os.path.exists(‘tasks.csv’): with open(‘tasks.csv’, ‘r’, encoding=’utf-8′, newline=”) as file: reader = csv.reader(file) for row in reader: if row and len(row) >= 3: task_text = row[0] completed = row[1] == ‘True’ add_time = row[2] completion_time = row[3] if len(row) > 3 else “未完成” alarm_time = row[4] if len(row) > 4 else “无” alarm_message = row[5] if len(row) > 5 else “无” date = add_time.split()[0] if date not in self.tasks_by_date: self.tasks_by_date[date] = [] self.group_states[date] = True self.tasks_by_date[date].append((task_text, completed, add_time, completion_time, alarm_time, alarm_message)) def save_tasks(self, silent=False): with open(‘tasks.csv’, ‘w’, encoding=’utf-8′, newline=”) as file: writer = csv.writer(file) for date in sorted(self.tasks_by_date.keys(), reverse=True): for task in self.tasks_by_date[date]: writer.writerow(list(task) if len(task) == 6 else list(task) + [“无”, “无”]) if not silent: messagebox.showinfo(“成功”, “任务已保存!”) def run(self): self.root.mainloop() if __name__ == “__main__”: root = tk.Tk() app = TodoApp(root) app.run()
  • 通过本项目的开发,我们掌握了: Tkinter高级组件的综合运用 Python时间处理的最佳实践 状态管理与数据持久化方案 用户体验优化的具体方法 遇到的典型问题及解决方案: 问题:滚动区域内容更新时闪烁 解决:使用Canvas+Frame组合代替直接滚动Frame 问题:提醒时间与完成状态冲突 解决:添加时间逻辑校验 下一步学习建议: 尝试添加语音提醒功能 学习PyQt5开发更复杂的界面 研究如何打包为exe可执行文件 Q&A环节: Q:如何修改界面主题色? A:只需修改代码中的颜色常量,例如将"#f0f0f0"改为"#e6f7ff"可获得浅蓝色主题 以上就是Python+Tkinter实现高颜值桌面便签工具的详细内容,更多关于Python桌面便签工具的资料请关注风君子博客其它相关文章! 您可能感兴趣的文章: python制作一个桌面便签软件 利用Python如何生成便签图片详解 利用Python编写一个Windows桌面小组件 使用Python编写一个桌面便签应用 基于python实现开箱即用的桌面时钟 基于Python编写一个桌面时钟屏保
  • 目录
    • 概述:为什么我们需要一个桌面便签工具
    • 核心功能实现详解
      • 1. 整体架构设计
      • 2. 智能提醒系统实现
      • 3. 任务状态管理逻辑
      • 4. 数据持久化方案
    • UI设计技巧与优化
      • 1. 现代化界面布局
      • 2. 交互细节优化
      • 3. 颜色方案
    • 功能扩展思路
      • 运行效果
        • 完整源码获取
          • 项目总结与收获

            在快节奏的现代生活中,高效的任务管理成为提升生产力的关键。市面上的待办事项应用虽然功能丰富,但往往存在以下痛点:

            • 需要联网使用,隐私性差
            • 界面复杂,启动缓慢
            • 无法深度自定义功能

            本文将带你用Python的Tkinter库开发一款轻量级桌面便签工具,具备以下亮点功能:

            • 日期分组管理
            • 智能提醒系统
            • 任务完成状态追踪
            • 简洁美观的UI设计
            • 本地化数据存储

            (开发环境:Python 3.8+,无需额外安装库)

            class TodoApp:
                def __init__(self, root):
                    self.tasks_by_date = {}  # 按日期分组的任务字典
                    self.group_states = {}   # 分组展开状态
                    self.create_widgets()    # 初始化UI
                    self.load_tasks()       # 加载本地任务
                    self.check_alarms_on_startup()  # 启动时检查提醒
                    self.update_time()      # 实时时钟
            

            核心算法通过比较当前时间与预设提醒时间实现精准提醒:

            def set_alarm_timer(self, alarm_time, alarm_message, task_text, add_time, date):
                delay = (alarm_time - datetime.now()).total_seconds() * 1000
                self.root.after(int(delay), lambda: self.show_alarm(...))
            
            def check_alarms_on_startup(self):
                # 启动时检查所有未完成任务的提醒时间
                for date in self.tasks_by_date:
                    for task in self.tasks_by_date[date]:
                        if task[4] != "无" and not task[1]:
                            alarm_time = datetime.strptime(task[4], "%Y-%m-%d %H:%M:%S")
                            if alarm_time <= datetime.now():
                                self.show_alarm(...)
            

            当用户标记任务完成时,系统会进行时间校验:

            def update_completion_time(self, var, time_label, task_text, add_time, date):
                if var.get():  # 如果标记为完成
                    completion_time = datetime.now()
                    # 检查提醒时间是否晚于完成时间
                    if alarm_time > completion_time:
                        var.set(False)  # 取消完成状态
                        messagebox.showinfo("提示", "提醒时间晚于完成时间...")
            

            采用CSV格式存储任务数据,每行包含:

            任务内容,完成状态,添加时间,完成时间,提醒时间,提醒信息

            读写实现:

            def load_tasks(self):
                if os.path.exists('tasks.csv'):
                    with open('tasks.csv', 'r', encoding='utf-8') as file:
                        reader = csv.reader(file)
                        for row in reader:
                            # 解析每行数据...
            
            def save_tasks(self, silent=False):
                with open('tasks.csv', 'w', encoding='utf-8', newline='') as file:
                    writer = csv.writer(file)
                    for date in self.tasks_by_date:
                        for task in self.tasks_by_date[date]:
                            writer.writerow(list(task))
            

            [界面结构图]

            主界面结构:
            – 顶部:标题+实时时钟
            – 中部:输入框+添加按钮
            – 底部:带滚动条的任务显示区域

            # 平滑滚动实现
            def _on_mousewheel(self, event):
                self.canvas.yview_scroll(int(-1*(event.delta/120)), "units")
            
            # 分组折叠功能
            def toggle_group(self, date, button):
                self.group_states[date] = not self.group_states.get(date, True)
                self.refresh_display()
            

            元素 颜色值 用途
            背景色 #f0f0f0 主窗口背景
            任务卡片 #fafafa 任务项背景
            完成文本 #2E7D32 已完成任务文字
            未完成文本 #757575 未完成任务文字

            1.云同步功能

            添加Dropbox/Google Drive API支持

            实现多设备同步

            2.分类标签系统

               def add_tag(self, task_id, tag_name):
                   # 为任务添加彩色标签
                   pass
            

            3.数据统计分析

            • 使用matplotlib生成任务完成率图表
            • 导出周报/月报

            4.快捷键支持

              self.root.bind('<Control-n>', lambda e: self.add_task())
              self.root.bind('<Control-s>', lambda e: self.save_tasks())
            

            import tkinter as tk
            from tkinter import messagebox
            import csv
            import os
            from datetime import datetime
            
            class TodoApp:
                def __init__(self, root):
                    self.root = root
                    self.root.title("桌面便签工具")
                    self.root.geometry("390x600")
                    self.root.configure(bg="#f0f0f0")
            
                    self.tasks_by_date = {}
                    self.group_states = {}
            
                    self.create_widgets()
                    self.load_tasks()
                    self.check_alarms_on_startup()
                    self.update_time()  # 启动时间更新
            
                def create_widgets(self):
                    # 标题
                    title_label = tk.Label(self.root, text="待办事项", font=("SimSun", 16, "bold"), bg="#f0f0f0", fg="#333333")
                    title_label.pack(pady=10)
            
                    # 输入框架
                    input_frame = tk.Frame(self.root, bg="#f0f0f0")
                    input_frame.pack(pady=5)
            
                    # 当前时间显示
                    self.time_label = tk.Label(input_frame, text="", font=("SimSun", 12), bg="#f0f0f0", fg="#333333")
                    self.time_label.pack(pady=(0, 5))
            
                    # 输入框
                    self.entry = tk.Entry(input_frame, width=40, font=("SimSun", 12), bd=2, relief="flat")
                    self.entry.pack(pady=(0, 5))  # 修改为垂直布局
                    self.entry.focus_set()
            
                    # 添加按钮
                    add_button = tk.Button(input_frame, text="添加", command=self.add_task, 
                                         font=("SimSun", 10), bg="#4CAF50", fg="white", bd=0, padx=10, pady=5)
                    add_button.pack()  # 放在输入框下方
            
                    # 任务显示框架
                    self.task_frame = tk.Frame(self.root, bg="#ffffff", bd=1, relief="solid")
                    self.task_frame.pack(pady=10, padx=10, fill=tk.BOTH, expand=True)
            
                    self.canvas = tk.Canvas(self.task_frame, bg="#ffffff", highlightthickness=0)
                    self.scrollbar = tk.Scrollbar(self.task_frame, orient="vertical", command=self.canvas.yview)
                    self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
                    self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
                    self.canvas.configure(yscrollcommand=self.scrollbar.set)
            
                    self.inner_frame = tk.Frame(self.canvas, bg="#ffffff")
                    self.canvas.create_window((0, 0), window=self.inner_frame, anchor="nw")
            
                    self.inner_frame.bind("<Configure>", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all")))
                    self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)
            
                def update_time(self):
                    """更新当前时间显示"""
                    current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                    self.time_label.config(text=f"当前时间: {current_time}")
                    self.root.after(1000, self.update_time)  # 每秒更新一次
            
                def _on_mousewheel(self, event):
                    self.canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
            
                def toggle_group(self, date, button):
                    is_expanded = self.group_states.get(date, True)
                    self.group_states[date] = not is_expanded
                    button.config(text=f"{'▼' if not is_expanded else '▶'} {date}")
                    self.refresh_display()
                    self.save_tasks(silent=True)
            
                def update_completion_time(self, var, time_label, task_text, add_time, date):
                    alarm_time_str = "无"
                    alarm_message = "无"
                    for task in self.tasks_by_date[date]:
                        if task[0] == task_text and task[2] == add_time:
                            alarm_time_str = task[4]
                            alarm_message = task[5]
                            break
            
                    if var.get():
                        completion_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                        if alarm_time_str != "无":
                            try:
                                alarm_time = datetime.strptime(alarm_time_str, "%Y-%m-%d %H:%M:%S")
                                completion_datetime = datetime.strptime(completion_time, "%Y-%m-%d %H:%M:%S")
                                if alarm_time > completion_datetime:
                                    var.set(False)
                                    messagebox.showinfo("提示", f"任务 '{task_text}' 的提醒时间晚于完成时间,已取消完成状态并保留提醒")
                                    completion_time = "未完成"
                                    time_label.config(text=f"添加时间: {add_time}n完成时间: {completion_time}", fg="#757575")
                                    for i, (t_text, _, a_time, _, at, am) in enumerate(self.tasks_by_date[date]):
                                        if t_text == task_text and a_time == add_time:
                                            self.tasks_by_date[date][i] = (task_text, False, add_time, completion_time, at, am)
                                            self.set_alarm_timer(alarm_time, alarm_message, task_text, add_time, date)
                                            break
                                    self.save_tasks(silent=True)
                                    self.refresh_display()
                                    return
                            except ValueError:
                                pass
            
                        time_label.config(text=f"添加时间: {add_time}n完成时间: {completion_time}", fg="#2E7D32")
                        for i, (t_text, _, a_time, _, at, am) in enumerate(self.tasks_by_date[date]):
                            if t_text == task_text and a_time == add_time:
                                self.tasks_by_date[date][i] = (task_text, True, add_time, completion_time, at, am)
                                break
                    else:
                        time_label.config(text=f"添加时间: {add_time}n完成时间: 未完成", fg="#757575")
                        for i, (t_text, _, a_time, _, at, am) in enumerate(self.tasks_by_date[date]):
                            if t_text == task_text and a_time == add_time:
                                self.tasks_by_date[date][i] = (task_text, False, add_time, "未完成", at, am)
                                if alarm_time_str != "无":
                                    try:
                                        alarm_time = datetime.strptime(alarm_time_str, "%Y-%m-%d %H:%M:%S")
                                        if alarm_time > datetime.now():
                                            self.set_alarm_timer(alarm_time, alarm_message, task_text, add_time, date)
                                    except ValueError:
                                        pass
                                break
                    self.save_tasks(silent=True)
                    self.refresh_display()
            
                def edit_task(self, task_text, add_time, date, label):
                    edit_window = tk.Toplevel(self.root)
                    edit_window.title("编辑任务")
                    edit_window.geometry("300x100")
                    edit_window.configure(bg="#f0f0f0")
            
                    tk.Label(edit_window, text="任务内容:", bg="#f0f0f0").pack(pady=5)
                    entry = tk.Entry(edit_window, width=40, font=("SimSun", 11))
                    entry.pack(pady=5)
                    entry.insert(0, task_text)
            
                    def save_edit():
                        new_text = entry.get().strip()
                        if new_text:
                            for i, (t_text, completed, a_time, c_time, at, am) in enumerate(self.tasks_by_date[date]):
                                if t_text == task_text and a_time == add_time:
                                    self.tasks_by_date[date][i] = (new_text, completed, a_time, c_time, at, am)
                                    break
                            self.refresh_display()
                            self.save_tasks(silent=True)
                            edit_window.destroy()
                        else:
                            messagebox.showwarning("警告", "任务内容不能为空!")
            
                    tk.Button(edit_window, text="保存", command=save_edit, bg="#4CAF50", fg="white", bd=0).pack(pady=5)
            
                # 删除功能2025.4.2已修改
                def delete_task(self, task_text, add_time, date):
                    if messagebox.askyesno("确认", "确定删除此任务吗?"):
                        for i, (t_text, _, a_time, _, _, _) in enumerate(self.tasks_by_date[date]):
                            if t_text == task_text and a_time == add_time:
                                del self.tasks_by_date[date][i]
                                break
                        if not self.tasks_by_date[date]:
                            del self.tasks_by_date[date]
                            del self.group_states[date]
                        self.refresh_display()
                        self.save_tasks(silent=True)
            
                def add_task(self):
                    task_text = self.entry.get().strip()
                    if task_text:
                        add_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                        date = add_time.split()[0]
                        if date not in self.tasks_by_date:
                            self.tasks_by_date[date] = []
                            self.group_states[date] = True
                        self.tasks_by_date[date].insert(0, (task_text, False, add_time, "未完成", "无", "无"))
                        self.refresh_display()
                        self.entry.delete(0, tk.END)
                        self.save_tasks(silent=True)
                    else:
                        messagebox.showwarning("警告", "请输入任务内容!")
            
                def set_alarm(self, task_text, add_time, date):
                    alarm_window = tk.Toplevel(self.root)
                    alarm_window.title("设置提醒")
                    alarm_window.geometry("400x300")
                    alarm_window.configure(bg="#f0f0f0")
            
                    tk.Label(alarm_window, text="提醒日期 (格式: YYYYMMDD, 留空默认当天):", bg="#f0f0f0").pack(pady=5)
                    date_entry = tk.Entry(alarm_window, width=40, font=("SimSun", 11))
                    date_entry.pack(pady=5)
            
                    tk.Label(alarm_window, text="提醒时间 (格式: HHMMSS或HHMM, 无秒默认为00):", bg="#f0f0f0").pack(pady=5)
                    time_entry = tk.Entry(alarm_window, width=40, font=("SimSun", 11))
                    time_entry.pack(pady=5)
            
                    tk.Label(alarm_window, text="提醒信息:", bg="#f0f0f0").pack(pady=5)
                    message_entry = tk.Entry(alarm_window, width=40, font=("SimSun", 11))
                    message_entry.pack(pady=5)
            
                    def save_alarm():
                        alarm_date_str = date_entry.get().strip()
                        alarm_time_str = time_entry.get().strip()
                        alarm_message = message_entry.get().strip()
            
                        if not alarm_date_str:
                            alarm_date_str = datetime.now().strftime("%Y%m%d")
            
                        try:
                            if len(alarm_time_str) == 4:
                                alarm_time_str += "00"
                            alarm_datetime_str = f"{alarm_date_str} {alarm_time_str}"
                            alarm_time = datetime.strptime(alarm_datetime_str, "%Y%m%d %H%M%S")
                            if alarm_time < datetime.now():
                                messagebox.showwarning("警告", "提醒时间不能早于当前时间!")
                                return
            
                            for i, (t_text, completed, a_time, c_time, _, _) in enumerate(self.tasks_by_date[date]):
                                if t_text == task_text and a_time == add_time:
                                    if completed and c_time != "未完成":
                                        completion_datetime = datetime.strptime(c_time, "%Y-%m-%d %H:%M:%S")
                                        if alarm_time > completion_datetime:
                                            completed = False
                                            c_time = "未完成"
                                            messagebox.showinfo("提示", f"任务 '{task_text}' 的提醒时间晚于完成时间,已取消完成状态")
            
                                    self.tasks_by_date[date][i] = (task_text, completed, add_time, c_time, 
                                                                alarm_time.strftime("%Y-%m-%d %H:%M:%S"), alarm_message)
                                    break
            
                            if not completed:
                                self.set_alarm_timer(alarm_time, alarm_message, task_text, add_time, date)
            
                            self.save_tasks(silent=True)
                            self.refresh_display()
                            alarm_window.destroy()
                        except ValueError:
                            messagebox.showwarning("警告", "时间格式不正确,请使用 YYYYMMDD HHMMSS 或 YYYYMMDD HHMM 格式!")
            
                    tk.Button(alarm_window, text="保存", command=save_alarm, bg="#4CAF50", fg="white", bd=0).pack(pady=5)
            
                def set_alarm_timer(self, alarm_time, alarm_message, task_text, add_time, date):
                    delay = (alarm_time - datetime.now()).total_seconds() * 1000
                    self.root.after(int(delay), lambda: self.show_alarm(alarm_message, task_text, add_time, date))
            
                def show_alarm(self, alarm_message, task_text, add_time, date):
                    for task in self.tasks_by_date[date]:
                        if task[0] == task_text and task[2] == add_time and not task[1]:
                            messagebox.showinfo("提醒", f"任务: {task_text}n信息: {alarm_message}")
                            break
            
                def check_alarms_on_startup(self):
                    current_time = datetime.now()
                    for date in self.tasks_by_date:
                        for i, (task_text, completed, add_time, completion_time, alarm_time_str, alarm_message) in enumerate(self.tasks_by_date[date]):
                            if alarm_time_str != "无":
                                try:
                                    alarm_time = datetime.strptime(alarm_time_str, "%Y-%m-%d %H:%M:%S")
                                    if completed and completion_time != "未完成":
                                        completion_datetime = datetime.strptime(completion_time, "%Y-%m-%d %H:%M:%S")
                                        if alarm_time > completion_datetime:
                                            self.tasks_by_date[date][i] = (task_text, False, add_time, "未完成", alarm_time_str, alarm_message)
                                            messagebox.showinfo("提示", f"任务 '{task_text}' 的提醒时间晚于完成时间,已取消完成状态")
                                            if alarm_time <= current_time:
                                                self.show_alarm(alarm_message, task_text, add_time, date)
                                            else:
                                                self.set_alarm_timer(alarm_time, alarm_message, task_text, add_time, date)
                                    elif not completed:
                                        if alarm_time <= current_time:
                                            self.show_alarm(alarm_message, task_text, add_time, date)
                                        else:
                                            self.set_alarm_timer(alarm_time, alarm_message, task_text, add_time, date)
                                except ValueError:
                                    continue
                    self.refresh_display()
                    self.save_tasks(silent=True)
            
                def create_task_frame(self, task_text, completed, add_time, completion_time, date):
                    task_frame = tk.Frame(self.inner_frame, bg="#fafafa", bd=1, relief="solid", padx=8, pady=8)
            
                    var = tk.BooleanVar(value=completed)
                    checkbox = tk.Checkbutton(task_frame, variable=var, bg="#fafafa")
                    checkbox.pack(side=tk.LEFT, padx=(0, 5))
            
                    content_frame = tk.Frame(task_frame, bg="#fafafa")
                    content_frame.pack(side=tk.LEFT, fill=tk.X, expand=True)
            
                    label = tk.Label(content_frame, text=task_text, font=("SimSun", 11, "bold"), 
                                    bg="#fafafa", fg="#333333", wraplength=300, justify="left")
                    label.pack(anchor="w")
            
                    alarm_time = "无"
                    alarm_message = "无"
                    for t in self.tasks_by_date[date]:
                        if t[0] == task_text and t[2] == add_time and len(t) >= 6:
                            alarm_time = t[4]
                            alarm_message = t[5]
                            break
            
                    time_text = f"添加时间: {add_time}n完成时间: {completion_time if completed else '未完成'}"
                    if alarm_time != "无" and not completed:
                        time_text += f"n提醒时间: {alarm_time}n提醒信息: {alarm_message}"
            
                    time_label = tk.Label(content_frame, text=time_text, 
                                        font=("SimSun", 9), bg="#fafafa", 
                                        fg="#2E7D32" if completed else "#757575",
                                        height=4, justify="left")
                    time_label.pack(anchor="w", pady=2)
            
                    button_frame = tk.Frame(task_frame, bg="#fafafa")
                    button_frame.pack(side=tk.RIGHT, padx=5)
            
                    edit_button = tk.Button(button_frame, text="编辑", font=("SimSun", 11), bg="#2196F3", fg="white", bd=0,
                                          command=lambda: self.edit_task(task_text, add_time, date, label))
                    edit_button.pack(side=tk.TOP, pady=2)
            
                    delete_button = tk.Button(button_frame, text="删除", font=("SimSun", 11), bg="#F44336", fg="white", bd=0,
                                            command=lambda: self.delete_task(task_text, add_time, date))
                    delete_button.pack(side=tk.TOP, pady=2)
            
                    alarm_button = tk.Button(button_frame, text="提醒", font=("SimSun", 11), bg="#FF9800", fg="white", bd=0,
                                            command=lambda: self.set_alarm(task_text, add_time, date))
                    alarm_button.pack(side=tk.TOP, pady=2)
            
                    var.trace("w", lambda *args: self.update_completion_time(var, time_label, task_text, add_time, date))
                    return task_frame
            
                def refresh_display(self):
                    for widget in self.inner_frame.winfo_children():
                        widget.destroy()
            
                    for date in sorted(self.tasks_by_date.keys(), reverse=True):
                        group_frame = tk.Frame(self.inner_frame, bg="#e0e0e0")
                        task_count = len(self.tasks_by_date[date])
                        toggle_button = tk.Button(group_frame, text=f"{'▼' if self.group_states.get(date, True) else '▶'} {date} ({task_count} 项)", 
                                            font=("SimSun", 10, "bold"), bg="#e0e0e0", fg="#333333", bd=0, 
                                            command=lambda d=date, btn=group_frame: self.toggle_group(d, btn.winfo_children()[0]))
                        toggle_button.pack(side=tk.LEFT, padx=5, pady=2)
                        group_frame.pack(fill=tk.X, pady=5)
            
                        if self.group_states.get(date, True):
                            for i, (task_text, completed, add_time, completion_time, _, _) in enumerate(self.tasks_by_date[date]):
                                task_frame = self.create_task_frame(task_text, completed, add_time, completion_time, date)
                                task_frame.pack(fill=tk.X, pady=2 if i > 0 else (5, 2))
            
                    self.canvas.configure(scrollregion=self.canvas.bbox("all"))
            
                def load_tasks(self):
                    if os.path.exists('tasks.csv'):
                        with open('tasks.csv', 'r', encoding='utf-8', newline='') as file:
                            reader = csv.reader(file)
                            for row in reader:
                                if row and len(row) >= 3:
                                    task_text = row[0]
                                    completed = row[1] == 'True'
                                    add_time = row[2]
                                    completion_time = row[3] if len(row) > 3 else "未完成"
                                    alarm_time = row[4] if len(row) > 4 else "无"
                                    alarm_message = row[5] if len(row) > 5 else "无"
                                    date = add_time.split()[0]
                                    if date not in self.tasks_by_date:
                                        self.tasks_by_date[date] = []
                                        self.group_states[date] = True
                                    self.tasks_by_date[date].append((task_text, completed, add_time, completion_time, 
                                                                   alarm_time, alarm_message))
            
                def save_tasks(self, silent=False):
                    with open('tasks.csv', 'w', encoding='utf-8', newline='') as file:
                        writer = csv.writer(file)
                        for date in sorted(self.tasks_by_date.keys(), reverse=True):
                            for task in self.tasks_by_date[date]:
                                writer.writerow(list(task) if len(task) == 6 else list(task) + ["无", "无"])
                    if not silent:
                        messagebox.showinfo("成功", "任务已保存!")
            
                def run(self):
                    self.root.mainloop()
            
            if __name__ == "__main__":
                root = tk.Tk()
                app = TodoApp(root)
                app.run()
            

            通过本项目的开发,我们掌握了:

            • Tkinter高级组件的综合运用
            • Python时间处理的最佳实践
            • 状态管理与数据持久化方案
            • 用户体验优化的具体方法

            遇到的典型问题及解决方案:

            问题:滚动区域内容更新时闪烁

            解决:使用Canvas+Frame组合代替直接滚动Frame

            问题:提醒时间与完成状态冲突

            解决:添加时间逻辑校验

            下一步学习建议

            • 尝试添加语音提醒功能
            • 学习PyQt5开发更复杂的界面
            • 研究如何打包为exe可执行文件

            Q&A环节

            Q:如何修改界面主题色?

            A:只需修改代码中的颜色常量,例如将"#f0f0f0"改为"#e6f7ff"可获得浅蓝色主题

            以上就是Python+Tkinter实现高颜值桌面便签工具的详细内容,更多关于Python桌面便签工具的资料请关注风君子博客其它相关文章!

            您可能感兴趣的文章:

            • python制作一个桌面便签软件
            • 利用Python如何生成便签图片详解
            • 利用Python编写一个Windows桌面小组件
            • 使用Python编写一个桌面便签应用
            • 基于python实现开箱即用的桌面时钟
            • 基于Python编写一个桌面时钟屏保

            站内搜索