我正在参加Trae「超级体验官」创意实践征文,本文所使用的 Trae 免费下载链接:https://www.trae.com.cn/?utm_source=juejin&utm_medium=juejin_trae&utm_campaign=422content
前言
番茄钟是一种时间管理方法和工具,最近由于我比较摆烂,就萌生出了想做一款属于自己的番茄钟在电脑桌面记录着我的学习,但是由于我不知道如何使用代码完成这一个小项目,我就苦恼了很久,但是最近trae突然爆火,我就想试试到底有传说中的那么厉害
下面是我制作出来的番茄钟效果展示:
番茄钟
摸鱼让trae帮我做一个番茄钟
我们下载好了之后进行登录,发现右侧栏就是我们询问ai的界面了,因为我们的这个主题是让他帮我生成一款电脑番茄钟软件,我们直接用小白话术和他对话:我现在想在电脑上做一款番茄钟应用,请你帮我实现
ai的反应速度还是蛮快的,并且回复我们的话也是很有逻辑的
他还生成了一个requirements.txt
来告诉我们需要安装什么依赖
以及一个README.md
来告诉我们这款软件的基本信息、功能特点、安装说明、使用方法等关键内容
等待他生成代码之后,我们直接在终端输入命令python pomodoro.py
运行这个文件
下面是效果展示
但是感觉还是有点单调说实话,正常来说,番茄钟是可以进行设置番茄时间、休息时间,还能统计任务完成情况,实体番茄钟则通常具有简单直观的操作界面,通过旋转表盘或按下按钮来启动计时。
他这个初步生成的代码虽然还不错,但是的话还远远达不到我们的预期,于是我们就继续进行ai代码生成,将我们的要求输送给他
在第二次ai代码生成的时候,我对他说:
正常来说,番茄钟是可以进行设置番茄时间、休息时间,还能统计任务完成情况,实体番茄钟则通常具有简单直观的操作界面,通过旋转表盘或按下按钮来启动计时。,而不是单单就是25分钟,可以自定义设定时间,并且可以设置任务
他的回答如下,在本次ai中他在代码中增加了时间自定义的能力以及添加添加任务的需求
依旧输入命令python pomodoro.py
运行下
效果如下:
体验下来我说说我的想法:能进行计时操作,可以添加任务,但是这个任务似乎点击完成任务不能消失,并且添加任务和这个时间都放在了同一个页面上,是否能搞成和手机番茄钟那样的效果呢?
下面是我在手机番茄钟找的界面效果图
对比我们当前的界面,我们缺少的是多样化的界面,历史数据统计,个人主页,导航栏
有了这些需求,我们直接将这些需求喂给ai,让他帮我们解决
根绝我的需求,他会根据每个需求点来创建对应的代码文件
在本次改动下
ai已经完成了对番茄钟应用的重大改进,根据我的需求实现了多页面导航和更丰富的功能。主要更新包括:
-
创建了新的多页面主窗口架构:
- 实现了底部导航栏,可以在不同功能页面之间切换
- 将原有的单一界面拆分为四个独立的功能页面
-
改进了计时器页面(PomodoroTimer):
- 将原有的主窗口类改造为可重用的组件
- 保留了核心的计时和提醒功能
-
新增了独立的任务管理页面(TaskManager):
- 分离了任务列表功能
- 实现了已完成任务的自动隐藏(移动到已完成任务列表)
- 优化了任务管理界面的布局
-
添加了统计数据页面(Statistics):
- 提供今日、本周、本月的统计数据
- 显示专注时长和任务完成情况
- 使用滚动区域展示详细统计信息
-
创建了个人主页(Profile):
- 提供用户个性化设置
- 可以设置默认的工作和休息时长
- 支持主题切换和提示音设置
这些改进使应用更加符合现代移动应用的设计理念,提供了更好的用户体验。用户现在可以:
- 在不同功能页面之间自由切换
- 查看任务完成历史
- 追踪使用数据统计
- 个性化定制应用设置
新的界面结构更加清晰,功能更加完整,为用户提供了更好的时间管理和任务追踪体验。
但是这个代码貌似运行不了,我们直接将问题反馈给ai,让他帮我们解决问题
他会逐个文件帮我分析代码,检查是否是组件的问题,以及文件之间的关联性
在代码修正之后,点击运行,效果稍微还行,有了底部的导航界面,但是还是没有达到心中预期,那么我们再次进行代码的修改
这一次我们的要求是:这个历史数据是否能够保存呢?每次打开这个程序都会存在历史数据呢?这个任务管理虽然可以将进行中的任务进行完成,但是我如果想将这个任务删除呢?好像没有方法进行删除操作,这个主页的话能否移除这个任务列表,因为我觉得这个任务列表在底部的任务里面已经有了,那么就没必要重复出现了,界面再稍微优化下,仍然是有些单调
经过修改之后
效果还是蛮不错的,完成任务还有弹窗,并且可以进行任务的删除操作以及任务的累计操作,在工作时间结束之后系统会有提示音,但是还是不足
这个统计数据面板好像没用啊,并不能统计我完成过的任务
计时的那个页面也是的,不能统计我今天的专注时间,我在将程序退出之后,之前删除的任务也出现在这里了,并且下面的已经完成的任务没有进行统计操作
直接询问ai
代码改变之后
- 添加了实时记录专注时间的功能:
-
新增 accumulated_focus_time 变量,用于累计专注时间(以秒为单位)
-
每秒钟都会累加专注时间
-
每满一分钟就保存一次专注时间到数据管理器中
- 在以下情况下会保存专注时间:
-
每满一分钟自动保存
-
点击暂停时保存
-
点击重置时保存
-
工作时间结束时保存
-
关闭窗口时保存
- 优化了专注时间的计算:
-
只在工作时间段计算专注时间
-
保留不足一分钟的秒数,下次继续累加
-
确保所有专注时间都被正确记录
- 添加了窗口关闭事件处理:
- 在关闭窗口时保存未记录的专注时间
这些修改确保了:
-
专注时间会实时累计
-
即使中途暂停或重置,已经专注的时间也不会丢失
-
时间统计更加准确
现在您可以重新运行程序,专注时间应该会正确累计到统计数据中。每次暂停、重置或完成一个工作周期时,都会看到统计数据的更新。
每次我们重新进入到程序之后,他会显示我们历史的专注时长
至此,我们的番茄钟就搞定了,其实我这里的还是比较简单的,如果你们真的想开发出一个属于自己的番茄钟的话,可以多花点时间在trae上面,进行魔改
下面是我的几个改进方案,大家可以考虑下:
- 可以对图标进行改变,改成一个番茄图标
- 可以对提示音进行改进,每次计时完成时候将默认的声音改成二次元的声音
- 将背景颜色进行改变
- 对每个按钮进行一个UI设置,设置为自己喜欢的图案
- 切换界面更加流畅不卡顿
我发现他是使用了一个文件来保存我每次设置的任务的,然后每次启动读取这个数据来获取历史信息,这个想法确实厉害,值得学习下
下面是我的RAEDME文件,大家可以进行一个参考
README文件详情
番茄钟应用
一个基于PyQt5开发的简洁番茄钟计时器应用,帮助用户提高工作效率和时间管理能力。具有任务管理、时间统计等功能。
功能特点
-
可自定义工作时间(1-60分钟)和休息时间(1-30分钟)
-
完整的任务管理系统
-
添加、完成、删除任务
-
查看任务完成历史
-
任务持久化存储
-
-
专注时间统计
-
今日专注时长统计
-
本周专注时长统计
-
本月专注时长统计
-
任务完成数量统计
-
-
简洁直观的用户界面
-
实时倒计时显示
-
工作/休息状态自动切换
-
时间结束提示音
-
开始/暂停/继续/重置功能
-
-
数据持久化
-
自动保存专注时间
-
自动保存任务状态
-
程序重启后保持数据
-
技术实现
-
使用PyQt5构建图形用户界面
-
采用QTimer实现精确计时
-
JSON文件实现数据持久化存储
-
模块化设计,功能解耦
-
使用winsound实现提示音效
安装说明
-
确保已安装Python 3.x
-
安装依赖包:
pip install -r requirements.txt
使用方法
-
运行程序:
python main.py
-
操作说明:
-
计时功能:
-
点击"开始"按钮启动计时器
-
计时过程中可点击"暂停"按钮暂停计时
-
暂停状态下可点击"继续"按钮继续计时
-
点击"重置"按钮重置计时器
-
时间结束时会自动切换工作/休息状态并播放提示音
-
-
任务管理:
-
在任务页面可以添加新任务
-
点击"完成任务"标记任务为已完成
-
点击"删除任务"移除任务
-
-
统计功能:
-
在统计页面可以查看专注时间统计
-
包括今日、本周、本月的专注时长
-
查看各时间段内完成的任务数量
-
-
项目结构
番茄钟软件/
├── main.py # 程序入口
├── main_window.py # 主窗口管理
├── pomodoro.py # 番茄钟计时器
├── task_manager.py # 任务管理模块
├── statistics.py # 统计功能模块
├── data_manager.py # 数据管理模块
├── profile.py # 用户配置模块
├── requirements.txt # 项目依赖文件
└── pomodoro_data.json # 数据存储文件
开发环境
-
Python 3.x
-
PyQt5 >= 5.15.0
-
Windows操作系统(提示音功能)
数据存储
应用数据存储在 pomodoro_data.json
文件中,包含:
-
任务列表
-
已删除的任务
-
专注时间记录
注意事项
-
专注时间会在以下情况自动保存:
-
每完成一个工作周期
-
暂停计时器时
-
重置计时器时
-
关闭程序时
-
-
统计数据实时更新,可以随时查看当前的专注状况
-
所有数据都会自动保存,重启程序后可以继续查看历史记录
具体代码
data_manager.py
import json
from datetime import datetime, timedelta
import os
class DataManager:
def __init__(self):
self.data_file = 'pomodoro_data.json'
self.tasks = []
self.focus_time = [] # [(duration_minutes, datetime), ...]
self.deleted_tasks = [] # 存储已删除的任务
self.load_data()
def save_data(self):
data = {
'tasks': [{
'content': task['content'],
'created_at': task['created_at'].isoformat(),
'completed': task['completed'],
'completed_at': task['completed_at'].isoformat() if task.get('completed_at') else None
} for task in self.tasks],
'focus_time': [[duration, dt.isoformat()] for duration, dt in self.focus_time],
'deleted_tasks': [{
'content': task['content'],
'created_at': task['created_at'].isoformat(),
'completed': task['completed'],
'completed_at': task['completed_at'].isoformat() if task.get('completed_at') else None,
'deleted_at': task['deleted_at'].isoformat()
} for task in self.deleted_tasks]
}
with open(self.data_file, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
def load_data(self):
if not os.path.exists(self.data_file):
return
try:
with open(self.data_file, 'r', encoding='utf-8') as f:
data = json.load(f)
# 使用集合来去除重复的任务
unique_tasks = {}
for task in data.get('tasks', []):
key = (task['content'], task['created_at'])
if key not in unique_tasks:
unique_tasks[key] = {
'content': task['content'],
'created_at': datetime.fromisoformat(task['created_at']),
'completed': task['completed'],
'completed_at': datetime.fromisoformat(task['completed_at']) if task.get('completed_at') else None
}
self.tasks = list(unique_tasks.values())
self.focus_time = [(duration, datetime.fromisoformat(dt))
for duration, dt in data.get('focus_time', [])]
if 'deleted_tasks' in data:
self.deleted_tasks = [{
'content': task['content'],
'created_at': datetime.fromisoformat(task['created_at']),
'completed': task['completed'],
'completed_at': datetime.fromisoformat(task['completed_at']) if task.get('completed_at') else None,
'deleted_at': datetime.fromisoformat(task['deleted_at'])
} for task in data['deleted_tasks']]
except Exception as e:
print(f'Error loading data: {e}')
def add_task(self, task):
# 检查是否存在相同内容和创建时间的任务
for existing_task in self.tasks:
if (existing_task['content'] == task['content'] and
existing_task['created_at'] == task['created_at']):
return # 如果任务已存在,则不添加
self.tasks.append(task)
self.save_data()
def update_task(self, task):
for i, t in enumerate(self.tasks):
if t['content'] == task['content'] and not t['completed']:
self.tasks[i] = task
break
self.save_data()
def add_focus_time(self, duration_minutes):
self.focus_time.append((duration_minutes, datetime.now()))
self.save_data()
def get_tasks(self):
return self.tasks
def get_focus_time(self):
return self.focus_time
def delete_task(self, task_content):
for i, task in enumerate(self.tasks):
if task['content'] == task_content:
deleted_task = self.tasks.pop(i)
deleted_task['deleted_at'] = datetime.now()
self.deleted_tasks.append(deleted_task)
self.save_data()
break
def get_deleted_tasks(self):
return self.deleted_tasks
def get_statistics(self, period='today'):
now = datetime.now()
if period == 'today':
start_time = datetime(now.year, now.month, now.day)
elif period == 'week':
start_time = now - timedelta(days=now.weekday())
start_time = datetime(start_time.year, start_time.month, start_time.day)
elif period == 'month':
start_time = datetime(now.year, now.month, 1)
else:
return 0, 0 # 默认返回0
# 计算专注时间
total_focus_time = sum(duration for duration, dt in self.focus_time if dt >= start_time)
# 计算完成的任务数
completed_tasks = sum(1 for task in self.tasks
if task['completed']
and task.get('completed_at')
and task['completed_at'] >= start_time)
return total_focus_time, completed_tasks
def get_today_focus_time(self):
total_time, _ = self.get_statistics('today')
hours = total_time // 60
minutes = total_time % 60
return f"{hours}小时{minutes}分钟"
main_window.py
import sys
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QStackedWidget, QPushButton, QLabel)
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont
from pomodoro import PomodoroTimer
from task_manager import TaskManager
from statistics import Statistics
from profile import Profile
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('番茄钟')
self.setGeometry(100, 100, 400, 600)
# 创建主窗口部件
central_widget = QWidget()
self.setCentralWidget(central_widget)
layout = QVBoxLayout(central_widget)
# 创建堆叠部件用于页面切换
self.stack = QStackedWidget()
# 添加各个页面
self.timer_page = PomodoroTimer()
self.task_page = QWidget()
self.stats_page = QWidget()
self.profile_page = QWidget()
# 创建各个页面的实例
self.task_page = TaskManager()
self.stats_page = Statistics()
self.profile_page = Profile()
# 将页面添加到堆叠部件中
self.stack.addWidget(self.timer_page)
self.stack.addWidget(self.task_page)
self.stack.addWidget(self.stats_page)
self.stack.addWidget(self.profile_page)
layout.addWidget(self.stack)
# 创建底部导航栏
nav_bar = QHBoxLayout()
# 创建导航按钮
self.timer_btn = QPushButton('计时')
self.task_btn = QPushButton('任务')
self.stats_btn = QPushButton('统计')
self.profile_btn = QPushButton('我的')
# 设置按钮点击事件
self.timer_btn.clicked.connect(lambda: self.stack.setCurrentIndex(0))
self.task_btn.clicked.connect(lambda: self.stack.setCurrentIndex(1))
self.stats_btn.clicked.connect(lambda: self.stack.setCurrentIndex(2))
self.profile_btn.clicked.connect(lambda: self.stack.setCurrentIndex(3))
# 添加按钮到导航栏
nav_bar.addWidget(self.timer_btn)
nav_bar.addWidget(self.task_btn)
nav_bar.addWidget(self.stats_btn)
nav_bar.addWidget(self.profile_btn)
layout.addLayout(nav_bar)
def main():
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
main.py
import sys
from PyQt5.QtWidgets import QApplication
from main_window import MainWindow
def main():
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
pomdoro.py
import sys
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QPushButton, QLabel,
QSpinBox, QHBoxLayout)
from PyQt5.QtCore import QTimer, Qt
from PyQt5.QtGui import QFont
import winsound
from datetime import datetime
from data_manager import DataManager
class PomodoroTimer(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
# 初始化变量
self.work_duration = 25 # 默认工作时间(分钟)
self.break_duration = 5 # 默认休息时间(分钟)
self.time_left = self.work_duration * 60
self.is_break = False
self.timer = QTimer()
self.timer.timeout.connect(self.update_timer)
self.is_running = False
self.data_manager = DataManager()
self.accumulated_focus_time = 0 # 累计专注时间(秒)
self.last_focus_time = None # 上次记录时间
# 创建专注时间更新计时器
self.focus_timer = QTimer()
self.focus_timer.timeout.connect(self.update_focus_time)
self.focus_timer.start(60000) # 每分钟更新一次
# 创建主窗口部件
central_widget = QWidget()
self.setCentralWidget(central_widget)
layout = QVBoxLayout(central_widget)
# 创建时间设置部分
time_settings = QHBoxLayout()
# 工作时间设置
work_layout = QVBoxLayout()
work_label = QLabel('工作时间(分钟):')
self.work_spinbox = QSpinBox()
self.work_spinbox.setRange(1, 60)
self.work_spinbox.setValue(self.work_duration)
self.work_spinbox.valueChanged.connect(self.update_work_duration)
work_layout.addWidget(work_label)
work_layout.addWidget(self.work_spinbox)
time_settings.addLayout(work_layout)
# 休息时间设置
break_layout = QVBoxLayout()
break_label = QLabel('休息时间(分钟):')
self.break_spinbox = QSpinBox()
self.break_spinbox.setRange(1, 30)
self.break_spinbox.setValue(self.break_duration)
self.break_spinbox.valueChanged.connect(self.update_break_duration)
break_layout.addWidget(break_label)
break_layout.addWidget(self.break_spinbox)
time_settings.addLayout(break_layout)
layout.addLayout(time_settings)
# 创建时间显示标签
self.time_label = QLabel('25:00')
self.time_label.setAlignment(Qt.AlignCenter)
self.time_label.setFont(QFont('Arial', 60))
layout.addWidget(self.time_label)
# 创建状态标签
self.status_label = QLabel('工作时间')
self.status_label.setAlignment(Qt.AlignCenter)
self.status_label.setFont(QFont('Arial', 20))
layout.addWidget(self.status_label)
# 创建专注信息显示
self.focus_label = QLabel('今日专注:0小时0分钟')
self.focus_label.setAlignment(Qt.AlignCenter)
self.focus_label.setFont(QFont('Arial', 12))
layout.addWidget(self.focus_label)
# 创建控制按钮
button_layout = QHBoxLayout()
self.start_button = QPushButton('开始')
self.start_button.clicked.connect(self.toggle_timer)
button_layout.addWidget(self.start_button)
self.reset_button = QPushButton('重置')
self.reset_button.clicked.connect(self.reset_timer)
button_layout.addWidget(self.reset_button)
layout.addLayout(button_layout)
# 初始化显示
self.update_focus_time()
def update_timer(self):
if self.time_left > 0:
self.time_left -= 1
if not self.is_break: # 只在工作时间计算专注时间
self.accumulated_focus_time += 1
# 每分钟保存一次专注时间
if self.accumulated_focus_time % 60 == 0:
self.save_focus_time()
minutes = self.time_left // 60
seconds = self.time_left % 60
self.time_label.setText(f'{minutes:02d}:{seconds:02d}')
else:
self.timer.stop()
winsound.Beep(1000, 1000) # 发出提示音
if not self.is_break:
# 保存剩余的专注时间
self.save_focus_time()
self.accumulated_focus_time = 0
self.time_left = self.break_duration * 60
self.is_break = True
self.status_label.setText('休息时间')
else:
self.time_left = self.work_duration * 60
self.is_break = False
self.status_label.setText('工作时间')
self.is_running = False
self.start_button.setText('开始')
self.update_display()
def toggle_timer(self):
if not self.is_running:
self.timer.start(1000)
self.is_running = True
self.start_button.setText('暂停')
if not self.is_break:
self.last_focus_time = datetime.now()
else:
self.timer.stop()
self.is_running = False
self.start_button.setText('继续')
if not self.is_break:
self.save_focus_time()
def reset_timer(self):
if self.is_running and not self.is_break:
self.save_focus_time()
self.timer.stop()
self.time_left = self.work_duration * 60
self.is_break = False
self.is_running = False
self.start_button.setText('开始')
self.status_label.setText('工作时间')
self.accumulated_focus_time = 0
self.update_display()
def save_focus_time(self):
# 保存累计的专注时间(转换为分钟)
if self.accumulated_focus_time > 0:
minutes = self.accumulated_focus_time // 60
if minutes > 0:
self.data_manager.add_focus_time(minutes)
self.accumulated_focus_time = self.accumulated_focus_time % 60 # 保留不足1分钟的秒数
self.update_focus_time()
def update_work_duration(self, value):
self.work_duration = value
if not self.is_running and not self.is_break:
self.time_left = value * 60
self.update_display()
def update_break_duration(self, value):
self.break_duration = value
def update_focus_time(self):
# 使用 DataManager 的统计功能获取今日专注时间
self.focus_label.setText(f'今日专注:{self.data_manager.get_today_focus_time()}')
def update_display(self):
minutes = self.time_left // 60
seconds = self.time_left % 60
self.time_label.setText(f'{minutes:02d}:{seconds:02d}')
def showEvent(self, event):
super().showEvent(event)
self.update_focus_time() # 每次显示页面时更新专注时间
def closeEvent(self, event):
if self.is_running and not self.is_break:
self.save_focus_time() # 确保关闭窗口时保存专注时间
super().closeEvent(event)
profile.py
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton,
QComboBox, QSpinBox, QLineEdit)
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont
import json
import os
class Profile(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setup_ui()
def setup_ui(self):
layout = QVBoxLayout(self)
# 标题
title = QLabel('个人主页')
title.setAlignment(Qt.AlignCenter)
title.setFont(QFont('Arial', 20))
layout.addWidget(title)
# 用户信息
info_layout = QVBoxLayout()
# 用户名设置
username_layout = QHBoxLayout()
username_label = QLabel('用户名:')
self.username_edit = QLineEdit()
username_layout.addWidget(username_label)
username_layout.addWidget(self.username_edit)
info_layout.addLayout(username_layout)
# 默认工作时长设置
work_time_layout = QHBoxLayout()
work_time_label = QLabel('默认工作时长(分钟):')
self.work_time_spin = QSpinBox()
self.work_time_spin.setRange(1, 60)
self.work_time_spin.setValue(25)
work_time_layout.addWidget(work_time_label)
work_time_layout.addWidget(self.work_time_spin)
info_layout.addLayout(work_time_layout)
# 默认休息时长设置
break_time_layout = QHBoxLayout()
break_time_label = QLabel('默认休息时长(分钟):')
self.break_time_spin = QSpinBox()
self.break_time_spin.setRange(1, 30)
self.break_time_spin.setValue(5)
break_time_layout.addWidget(break_time_label)
break_time_layout.addWidget(self.break_time_spin)
info_layout.addLayout(break_time_layout)
# 主题设置
theme_layout = QHBoxLayout()
theme_label = QLabel('主题:')
self.theme_combo = QComboBox()
self.theme_combo.addItems(['浅色', '深色'])
theme_layout.addWidget(theme_label)
theme_layout.addWidget(self.theme_combo)
info_layout.addLayout(theme_layout)
# 提示音设置
sound_layout = QHBoxLayout()
sound_label = QLabel('提示音:')
self.sound_combo = QComboBox()
self.sound_combo.addItems(['默认', '静音'])
sound_layout.addWidget(sound_label)
sound_layout.addWidget(self.sound_combo)
info_layout.addLayout(sound_layout)
layout.addLayout(info_layout)
# 保存按钮
save_button = QPushButton('保存设置')
save_button.clicked.connect(self.save_settings)
layout.addWidget(save_button)
# 添加弹性空间
layout.addStretch()
def save_settings(self):
settings = {
'username': self.username_edit.text(),
'work_time': self.work_time_spin.value(),
'break_time': self.break_time_spin.value(),
'theme': self.theme_combo.currentText(),
'sound': self.sound_combo.currentText()
}
try:
with open('user_settings.json', 'w', encoding='utf-8') as f:
json.dump(settings, f, ensure_ascii=False, indent=2)
except Exception as e:
print(f'Error saving settings: {e}')
def load_settings(self):
try:
if os.path.exists('user_settings.json'):
with open('user_settings.json', 'r', encoding='utf-8') as f:
settings = json.load(f)
self.username_edit.setText(settings.get('username', ''))
self.work_time_spin.setValue(settings.get('work_time', 25))
self.break_time_spin.setValue(settings.get('break_time', 5))
theme_index = self.theme_combo.findText(settings.get('theme', '浅色'))
if theme_index >= 0:
self.theme_combo.setCurrentIndex(theme_index)
sound_index = self.sound_combo.findText(settings.get('sound', '默认'))
if sound_index >= 0:
self.sound_combo.setCurrentIndex(sound_index)
except Exception as e:
print(f'Error loading settings: {e}')
requirements.txt
PyQt5>=5.15.0
statistics.py
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QScrollArea
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont
from datetime import datetime, timedelta
from data_manager import DataManager
class Statistics(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.data_manager = DataManager()
self.setup_ui()
self.update_statistics()
def setup_ui(self):
layout = QVBoxLayout(self)
# 创建滚动区域
scroll = QScrollArea()
scroll.setWidgetResizable(True)
content_widget = QWidget()
scroll_layout = QVBoxLayout(content_widget)
# 标题
title = QLabel('统计数据')
title.setAlignment(Qt.AlignCenter)
title.setFont(QFont('Arial', 20))
scroll_layout.addWidget(title)
# 今日统计
today_stats = QLabel('今日统计')
today_stats.setFont(QFont('Arial', 16))
scroll_layout.addWidget(today_stats)
self.today_focus_time = QLabel('专注时长:0小时0分钟')
self.today_completed_tasks = QLabel('完成任务:0个')
scroll_layout.addWidget(self.today_focus_time)
scroll_layout.addWidget(self.today_completed_tasks)
# 本周统计
week_stats = QLabel('本周统计')
week_stats.setFont(QFont('Arial', 16))
scroll_layout.addWidget(week_stats)
self.week_focus_time = QLabel('专注时长:0小时0分钟')
self.week_completed_tasks = QLabel('完成任务:0个')
scroll_layout.addWidget(self.week_focus_time)
scroll_layout.addWidget(self.week_completed_tasks)
# 本月统计
month_stats = QLabel('本月统计')
month_stats.setFont(QFont('Arial', 16))
scroll_layout.addWidget(month_stats)
self.month_focus_time = QLabel('专注时长:0小时0分钟')
self.month_completed_tasks = QLabel('完成任务:0个')
scroll_layout.addWidget(self.month_focus_time)
scroll_layout.addWidget(self.month_completed_tasks)
# 设置滚动区域
scroll.setWidget(content_widget)
layout.addWidget(scroll)
def update_statistics(self):
# 更新今日统计
today_focus, today_tasks = self.data_manager.get_statistics('today')
self.today_focus_time.setText(f'专注时长:{today_focus//60}小时{today_focus%60}分钟')
self.today_completed_tasks.setText(f'完成任务:{today_tasks}个')
# 更新本周统计
week_focus, week_tasks = self.data_manager.get_statistics('week')
self.week_focus_time.setText(f'专注时长:{week_focus//60}小时{week_focus%60}分钟')
self.week_completed_tasks.setText(f'完成任务:{week_tasks}个')
# 更新本月统计
month_focus, month_tasks = self.data_manager.get_statistics('month')
self.month_focus_time.setText(f'专注时长:{month_focus//60}小时{month_focus%60}分钟')
self.month_completed_tasks.setText(f'完成任务:{month_tasks}个')
def showEvent(self, event):
super().showEvent(event)
self.update_statistics() # 每次显示页面时更新统计数据
task_manager.py
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel,
QListWidget, QListWidgetItem, QInputDialog, QMessageBox)
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont
from datetime import datetime
from data_manager import DataManager
class TaskManager(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.data_manager = DataManager()
self.tasks = self.data_manager.get_tasks()
self.completed_tasks = sum(1 for task in self.tasks if task['completed'])
self.setup_ui()
self.load_tasks()
def setup_ui(self):
layout = QVBoxLayout(self)
# 标题
title = QLabel('任务管理')
title.setAlignment(Qt.AlignCenter)
title.setFont(QFont('Arial', 20))
layout.addWidget(title)
# 任务列表
self.active_tasks_label = QLabel('进行中的任务')
self.active_tasks_label.setAlignment(Qt.AlignLeft)
self.active_tasks_label.setFont(QFont('Arial', 12))
layout.addWidget(self.active_tasks_label)
self.active_task_list = QListWidget()
layout.addWidget(self.active_task_list)
# 已完成任务列表
self.completed_tasks_label = QLabel('已完成的任务')
self.completed_tasks_label.setAlignment(Qt.AlignLeft)
self.completed_tasks_label.setFont(QFont('Arial', 12))
layout.addWidget(self.completed_tasks_label)
self.completed_task_list = QListWidget()
layout.addWidget(self.completed_task_list)
# 按钮布局
button_layout = QHBoxLayout()
self.add_button = QPushButton('添加任务')
self.add_button.clicked.connect(self.add_task)
button_layout.addWidget(self.add_button)
self.complete_button = QPushButton('完成任务')
self.complete_button.clicked.connect(self.complete_task)
button_layout.addWidget(self.complete_button)
self.delete_button = QPushButton('删除任务')
self.delete_button.clicked.connect(self.delete_task)
button_layout.addWidget(self.delete_button)
layout.addLayout(button_layout)
# 统计信息
self.stats_label = QLabel('已完成任务数:0')
self.stats_label.setAlignment(Qt.AlignCenter)
layout.addWidget(self.stats_label)
def load_tasks(self):
# 清空列表
self.active_task_list.clear()
self.completed_task_list.clear()
# 重新加载任务
for task in self.tasks:
item = QListWidgetItem(task['content'])
if task['completed']:
self.completed_task_list.addItem(item)
else:
self.active_task_list.addItem(item)
def add_task(self):
text, ok = QInputDialog.getText(self, '添加任务', '请输入任务内容:')
if ok and text:
task = {
'content': text,
'created_at': datetime.now(),
'completed': False
}
item = QListWidgetItem(text)
self.active_task_list.addItem(item)
self.tasks.append(task)
self.data_manager.add_task(task)
def complete_task(self):
current_item = self.active_task_list.currentItem()
if current_item:
# 从活动任务列表移除
row = self.active_task_list.row(current_item)
self.active_task_list.takeItem(row)
# 添加到已完成任务列表
self.completed_task_list.addItem(current_item)
# 更新统计信息
self.completed_tasks += 1
self.stats_label.setText(f'已完成任务数:{self.completed_tasks}')
# 更新任务状态
for task in self.tasks:
if task['content'] == current_item.text() and not task['completed']:
task['completed'] = True
task['completed_at'] = datetime.now()
self.data_manager.update_task(task)
break
QMessageBox.information(self, '任务完成', '恭喜完成一个任务!')
def delete_task(self):
# 检查活动任务列表
current_item = self.active_task_list.currentItem()
if current_item:
row = self.active_task_list.row(current_item)
self.active_task_list.takeItem(row)
# 找到要删除的任务并添加删除时间
for task in self.tasks:
if task['content'] == current_item.text():
task['deleted_at'] = datetime.now()
self.data_manager.deleted_tasks.append(task)
self.tasks = [task for task in self.tasks if task['content'] != current_item.text()]
self.data_manager.save_data()
return
# 检查已完成任务列表
current_item = self.completed_task_list.currentItem()
if current_item:
row = self.completed_task_list.row(current_item)
self.completed_task_list.takeItem(row)
# 找到要删除的任务并添加删除时间
for task in self.tasks:
if task['content'] == current_item.text():
task['deleted_at'] = datetime.now()
self.data_manager.deleted_tasks.append(task)
self.tasks = [task for task in self.tasks if task['content'] != current_item.text()]
self.completed_tasks -= 1
self.stats_label.setText(f'已完成任务数:{self.completed_tasks}')
self.data_manager.save_data()
此外还要衷心建议下大家,只要我们有创造力,什么应用产品都能借助ai产生出来,加油各位,我们和trae一起进行迭代,变得更强