开发者

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

目录
  • 概述:为什么我们需要一个桌面便签工具
  • 核心功能实现详解
    • 1. 整体架构设计
    • 2. 智能提醒系统实现
    • 3. 任务状态管理逻辑
    • 4. 数据持久化方案
  • UI设计技巧与优化
    • 1. 现代化界面布局
    • 2. 交互细节优化
    • 3. 颜色方案
  • 功能扩展思路
    • 运行效果
      • 完整源码获取
        • 项目总结与收获

          概述:为什么我们需要一个桌面便签工具

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

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

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

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

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

          核心功能实现详解

          1. 整体架构设计

          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()      # 实时时钟
          

          2. 智能提醒系统实现

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

          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(...)
          

          3. 任务状态管理逻辑

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

          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("提示", "提醒时间晚于完成时间...")
          

          4. 数据持久化方案

          采用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))
          

          UI设计技巧与优化

          1. 现代化界面布局

          [界面结构图]

          主界面结构:

          - 顶部:标题+实时时钟

          - 中部:输入框+添加按钮

          - 底部:带滚动条的任务显示区域

          2. 交互细节优化

          # 平滑滚动实现
          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()
          

          3. 颜色方案

          元素颜色值用途
          背景色#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())
          

          运行效果

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

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

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

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

          完整源码获取

          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_alarmuZhHcrzcRKs_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=("SimSuhttp://www.devze.comn", 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_taswww.devze.comk, 
                                       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, rphpelief="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_uZhHcrzcRKtime 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桌面便签工具的资料请关注编程客栈(www.devze.com)其它相关文章!

          0

          上一篇:

          下一篇:

          精彩评论

          暂无评论...
          验证码 换一张
          取 消

          最新开发

          开发排行榜