待办事项应用程序
简介
使用 Python 和 PySide6 开发。它提供了一个直观的图形界面,帮助用户有效地组织和管理他们的日常任务。
主要功能
1. 任务管理
- 添加任务:用户可以轻松添加新的待办事项。
- 完成任务:将任务标记为已完成,自动移至已完成列表。
- 编辑任务:双击待办事项可以编辑任务内容。
- 删除任务:可以从待办或已完成列表中删除任务。
- 恢复任务:已完成的任务可以恢复为未完成状态。
2. 用户界面
- 双列表视图:分别显示待办事项和已完成事项。
- 任务序号:自动为任务添加序号,已完成任务的序号显示为灰色。
- 图标指示:使用不同图标区分待办和已完成任务。
- 多选功能:支持使用 Shift 键进行多选操作。
3. 数据持久化
- 自动保存:任务数据自动保存到本地文件。
- 加载存档:启动时自动加载之前保存的任务。
4. 用户交互
- 确认对话框:重要操作(如删除、完成多个任务)会显示确认对话框。
- 提示信息:操作反馈和错误提示,增强用户体验。
5. 界面美化
- 现代化设计:使用现代化的界面设计,包括圆角、阴影等元素。
- 自定义样式:使用自定义 CSS 样式,提供美观的视觉体验。
- 响应式布局:界面元素会根据窗口大小自适应调整。
6. 辅助功能
- 快捷键支持:支持回车键快速添加新任务。
- 双击编辑:双击待办事项可以快速编辑任务内容。
技术特性
- 使用 Python 和 PySide6 开发,确保跨平台兼容性。
- 采用面向对象编程方法,代码结构清晰,易于维护和扩展。
- 实现了自定义的
NumberedListWidget
类,提供更丰富的列表功能。 - 使用 JSON 格式进行数据存储,确保数据的可读性和可移植性。
使用说明
- 在输入框中输入任务,点击"添加"按钮或按回车键添加新任务。
- 选择任务后,可以点击"完成"、"删除"或"改为未完成"按钮进行相应操作。
- 使用 Shift 键可以进行多选操作。
- 双击待办事项可以编辑任务内容。
- 使用顶部的标签页切换between待办事项和已完成事项列表。
视频链接
1.https://v.kuaishou.com/dr4Kcw 完成代办事项界面的开发"代办事项
2.【完成代办事项界面的开发-哔哩哔哩】https://bili2233.cn/8zCfUQS
声明-License
源代码
"""
:name = Todo(待办事项)
:version = 1.0.0
:author = Jin
:email = 2760320153@qq.com
:license = Get_license(GPLv3)
:copyright = © 2023-Present Jin Rights Reserved
"""
import sys
import json
from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
QHBoxLayout, QListWidget, QLineEdit, QPushButton,
QListWidgetItem, QMessageBox, QInputDialog, QTabWidget)
from PySide6.QtCore import Qt
from PySide6.QtGui import QIcon, QColor, QPainter
class NumberedListWidget(QListWidget):
def __init__(self, is_completed_list=False, parent=None):
super().__init__(parent)
self.setTextElideMode(Qt.ElideRight)
self.is_completed_list = is_completed_list
def paintEvent(self, event):
super().paintEvent(event)
painter = QPainter(self.viewport())
painter.setRenderHint(QPainter.Antialiasing)
for i in range(self.count()):
rect = self.visualItemRect(self.item(i))
if self.is_completed_list:
painter.setPen(QColor("#9ca3af")) # 灰色
else:
painter.setPen(QColor("#000000")) # 黑色
painter.drawText(rect.adjusted(5, 0, 0, 0), Qt.AlignLeft | Qt.AlignVCenter, f"{i + 1}. ")
class TodoApp(QMainWindow):
def show_dialog(self, title, text, icon=QMessageBox.Question):
dialog = QMessageBox(self)
dialog.setWindowTitle(title)
dialog.setText(text)
dialog.setIcon(icon)
dialog.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
dialog.setDefaultButton(QMessageBox.No)
dialog.setStyleSheet("""
QMessageBox {
background-color: #f0f4f8;
}
QLabel {
color: #1f2937;
font-size: 16px;
}
QPushButton {
background-color: #10b981;
color: white;
padding: 6px 12px;
border: none;
border-radius: 4px;
font-size: 14px;
}
QPushButton:hover {
background-color: #059669;
}
""")
return dialog.exec() == QMessageBox.Yes
def __init__(self):
super().__init__()
self.setWindowTitle("待办事项")
self.setGeometry(100, 100, 500, 700)
self.setMinimumSize(500, 700)
self.setWindowIcon(QIcon("task_icon.png"))
self.setStyleSheet("""
QMainWindow {
background-color: #f0f4f8;
}
QListWidget {
background-color: white;
border: 1px solid #d1d5db;
border-radius: 8px;
font-size: 16px;
padding: 5px;
}
QLineEdit {
padding: 10px;
border: 1px solid #d1d5db;
border-radius: 5px;
font-size: 16px;
background-color: white;
}
QPushButton {
background-color: #10b981;
color: white;
padding: 10px 20px;
border: none;
border-radius: 5px;
font-size: 16px;
font-weight: bold;
}
QPushButton#add_button {
background-color: #3b82f6;
}
QPushButton#add_button:hover {
background-color: #2563eb;
}
QPushButton#complete_button, QPushButton#clear_completed_button, QPushButton#uncomplete_button {
background-color: #10b981;
}
QPushButton#complete_button:hover, QPushButton#clear_completed_button:hover, QPushButton#uncomplete_button:hover {
background-color: #059669;
}
QPushButton#delete_button {
background-color: #ef4444;
}
QPushButton#delete_button:hover {
background-color: #dc2626;
}
QListWidget::item {
padding: 10px;
border-bottom: 1px solid #e5e7eb;
padding-left: 30px; /* 为序号留出空间 */
}
QListWidget::item:selected {
background-color: #e5e7eb;
color: #4b5563;
}
QTabWidget::pane {
border: 1px solid #d1d5db;
background-color: white;
border-radius: 8px;
}
QTabBar::tab {
background-color: #e5e7eb;
color: #4b5563;
padding: 8px 16px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
QTabBar::tab:selected {
background-color: white;
color: #1f2937;
}
""")
central_widget = QWidget()
self.setCentralWidget(central_widget)
main_layout = QVBoxLayout(central_widget)
self.tab_widget = QTabWidget()
main_layout.addWidget(self.tab_widget)
# 待办事项标签页
todo_tab = QWidget()
todo_layout = QVBoxLayout(todo_tab)
self.todo_list = NumberedListWidget()
self.todo_list.setSelectionMode(QListWidget.ExtendedSelection)
self.todo_list.itemDoubleClicked.connect(self.edit_task)
self.todo_list.setTextElideMode(Qt.ElideRight)
todo_layout.addWidget(self.todo_list)
input_layout = QHBoxLayout()
self.task_input = QLineEdit()
self.task_input.setPlaceholderText("输入新任务...")
self.add_button = QPushButton("添加")
self.add_button.setObjectName("add_button")
input_layout.addWidget(self.task_input)
input_layout.addWidget(self.add_button)
todo_layout.addLayout(input_layout)
button_layout = QHBoxLayout()
self.complete_button = QPushButton("完成")
self.complete_button.setObjectName("complete_button")
self.delete_button = QPushButton("删除")
self.delete_button.setObjectName("delete_button")
button_layout.addWidget(self.complete_button)
button_layout.addWidget(self.delete_button)
todo_layout.addLayout(button_layout)
self.tab_widget.addTab(todo_tab, "待办事项")
# 已完成事项标签页
completed_tab = QWidget()
completed_layout = QVBoxLayout(completed_tab)
self.completed_list = NumberedListWidget(is_completed_list=True)
self.completed_list.setSelectionMode(QListWidget.ExtendedSelection)
self.completed_list.itemDoubleClicked.connect(self.edit_task)
self.completed_list.setTextElideMode(Qt.ElideRight)
completed_layout.addWidget(self.completed_list)
completed_button_layout = QHBoxLayout()
self.uncomplete_button = QPushButton("改为未完成")
self.uncomplete_button.setObjectName("uncomplete_button")
self.clear_completed_button = QPushButton("清空已完成任务")
self.clear_completed_button.setObjectName("clear_completed_button")
completed_button_layout.addWidget(self.uncomplete_button)
completed_button_layout.addWidget(self.clear_completed_button)
completed_layout.addLayout(completed_button_layout)
self.tab_widget.addTab(completed_tab, "已完成事项")
# 连接信号和槽
self.add_button.clicked.connect(self.add_task)
self.complete_button.clicked.connect(self.complete_task)
self.delete_button.clicked.connect(self.delete_task)
self.uncomplete_button.clicked.connect(self.uncomplete_task)
self.clear_completed_button.clicked.connect(self.clear_completed_tasks)
self.task_input.returnPressed.connect(self.add_task)
# 设置Shift多选
self.todo_list.itemSelectionChanged.connect(self.handle_selection_changed)
self.completed_list.itemSelectionChanged.connect(self.handle_selection_changed)
# 加载保存的任务
self.load_tasks()
def add_task(self):
task = self.task_input.text().strip()
if task:
item = QListWidgetItem(QIcon("task_icon.png"), task)
item.setFlags(item.flags() & ~Qt.ItemIsEditable)
self.todo_list.addItem(item)
self.task_input.clear()
self.save_tasks()
def complete_task(self):
selected_items = self.todo_list.selectedItems()
if selected_items:
if self.show_dialog("确认完成", f"您确定要将选中的 {len(selected_items)} 个任务标记为已完成吗?"):
for item in selected_items:
self.todo_list.takeItem(self.todo_list.row(item))
item.setIcon(QIcon("complete_task_icon.png"))
item.setForeground(QColor("#9ca3af"))
self.completed_list.addItem(item)
self.save_tasks()
else:
QMessageBox.information(self, "提示", "请先选择要完成的任务")
def delete_task(self):
current_list = self.todo_list if self.tab_widget.currentIndex() == 0 else self.completed_list
selected_items = current_list.selectedItems()
if selected_items:
if self.show_dialog("确认删除", f"您确定要删除选中的 {len(selected_items)} 个任务吗?"):
for item in selected_items:
current_list.takeItem(current_list.row(item))
self.save_tasks()
else:
QMessageBox.information(self, "提示", "请先选择要删除的任务")
def uncomplete_task(self):
selected_items = self.completed_list.selectedItems()
if selected_items:
if self.show_dialog("确认改为未完成", f"您确定要将选中的 {len(selected_items)} 个任务改为未完成吗?"):
for item in selected_items:
self.completed_list.takeItem(self.completed_list.row(item))
item.setIcon(QIcon("task_icon.png"))
item.setForeground(QColor("black"))
self.todo_list.addItem(item)
self.save_tasks()
else:
QMessageBox.information(self, "提示", "请先选择要改为未完成的任务")
def edit_task(self, item):
if self.tab_widget.currentIndex() == 0: # 只有在待办事项标签页才允许编辑
old_text = item.text()
new_text, ok = QInputDialog.getText(self, "编辑任务", "请输入新的任务内容:", text=old_text)
if ok and new_text:
item.setText(new_text)
self.save_tasks()
else:
QMessageBox.information(self, "提示", "已完成的任务不能修改")
def clear_completed_tasks(self):
if self.completed_list.count() > 0:
if self.show_dialog("确认清空", "您确定要清空所有已完成的任务吗?"):
self.completed_list.clear()
self.save_tasks()
else:
QMessageBox.information(self, "提示", "没有已完成的任务可清空")
def handle_selection_changed(self):
modifiers = QApplication.keyboardModifiers()
if modifiers != Qt.ShiftModifier:
self.last_selected_item = None
return
current_list = self.todo_list if self.tab_widget.currentIndex() == 0 else self.completed_list
selected_items = current_list.selectedItems()
if not selected_items:
return
if self.last_selected_item is None:
self.last_selected_item = selected_items[0]
return
start = current_list.row(self.last_selected_item)
end = current_list.row(selected_items[-1])
if start > end:
start, end = end, start
current_list.clearSelection()
for i in range(start, end + 1):
current_list.item(i).setSelected(True)
self.last_selected_item = selected_items[-1]
def save_tasks(self):
tasks = {
'todo': [],
'completed': []
}
for i in range(self.todo_list.count()):
item = self.todo_list.item(i)
tasks['todo'].append(item.text())
for i in range(self.completed_list.count()):
item = self.completed_list.item(i)
tasks['completed'].append(item.text())
with open('tasks.json', 'w', encoding='utf-8') as f:
json.dump(tasks, f, ensure_ascii=False)
def load_tasks(self):
try:
with open('tasks.json', 'r', encoding='utf-8') as f:
tasks = json.load(f)
if isinstance(tasks, list):
# Old format: list of tasks
for task in tasks:
item = QListWidgetItem(
QIcon("task_icon.png" if not task['completed'] else "complete_task_icon.png"), task['text'])
item.setFlags(item.flags() & ~Qt.ItemIsEditable)
if task['completed']:
item.setForeground(QColor("#9ca3af"))
self.completed_list.addItem(item)
else:
self.todo_list.addItem(item)
elif isinstance(tasks, dict):
# New format: dictionary with 'todo' and 'completed' keys
for task in tasks.get('todo', []):
item = QListWidgetItem(QIcon("task_icon.png"), task)
item.setFlags(item.flags() & ~Qt.ItemIsEditable)
self.todo_list.addItem(item)
for task in tasks.get('completed', []):
item = QListWidgetItem(QIcon("complete_task_icon.png"), task)
item.setFlags(item.flags() & ~Qt.ItemIsEditable)
item.setForeground(QColor("#9ca3af"))
self.completed_list.addItem(item)
else:
raise ValueError("tasks.json中的数据格式异常")
except FileNotFoundError:
pass
except json.JSONDecodeError:
QMessageBox.warning(self, "错误", "任务文件已损坏。将从空任务列表开始。")
except ValueError as e:
QMessageBox.warning(self, "错误", str(e))
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setStyle('Fusion')
todo_app = TodoApp()
todo_app.show()
sys.exit(app.exec())