代码实战项目3:待办事项清单应用¶
项目概述¶
待办事项清单(Todo List)是一个经典的项目管理工具,帮助用户记录、跟踪和管理日常任务。本应用将实现任务的增删改查、优先级设置、截止日期、完成状态管理等功能。
学习目标¶
- 掌握面向对象编程的核心概念
- 学会使用 datetime 模块处理日期时间
- 理解优先级队列和排序算法的应用
- 掌握用户界面的设计和实现
功能需求¶
核心功能¶
- 任务管理
- 添加新任务
- 编辑任务信息
- 删除任务
-
标记任务完成/未完成
-
任务属性
- 任务标题
- 任务描述
- 优先级(高、中、低)
- 截止日期
- 创建时间
-
完成状态
-
查询和筛选
- 查看所有任务
- 按优先级筛选
- 按完成状态筛选
-
按截止日期筛选
-
统计功能
- 统计已完成/未完成任务数
- 统计过期任务数
-
统计各优先级任务数
-
数据持久化
- 保存任务到文件
- 从文件加载任务
系统设计¶
数据结构设计¶
from datetime import datetime
from enum import Enum
class Priority(Enum):
"""优先级枚举"""
HIGH = 3
MEDIUM = 2
LOW = 1
class Task:
"""任务类"""
def __init__(self, title, description="", priority=Priority.MEDIUM, due_date=None):
self.id = None # 任务ID
self.title = title # 任务标题
self.description = description # 任务描述
self.priority = priority # 优先级
self.due_date = due_date # 截止日期
self.created_at = datetime.now() # 创建时间
self.completed = False # 完成状态
self.completed_at = None # 完成时间
class TodoManager:
"""待办事项管理类"""
def __init__(self):
self.tasks = {} # 任务字典 {id: Task}
self.next_id = 1 # 下一个任务ID
文件结构¶
todo_list/
├── main.py # 主程序
├── task.py # 任务类
├── manager.py # 管理类
├── data.json # 数据文件
└── README.md # 说明文档
代码实现¶
1. 任务类(task.py)¶
from datetime import datetime
from enum import Enum
class Priority(Enum):
"""优先级枚举"""
HIGH = 3
MEDIUM = 2
LOW = 1
def __str__(self):
"""优先级字符串表示"""
return self.name
@classmethod
def from_string(cls, priority_str):
"""从字符串创建优先级"""
priority_map = {
'high': cls.HIGH,
'medium': cls.MEDIUM,
'low': cls.LOW,
'高': cls.HIGH,
'中': cls.MEDIUM,
'低': cls.LOW
}
return priority_map.get(priority_str.lower(), cls.MEDIUM)
class Task:
"""任务类"""
def __init__(self, title, description="", priority=Priority.MEDIUM, due_date=None):
"""
初始化任务
参数:
title: 任务标题
description: 任务描述
priority: 优先级
due_date: 截止日期(datetime对象或字符串)
"""
self.id = None
self.title = title
self.description = description
self.priority = priority
self.created_at = datetime.now()
self.completed = False
self.completed_at = None
# 处理截止日期
if due_date:
if isinstance(due_date, str):
self.due_date = datetime.strptime(due_date, "%Y-%m-%d")
else:
self.due_date = due_date
else:
self.due_date = None
def complete(self):
"""标记任务为完成"""
self.completed = True
self.completed_at = datetime.now()
def uncomplete(self):
"""标记任务为未完成"""
self.completed = False
self.completed_at = None
def is_overdue(self):
"""检查任务是否过期"""
if self.due_date and not self.completed:
return datetime.now() > self.due_date
return False
def days_until_due(self):
"""计算距离截止日期的天数"""
if not self.due_date:
return None
delta = self.due_date - datetime.now()
return delta.days
def __str__(self):
"""字符串表示"""
status = "✓" if self.completed else "○"
priority_icon = {Priority.HIGH: "🔴", Priority.MEDIUM: "🟡", Priority.LOW: "🟢"}
due_str = ""
if self.due_date:
if self.is_overdue():
due_str = f" ⚠️ 过期 {abs(self.days_until_due())} 天"
else:
due_str = f" 📅 {self.due_date.strftime('%Y-%m-%d')} ({self.days_until_due()} 天)"
return f"{status} [{priority_icon[self.priority]}] {self.title}{due_str}"
def to_dict(self):
"""转换为字典"""
return {
'id': self.id,
'title': self.title,
'description': self.description,
'priority': self.priority.value,
'due_date': self.due_date.isoformat() if self.due_date else None,
'created_at': self.created_at.isoformat(),
'completed': self.completed,
'completed_at': self.completed_at.isoformat() if self.completed_at else None
}
@classmethod
def from_dict(cls, data):
"""从字典创建对象"""
task = cls(
title=data['title'],
description=data.get('description', ''),
priority=Priority(data['priority']),
due_date=data['due_date']
)
task.id = data['id']
task.created_at = datetime.fromisoformat(data['created_at'])
task.completed = data['completed']
if data['completed_at']:
task.completed_at = datetime.fromisoformat(data['completed_at'])
return task
2. 管理类(manager.py)¶
import json
from datetime import datetime
from task import Task, Priority
class TodoManager:
"""待办事项管理类"""
def __init__(self):
"""初始化管理器"""
self.tasks = {}
self.next_id = 1
def add_task(self, title, description="", priority=Priority.MEDIUM, due_date=None):
"""
添加任务
参数:
title: 任务标题
description: 任务描述
priority: 优先级
due_date: 截止日期
返回:
Task: 创建的任务对象
"""
task = Task(title, description, priority, due_date)
task.id = self.next_id
self.tasks[task.id] = task
self.next_id += 1
return task
def get_task(self, task_id):
"""
获取任务
参数:
task_id: 任务ID
返回:
Task: 任务对象,不存在返回None
"""
return self.tasks.get(task_id)
def update_task(self, task_id, title=None, description=None, priority=None, due_date=None):
"""
更新任务
参数:
task_id: 任务ID
title: 新标题
description: 新描述
priority: 新优先级
due_date: 新截止日期
返回:
bool: 更新成功返回True
"""
task = self.get_task(task_id)
if not task:
return False
if title is not None:
task.title = title
if description is not None:
task.description = description
if priority is not None:
task.priority = priority
if due_date is not None:
if isinstance(due_date, str):
task.due_date = datetime.strptime(due_date, "%Y-%m-%d")
else:
task.due_date = due_date
return True
def delete_task(self, task_id):
"""
删除任务
参数:
task_id: 任务ID
返回:
bool: 删除成功返回True
"""
if task_id in self.tasks:
del self.tasks[task_id]
return True
return False
def complete_task(self, task_id):
"""
完成任务
参数:
task_id: 任务ID
返回:
bool: 操作成功返回True
"""
task = self.get_task(task_id)
if task:
task.complete()
return True
return False
def uncomplete_task(self, task_id):
"""
取消任务完成
参数:
task_id: 任务ID
返回:
bool: 操作成功返回True
"""
task = self.get_task(task_id)
if task:
task.uncomplete()
return True
return False
def get_all_tasks(self, sort_by='priority'):
"""
获取所有任务
参数:
sort_by: 排序方式 ('priority', 'due_date', 'created_at')
返回:
list: 任务列表
"""
tasks = list(self.tasks.values())
if sort_by == 'priority':
tasks.sort(key=lambda t: (-t.priority.value, t.id))
elif sort_by == 'due_date':
tasks.sort(key=lambda t: (t.due_date or datetime.max, t.id))
elif sort_by == 'created_at':
tasks.sort(key=lambda t: t.created_at)
return tasks
def get_tasks_by_status(self, completed=None):
"""
按状态获取任务
参数:
completed: 完成状态(True/False/None)
返回:
list: 任务列表
"""
if completed is None:
return list(self.tasks.values())
return [t for t in self.tasks.values() if t.completed == completed]
def get_tasks_by_priority(self, priority):
"""
按优先级获取任务
参数:
priority: 优先级
返回:
list: 任务列表
"""
return [t for t in self.tasks.values() if t.priority == priority]
def get_overdue_tasks(self):
"""
获取过期任务
返回:
list: 任务列表
"""
return [t for t in self.tasks.values() if t.is_overdue()]
def get_statistics(self):
"""
获取统计信息
返回:
dict: 统计信息
"""
total = len(self.tasks)
completed = len(self.get_tasks_by_status(True))
uncompleted = total - completed
overdue = len(self.get_overdue_tasks())
high_priority = len(self.get_tasks_by_priority(Priority.HIGH))
medium_priority = len(self.get_tasks_by_priority(Priority.MEDIUM))
low_priority = len(self.get_tasks_by_priority(Priority.LOW))
return {
'total': total,
'completed': completed,
'uncompleted': uncompleted,
'overdue': overdue,
'high_priority': high_priority,
'medium_priority': medium_priority,
'low_priority': low_priority
}
def search_tasks(self, keyword):
"""
搜索任务
参数:
keyword: 关键词
返回:
list: 任务列表
"""
keyword = keyword.lower()
return [t for t in self.tasks.values()
if keyword in t.title.lower() or keyword in t.description.lower()]
def save_to_file(self, filename='todo_data.json'):
"""
保存数据到文件
参数:
filename: 文件名
"""
data = {
'tasks': [task.to_dict() for task in self.tasks.values()],
'next_id': self.next_id
}
try:
with open(filename, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
print(f"数据已保存到 {filename}")
except Exception as e:
print(f"保存失败: {e}")
def load_from_file(self, filename='todo_data.json'):
"""
从文件加载数据
参数:
filename: 文件名
"""
try:
with open(filename, 'r', encoding='utf-8') as f:
data = json.load(f)
self.tasks = {
task_data['id']: Task.from_dict(task_data)
for task_data in data.get('tasks', [])
}
self.next_id = data.get('next_id', 1)
print(f"数据已从 {filename} 加载")
except FileNotFoundError:
print(f"文件 {filename} 不存在")
except Exception as e:
print(f"加载失败: {e}")
3. 主程序(main.py)¶
from datetime import datetime
from manager import TodoManager
from task import Priority
def display_menu():
"""显示菜单"""
print("\n" + "="*50)
print("待办事项清单")
print("="*50)
print("1. 添加任务")
print("2. 查看所有任务")
print("3. 查看未完成任务")
print("4. 查看已完成任务")
print("5. 查看过期任务")
print("6. 搜索任务")
print("7. 编辑任务")
print("8. 标记完成/未完成")
print("9. 删除任务")
print("10. 统计信息")
print("11. 保存数据")
print("12. 加载数据")
print("0. 退出")
print("="*50)
def display_tasks(tasks, title="任务列表"):
"""显示任务列表"""
if not tasks:
print(f"\n{title}: 没有任务")
return
print(f"\n{title} ({len(tasks)} 个任务):")
print("-" * 60)
for task in tasks:
print(f"{task}")
if task.description:
print(f" 描述: {task.description}")
if task.completed and task.completed_at:
print(f" 完成时间: {task.completed_at.strftime('%Y-%m-%d %H:%M')}")
print()
def main():
"""主函数"""
manager = TodoManager()
while True:
display_menu()
choice = input("请选择操作: ").strip()
if choice == '0':
print("感谢使用,再见!")
break
elif choice == '1':
# 添加任务
print("\n--- 添加新任务 ---")
title = input("任务标题: ").strip()
if not title:
print("标题不能为空")
continue
description = input("任务描述(可选): ").strip()
priority_str = input("优先级(高/中/低,默认中): ").strip() or "中"
priority = Priority.from_string(priority_str)
due_date_str = input("截止日期(YYYY-MM-DD,可选): ").strip()
due_date = None
if due_date_str:
try:
due_date = datetime.strptime(due_date_str, "%Y-%m-%d")
except ValueError:
print("日期格式错误,请使用 YYYY-MM-DD 格式")
task = manager.add_task(title, description, priority, due_date)
print(f"✓ 任务已添加(ID: {task.id})")
elif choice == '2':
# 查看所有任务
tasks = manager.get_all_tasks()
display_tasks(tasks, "所有任务")
elif choice == '3':
# 查看未完成任务
tasks = manager.get_tasks_by_status(False)
display_tasks(tasks, "未完成任务")
elif choice == '4':
# 查看已完成任务
tasks = manager.get_tasks_by_status(True)
display_tasks(tasks, "已完成任务")
elif choice == '5':
# 查看过期任务
tasks = manager.get_overdue_tasks()
display_tasks(tasks, "过期任务")
elif choice == '6':
# 搜索任务
keyword = input("搜索关键词: ").strip()
if keyword:
tasks = manager.search_tasks(keyword)
display_tasks(tasks, f"搜索结果: '{keyword}'")
elif choice == '7':
# 编辑任务
task_id = int(input("任务ID: ").strip())
task = manager.get_task(task_id)
if not task:
print("任务不存在")
continue
print(f"\n当前任务: {task}")
print(f"描述: {task.description}")
print(f"优先级: {task.priority}")
print(f"截止日期: {task.due_date.strftime('%Y-%m-%d') if task.due_date else '无'}")
print("\n--- 编辑任务(留空保持原值) ---")
title = input(f"新标题(当前: {task.title}): ").strip()
description = input(f"新描述(当前: {task.description}): ").strip()
priority_str = input(f"新优先级(当前: {task.priority}): ").strip()
due_date_str = input(f"新截止日期(当前: {task.due_date.strftime('%Y-%m-%d') if task.due_date else '无'}): ").strip()
priority = Priority.from_string(priority_str) if priority_str else None
due_date = None
if due_date_str:
try:
due_date = datetime.strptime(due_date_str, "%Y-%m-%d")
except ValueError:
print("日期格式错误")
manager.update_task(task_id, title or None, description or None, priority, due_date)
print("✓ 任务已更新")
elif choice == '8':
# 标记完成/未完成
task_id = int(input("任务ID: ").strip())
task = manager.get_task(task_id)
if not task:
print("任务不存在")
continue
if task.completed:
manager.uncomplete_task(task_id)
print("✓ 任务已标记为未完成")
else:
manager.complete_task(task_id)
print("✓ 任务已标记为完成")
elif choice == '9':
# 删除任务
task_id = int(input("任务ID: ").strip())
if manager.delete_task(task_id):
print("✓ 任务已删除")
else:
print("任务不存在")
elif choice == '10':
# 统计信息
stats = manager.get_statistics()
print("\n--- 统计信息 ---")
print(f"总任务数: {stats['total']}")
print(f"已完成: {stats['completed']} ({stats['completed']/stats['total']*100:.1f}%)" if stats['total'] > 0 else "已完成: 0")
print(f"未完成: {stats['uncompleted']} ({stats['uncompleted']/stats['total']*100:.1f}%)" if stats['total'] > 0 else "未完成: 0")
print(f"过期: {stats['overdue']}")
print(f"\n按优先级:")
print(f" 高: {stats['high_priority']}")
print(f" 中: {stats['medium_priority']}")
print(f" 低: {stats['low_priority']}")
elif choice == '11':
# 保存数据
filename = input("文件名(默认todo_data.json): ").strip() or 'todo_data.json'
manager.save_to_file(filename)
elif choice == '12':
# 加载数据
filename = input("文件名(默认todo_data.json): ").strip() or 'todo_data.json'
manager.load_from_file(filename)
else:
print("无效的选择,请重新输入")
if __name__ == '__main__':
main()
运行指南¶
1. 安装依赖¶
本项目只使用 Python 标准库,无需安装额外依赖。
2. 运行程序¶
3. 使用说明¶
- 首先添加一些任务
- 可以设置优先级和截止日期
- 定期查看任务列表
- 完成任务后标记为完成
- 定期保存数据
扩展功能建议¶
基础扩展¶
- 任务分类
- 添加任务类别(工作、学习、生活等)
-
按类别筛选任务
-
提醒功能
- 设置任务提醒时间
-
提前提醒过期任务
-
批量操作
- 批量删除任务
- 批量标记完成
进阶扩展¶
- 子任务
- 支持将任务分解为子任务
-
子任务进度跟踪
-
标签系统
- 为任务添加多个标签
-
按标签筛选任务
-
任务模板
- 创建常用任务模板
- 快速创建重复性任务
高级扩展¶
- 多用户支持
- 支持多用户登录
-
每个用户独立的任务列表
-
同步功能
- 与云服务同步
-
跨设备访问
-
图形界面
- 使用 PyQt/Tkinter 开发桌面应用
- 使用 Web 技术开发在线应用
常见问题¶
Q: 如何查看任务的详细信息?¶
A: 选择"编辑任务"选项,输入任务ID即可查看当前任务的所有信息。
Q: 过期任务会自动提醒吗?¶
A: 不会,但你可以随时选择"查看过期任务"来查看所有过期的任务。
Q: 优先级有什么用?¶
A: 优先级决定了任务在列表中的显示顺序(高优先级排在前面)。
Q: 数据保存在哪里?¶
A: 默认保存在程序目录下的 todo_data.json 文件中。
项目总结¶
本项目实现了一个功能完整的待办事项清单应用,涵盖了:
- 任务管理:完整的 CRUD 操作
- 优先级系统:支持高、中、低三个优先级
- 日期管理:支持创建时间、截止日期、完成时间
- 状态管理:完成/未完成状态
- 数据持久化:使用 JSON 保存数据
- 统计分析:多种统计和查询功能
通过本项目,你将掌握: - Python 面向对象编程 - 枚举类型的使用 - datetime 模块的应用 - 数据序列化和持久化 - 用户界面设计
下一步¶
尝试添加更多功能,如: - 任务分类和标签 - 子任务支持 - 图形界面(PyQt/Tkinter) - Web 版本(Flask/Django) - 单元测试
祝你使用愉快!