文章目录
一、界面展示
二、脚本功能
1. 核心功能
单次提醒功能:在指定日期和时间弹出提醒
重复提醒功能:支持每天/每周/每月定时提醒
删除提醒功能:可以删除已设置的提醒
提醒列表显示:实时显示所有设置的提醒
2. 界面功能
使用选项卡分类显示不同类型的提醒设置
提供图形化界面输入
实时显示提醒列表
错误提示和成功确认提示
3. 安全性功能
输入验证:检查必填字段
时间验证:防止设置过去的时间
格式验证:确保日期和时间格式正确
三、使用方法
1. 单次提醒
2. 重复提醒设置
3. 删除提醒
四、代码实现
1. 类结构设计
class ReminderApp:
def __init__(self):
# 初始化主窗口
self.root = tk.Tk()
# 初始化提醒字典
self.reminders = {}
# 创建界面组件
self.create_widgets()
# 启动调度线程
self.schedule_thread = threading.Thread(target=self.run_schedule, daemon=True)
2. 关键方法实现
a. 创建界面组件
def create_widgets(self):
# 创建选项卡
self.notebook = ttk.Notebook(self.root)
# 创建单次提醒和重复提醒标签页
self.single_frame = ttk.Frame(self.notebook)
self.repeat_frame = ttk.Frame(self.notebook)
# 创建提醒列表
self.reminder_list = tk.Listbox(self.root)
b. 设置单次提醒
def set_single_reminder(self):
# 获取用户输入
date_str = self.date_entry.get()
time_str = self.single_time_entry.get()
message = self.single_message_entry.get()
# 验证输入
if not all([date_str, time_str, message]):
messagebox.showerror("错误", "请填写所有字段")
return
try:
# 创建定时器
reminder_datetime = datetime.strptime(f"{date_str} {time_str}",
"%Y-%m-%d %H:%M")
delay = (reminder_datetime - datetime.now()).total_seconds()
timer = threading.Timer(delay, self.show_reminder,
args=[message, reminder_id])
# 保存提醒信息
self.reminders[reminder_id] = {
'datetime': reminder_datetime,
'message': message,
'timer': timer,
'repeat': False
}
c. 设置重复提醒
def set_repeat_reminder(self):
# 获取用户输入
time_str = self.repeat_time_entry.get()
message = self.repeat_message_entry.get()
repeat_type = self.repeat_type.get()
try:
# 根据重复类型设置调度任务
if repeat_type == "daily":
job = schedule.every().day.at(time_str).do(
self.show_reminder, message, reminder_id)
# 保存提醒信息
self.reminders[reminder_id] = {
'time': time_str,
'message': message,
'job': job,
'repeat': True,
'repeat_type': repeat_type
}
d. 删除提醒
def delete_reminder(self):
# 获取选中的提醒
selection = self.reminder_list.curselection()
if not selection:
messagebox.showwarning("警告", "请先选择要删除的提醒")
return
# 删除提醒
reminder_id = list(self.reminders.keys())[selection[0]]
self.remove_reminder(reminder_id)
self.update_reminder_list()
3. 关键技术点
a. 多线程处理
# 使用守护线程运行调度器
self.schedule_thread = threading.Thread(target=self.run_schedule, daemon=True)
self.schedule_thread.start()
b. 定时器实现
# 单次提醒使用 threading.Timer
timer = threading.Timer(delay, self.show_reminder, args=[message, reminder_id])
# 重复提醒使用 schedule 库
schedule.every().day.at(time_str).do(self.show_reminder, message, reminder_id)
c. 数据存储
# 使用字典存储所有提醒
self.reminders = {
'reminder_id': {
'datetime': datetime_obj, # 对于单次提醒
'time': time_str, # 对于重复提醒
'message': message,
'timer/job': timer_or_job_obj,
'repeat': boolean,
'repeat_type': type_str # 对于重复提醒
}
}
4. 错误处理
try:
# 时间格式转换
reminder_datetime = datetime.strptime(f"{date_str} {time_str}",
"%Y-%m-%d %H:%M")
# 验证时间是否有效
if reminder_datetime < datetime.now():
messagebox.showerror("错误", "不能设置过去的时间")
return
except ValueError:
messagebox.showerror("错误", "日期或时间格式错误")
五、完整代码
import tkinter as tk
from tkinter import messagebox, ttk
import time
import schedule
import threading
from datetime import datetime, timedelta
import platform
import os
# 根据操作系统选择声音模块
if platform.system() == 'Windows':
import winsound
def play_alert():
winsound.Beep(1000, 1000) # 频率1000Hz,持续1000毫秒
else:
def play_alert():
os.system('play -nq -t alsa synth 1 sine 440') # Linux系统下的提示音
class ReminderApp:
def __init__(self):
self.root = tk.Tk()
self.root.title("定时提醒")
self.root.geometry("400x600")
# 存储所有提醒的字典
self.reminders = {}
# 声音开关变量
self.sound_enabled = tk.BooleanVar(value=True)
# 创建输入框和按钮
self.create_widgets()
# 创建一个线程来运行调度器
self.schedule_thread = threading.Thread(target=self.run_schedule, daemon=True)
self.schedule_thread.start()
def create_widgets(self):
# 创建notebook用于切换不同类型的提醒设置
self.notebook = ttk.Notebook(self.root)
self.notebook.pack(pady=5, expand=True, fill="both")
# 单次提醒标签页
self.single_frame = ttk.Frame(self.notebook)
self.notebook.add(self.single_frame, text="单次提醒")
# 重复提醒标签页
self.repeat_frame = ttk.Frame(self.notebook)
self.notebook.add(self.repeat_frame, text="重复提醒")
# 设置单次提醒的控件
self.setup_single_reminder_widgets()
# 设置重复提醒的控件
self.setup_repeat_reminder_widgets()
# 声音控制区域
sound_frame = ttk.LabelFrame(self.root, text="声音设置")
sound_frame.pack(pady=5, padx=5, fill="x")
# 声音开关复选框
self.sound_checkbox = tk.Checkbutton(sound_frame, text="启用提醒声音",
variable=self.sound_enabled)
self.sound_checkbox.pack(side=tk.LEFT, padx=5)
# 测试声音按钮
self.test_sound_button = tk.Button(sound_frame, text="测试提醒声音",
command=self.test_sound)
self.test_sound_button.pack(side=tk.RIGHT, padx=5)
# 提醒列表框架
list_frame = ttk.LabelFrame(self.root, text="提醒列表")
list_frame.pack(pady=5, padx=5, fill="both", expand=True)
# 提醒列表
self.reminder_list = tk.Listbox(list_frame, width=50, height=10)
self.reminder_list.pack(pady=5, padx=5, fill="both", expand=True)
# 删除提醒按钮
self.delete_button = tk.Button(list_frame, text="删除选中的提醒",
command=self.delete_reminder)
self.delete_button.pack(pady=5)
def setup_single_reminder_widgets(self):
# 日期输入
date_frame = ttk.Frame(self.single_frame)
date_frame.pack(pady=5, fill="x")
ttk.Label(date_frame, text="日期 (YYYY-MM-DD):").pack(side=tk.LEFT, padx=5)
self.date_entry = ttk.Entry(date_frame)
self.date_entry.pack(side=tk.LEFT, expand=True, fill="x", padx=5)
self.date_entry.insert(0, datetime.now().strftime("%Y-%m-%d"))
# 时间输入
time_frame = ttk.Frame(self.single_frame)
time_frame.pack(pady=5, fill="x")
ttk.Label(time_frame, text="时间 (HH:MM):").pack(side=tk.LEFT, padx=5)
self.single_time_entry = ttk.Entry(time_frame)
self.single_time_entry.pack(side=tk.LEFT, expand=True, fill="x", padx=5)
# 提醒内容输入
message_frame = ttk.Frame(self.single_frame)
message_frame.pack(pady=5, fill="x")
ttk.Label(message_frame, text="提醒内容:").pack(side=tk.LEFT, padx=5)
self.single_message_entry = ttk.Entry(message_frame)
self.single_message_entry.pack(side=tk.LEFT, expand=True, fill="x", padx=5)
# 设置提醒按钮
self.single_set_button = ttk.Button(self.single_frame, text="设置单次提醒",
command=self.set_single_reminder)
self.single_set_button.pack(pady=10)
def setup_repeat_reminder_widgets(self):
# 时间输入
time_frame = ttk.Frame(self.repeat_frame)
time_frame.pack(pady=5, fill="x")
ttk.Label(time_frame, text="时间 (HH:MM):").pack(side=tk.LEFT, padx=5)
self.repeat_time_entry = ttk.Entry(time_frame)
self.repeat_time_entry.pack(side=tk.LEFT, expand=True, fill="x", padx=5)
# 提醒内容输入
message_frame = ttk.Frame(self.repeat_frame)
message_frame.pack(pady=5, fill="x")
ttk.Label(message_frame, text="提醒内容:").pack(side=tk.LEFT, padx=5)
self.repeat_message_entry = ttk.Entry(message_frame)
self.repeat_message_entry.pack(side=tk.LEFT, expand=True, fill="x", padx=5)
# 重复类型选择
repeat_type_frame = ttk.LabelFrame(self.repeat_frame, text="重复类型")
repeat_type_frame.pack(pady=5, padx=5, fill="x")
self.repeat_type = tk.StringVar(value="daily")
repeat_options = [
("每天", "daily"),
("每周", "weekly"),
("每月", "monthly")
]
for text, value in repeat_options:
ttk.Radiobutton(repeat_type_frame, text=text, variable=self.repeat_type,
value=value).pack(side=tk.LEFT, padx=10)
# 设置提醒按钮
self.repeat_set_button = ttk.Button(self.repeat_frame, text="设置重复提醒",
command=self.set_repeat_reminder)
self.repeat_set_button.pack(pady=10)
def show_reminder(self, message, reminder_id):
# 如果启用了声音,播放提示音
if self.sound_enabled.get():
sound_thread = threading.Thread(target=play_alert)
sound_thread.start()
# 显示消息框
messagebox.showinfo("提醒", message)
# 如果是单次提醒,显示后删除
if reminder_id in self.reminders and not self.reminders[reminder_id].get('repeat'):
self.remove_reminder(reminder_id)
self.update_reminder_list()
def test_sound(self):
"""测试提醒声音"""
if self.sound_enabled.get():
play_alert()
def set_single_reminder(self):
date_str = self.date_entry.get()
time_str = self.single_time_entry.get()
message = self.single_message_entry.get()
if not all([date_str, time_str, message]):
messagebox.showerror("错误", "请填写所有字段")
return
try:
reminder_datetime = datetime.strptime(f"{date_str} {time_str}", "%Y-%m-%d %H:%M")
if reminder_datetime < datetime.now():
messagebox.showerror("错误", "不能设置过去的时间")
return
reminder_id = f"single_{len(self.reminders)}"
delay = (reminder_datetime - datetime.now()).total_seconds()
timer = threading.Timer(delay, self.show_reminder, args=[message, reminder_id])
timer.start()
self.reminders[reminder_id] = {
'datetime': reminder_datetime,
'message': message,
'timer': timer,
'repeat': False
}
self.update_reminder_list()
messagebox.showinfo("成功", f"提醒已设置在 {date_str} {time_str}")
except ValueError:
messagebox.showerror("错误", "日期或时间格式错误")
def set_repeat_reminder(self):
time_str = self.repeat_time_entry.get()
message = self.repeat_message_entry.get()
repeat_type = self.repeat_type.get()
if not all([time_str, message]):
messagebox.showerror("错误", "请填写所有字段")
return
try:
reminder_id = f"repeat_{len(self.reminders)}"
if repeat_type == "daily":
job = schedule.every().day.at(time_str).do(
self.show_reminder, message, reminder_id)
elif repeat_type == "weekly":
job = schedule.every().week.at(time_str).do(
self.show_reminder, message, reminder_id)
elif repeat_type == "monthly":
job = schedule.every().month.at(time_str).do(
self.show_reminder, message, reminder_id)
self.reminders[reminder_id] = {
'time': time_str,
'message': message,
'job': job,
'repeat': True,
'repeat_type': repeat_type
}
self.update_reminder_list()
messagebox.showinfo("成功", f"重复提醒已设置")
except schedule.ScheduleValueError:
messagebox.showerror("错误", "时间格式错误,请使用HH:MM格式")
def delete_reminder(self):
selection = self.reminder_list.curselection()
if not selection:
messagebox.showwarning("警告", "请先选择要删除的提醒")
return
reminder_id = list(self.reminders.keys())[selection[0]]
self.remove_reminder(reminder_id)
self.update_reminder_list()
messagebox.showinfo("成功", "提醒已删除")
def remove_reminder(self, reminder_id):
if reminder_id in self.reminders:
reminder = self.reminders[reminder_id]
if reminder.get('repeat'):
schedule.cancel_job(reminder['job'])
else:
reminder['timer'].cancel()
del self.reminders[reminder_id]
def update_reminder_list(self):
self.reminder_list.delete(0, tk.END)
for reminder_id, reminder in self.reminders.items():
if reminder.get('repeat'):
text = f"重复提醒: {reminder['time']} - {reminder['message']} ({reminder['repeat_type']})"
else:
text = f"单次提醒: {reminder['datetime'].strftime('%Y-%m-%d %H:%M')} - {reminder['message']}"
self.reminder_list.insert(tk.END, text)
def run_schedule(self):
while True:
schedule.run_pending()
time.sleep(1)
def run(self):
self.root.mainloop()
if __name__ == "__main__":
app = ReminderApp()
app.run()
六、总结
脚本通过组合使用多线程、定时器、图形界面和事件调度等技术,实现一个功能完整的定时提醒系统。
七、注意事项
这个脚本仍然需要保持运行状态才能实现提醒功能。如果你需要在计算机启动时自动运行,可以将其设置为开机启动项。