目录
- 一、功能简述
- 二、使用到的主要模块
- 三、核心模块代码分析
- 1、番茄钟模块
- 2、音乐控制函数
- 3、main中的按钮部分
- 四、整体代码
一、功能简述
番茄钟即番茄工作法,番茄工作法是简单易行的时间管理工具,使用番茄工作法即一个番茄时间共30分钟,25分钟工作,5分钟休息;
特点一:番茄时长有三档
因为这个工具本人也是考虑到每个人情况不一样,不一定25分钟就适合自己,所以将番茄钟时长设为30min/45min/60min三档,自由选择
特点二:番茄统计功能
特点三:休息期间会自动播放放松音乐,当然不喜欢也支持禁止播放放松音乐,休息时间结束后,音乐也会自动停止(意味着又要开始“搬砖”了)
特点四:番茄钟结束后,会有蜂鸣音提示,并且跳出弹框
二、使用到的主要模块
tkinter:用于界面设计
winsound:用于调用蜂鸣器提示音
pygame:用于音乐播放
time:时间相关的格式转换
三、核心模块代码分析
代码的主要思路是主函数中,利用tkinter模块布局界面、按钮、标签等组件,然后将番茄钟、休息两大核心功能封装到函数中,一旦点击对应的按钮,即开启一个新线程用于执行对应的功能,同时通过全局变量thread_flag来保持永远只有主线程和功能线程2个线程,避免多次点击,产生多个线程同时运行,造成番茄钟混乱;
1、番茄钟模块
## 创建番茄计时函数 | |
def tomato_clock(remain_time): | |
# 如果在休息时间未结束就开启番茄钟,则停止音乐 | |
pygame.mixer.music.pause() | |
# 用来提醒用户选择番茄钟时长,为选择的话,就跳出函数,结束线程 | |
if remain_time ==: | |
lb.configure(text='请先选择番茄钟时长') | |
return | |
print(remain_time) | |
# gmtime这里是将时间转化为计算机可处理的时间格式即time_t到tm类型的转换;不是重点,知道是格式转化即可 | |
# strptime()函数将字符串转换为datetime | |
begin_time = time.strftime('%M:%S', time.gmtime(remain_time)) | |
# 将时间内容打印到界面上 | |
lb.configure(text=begin_time) | |
lb.configure(text='总时间/剩余时间') | |
# 用于保证番茄钟线程或者休息线程只有一个能存在,这个也是本人觉得比较巧的一个点 | |
global thread_flag | |
if thread_flag: | |
thread_flag = False | |
else: | |
thread_flag = True | |
tmp_thread_flag = thread_flag | |
# 时间变化部分 | |
for i in range(remain_time): | |
# 如果收到休息线程导致的thread_flag标志位的变化,则退出线程 | |
if tmp_thread_flag != thread_flag: | |
return | |
remain_time -= | |
remain_time_str = time.strftime('/ %M:%S', time.gmtime(remain_time)) | |
# 将时钟实时更新到界面上 | |
lb.configure(text=remain_time_str) | |
root.update() | |
time.sleep() | |
#时间到了,开启蜂鸣提醒与提示框提醒 | |
if remain_time ==: | |
Beep(, 800) | |
tomato_count() | |
mymsg() | |
lb.configure(text=begin_time) | |
#使用者确认后,自动进入休息模式 | |
relax() |
2、音乐控制函数
# 音乐控制函数,用来控制是否允许休息时播放音乐,其实本质只是静音而已,狗头.jpg | |
# 在定义时,music_flag已经初始化True,代表运行休息时播放音乐 | |
def music_allow(): | |
global music_flag #声明全局变量 | |
# 如果已经是True(即不禁止音乐时),勾选了按钮,则music_flag 变为 False,禁止音乐 | |
if music_flag: | |
music_flag = False | |
pygame.mixer.music.set_volume(.0) | |
else: | |
# 代表取消勾选,不禁止音乐 | |
music_flag = True | |
pygame.mixer.music.set_volume(.5) |
3、main中的按钮部分
# 每当按钮点击后,就会产生一个线程,执行对应的功能,和主线程并行 | |
# 以防止单线程的话,进入番茄钟或者休息时,界面中的其他功能按钮失效 | |
# 开启番茄钟按钮,使用lambda构造匿名函数是因为command后接的函数如果有参数会失效,这点本人也不清楚,没去深究,直接匿名函数走去 | |
# 同时daemon=True,即将线程设为守护线程,解决主线程退出时,其他线程不正常退出的问题 | |
Button = tk.Button(root, text='开启一个番茄', bg='orange', fg='black', font='Verdana 13 bold',width=15, | |
height=, command=lambda: threading.Thread(target=tomato_clock, daemon=True,args=(var.get(),)).start()) | |
Button.place(x=70, y=150) | |
# 休息一下按钮 | |
Button = tk.Button(root, text='休息一下', bg='cornflowerblue', fg='black', font='Verdana 13 bold', | |
width=,height=1,command=lambda:threading.Thread(target=relax,daemon=True).start()) | |
Button.place(x=70, y=200) |
四、整体代码
# -*- coding:utf- -*- | |
import tkinter as tk | |
import tkinter.messagebox | |
from winsound import Beep | |
import threading | |
import sys | |
import pygame | |
import time | |
# 用于统计完成的番茄钟个数 | |
count = | |
# 线程切换标志 | |
thread_flag = True | |
# 音乐开关标志 | |
music_flag = True | |
# 调用Tk()创建主窗口 | |
root = tk.Tk() | |
# 给主窗口起一个名字,也就是窗口的名字 | |
root.title('Rio的番茄钟') | |
# 设置窗口大小:宽x高,注,此处不能为 "*",必须使用 "x" | |
root.geometry('x300') | |
root.configure(bg='Tomato') | |
# 创建完成计时后的弹窗 | |
def mymsg(): | |
try: | |
tk.messagebox.showinfo("提示", "恭喜完成一个番茄钟!!记得休息一下") | |
except Exception as e: | |
print(type(e), e) | |
sys.exit() | |
# 休息结束弹窗 | |
def mymsg(): | |
tk.messagebox.showinfo("提示", "休息完毕!") | |
# 创建番茄计时函数 | |
# strptime()函数将字符串转换为datetime | |
def tomato_clock(remain_time): | |
# 如果在休息时间未结束就开启番茄钟,则停止音乐 | |
pygame.mixer.music.pause() | |
# 避免未进行番茄钟时长选择 | |
if remain_time ==: | |
lb.configure(text='请先选择番茄钟时长') | |
return | |
print(remain_time) | |
begin_time = time.strftime('%M:%S', time.gmtime(remain_time)) | |
lb.configure(text=begin_time) | |
lb.configure(text='总时间/剩余时间') | |
global thread_flag | |
if thread_flag: | |
thread_flag = False | |
else: | |
thread_flag = True | |
tmp_thread_flag = thread_flag | |
for i in range(remain_time): | |
if tmp_thread_flag != thread_flag: | |
return | |
remain_time -= | |
remain_time_str = time.strftime('/ %M:%S', time.gmtime(remain_time)) | |
lb.configure(text=remain_time_str) | |
root.update() | |
time.sleep() | |
if remain_time ==: | |
Beep(, 800) | |
tomato_count() | |
mymsg() | |
lb.configure(text=begin_time) | |
relax() | |
# 创建番茄计数的函数 | |
def tomato_count(): | |
global count | |
count += | |
lb.configure(text=count) | |
# 创建休息时间函数 | |
def relax(): | |
remain_time = # 休息8分钟 | |
begin_time = time.strftime('%M:%S', time.gmtime(remain_time)) | |
lb.configure(text=begin_time) | |
lb.configure(text='总时间/剩余时间') | |
# 线程标志,用于结束旧线程 | |
global thread_flag | |
if thread_flag: | |
thread_flag = False | |
else: | |
thread_flag = True | |
tmp_thread_flag = thread_flag | |
pygame.mixer.music.play(-) | |
for i in range(remain_time): | |
if tmp_thread_flag != thread_flag: | |
return | |
remain_time -= | |
remain_time_str = time.strftime('/ %M:%S', time.gmtime(remain_time)) | |
lb.configure(text=remain_time_str) | |
root.update() | |
time.sleep() | |
if remain_time ==: | |
pygame.mixer.music.pause() | |
mymsg() | |
lb.configure(text=begin_time) | |
# 音乐控制函数 | |
def music_allow(): | |
global music_flag | |
# 如果已经是True(即不禁止音乐时),勾选了按钮,则music_flag 变为 False,禁止音乐 | |
if music_flag: | |
music_flag = False | |
pygame.mixer.music.set_volume(.0) | |
else: | |
music_flag = True | |
pygame.mixer.music.set_volume(.5) | |
if __name__ == "__main__": | |
#音乐初始化 | |
pygame.mixer.init() | |
# 异常抛出,防止没有放音乐文件 | |
try: | |
pygame.mixer.music.load('music.mp') | |
except Exception as e: | |
print(type(e), e) | |
tk.messagebox.showinfo("提示", "无文件music.mp或改文件路径不对") | |
sys.exit() | |
pygame.mixer.music.set_volume(.5) | |
# 创建变量 | |
var = tk.IntVar() | |
# 给变量赋初值为 | |
var.set() | |
# 番茄动态计时 | |
lb = tk.Label(root, text='0', bg='Tomato', fg='white', font='Verdana 16 bold', width=7, height=1) | |
lb.place(x=130, y=100) | |
# 番茄固定时间 | |
lb = tk.Label(root, text='0', bg='Tomato', fg='white', font='Verdana 16 bold', width=5, height=1) | |
lb.place(x=60, y=100) | |
# 剩余时间/总时间 | |
lb = tk.Label(root, text=' ', bg='Tomato', fg='white', font='Verdana 16 bold', width=14, height=2) | |
lb.place(x=50, y=44) | |
# 番茄个数显示 | |
lb = tk.Label(root, text='0', bg='Tomato', fg='white', font='Verdana 16 bold', width=7, height=1) | |
lb.place(x=90, y=20) | |
# 左上角的 番茄: | |
lb = tk.Label(root, text='已积累番茄:', bg='Tomato', fg='white', font='Verdana 16 bold', width=8, height=1) | |
lb.place(x=5, y=20) | |
# 按钮 | |
##创造一个frame来收纳按钮 | |
fr = tk.LabelFrame(root,bg='LightGreen',text='选择番茄钟时长', relief='groove', bd=1,) | |
fr.pack(side='right') | |
r = tk.Radiobutton(fr1, text='30min', variable=var, bg='LightGreen', value=1800) | |
r.pack() | |
r = tk.Radiobutton(fr1, text='45min', variable=var, bg='LightGreen', value=2700) | |
r.pack() | |
r = tk.Radiobutton(fr1, text='60min', variable=var, bg='LightGreen', value=3599) | |
r.pack() | |
Checkbutton = tk.Checkbutton(fr, text="是否禁止音乐", fg='black', bg='LightGreen', command=music_allow) | |
Checkbutton.pack() | |
# 开启一个番茄 | |
#利用多线程,避免进入番茄钟后,退出按钮失效 | |
Button = tk.Button(root, text='开启一个番茄', bg='orange', fg='black', font='Verdana 13 bold',width=15, | |
height=, command=lambda: threading.Thread(target=tomato_clock, daemon=True,args=(var.get(),)).start()) | |
Button.place(x=70, y=150) | |
# 休息一下 | |
Button = tk.Button(root, text='休息一下', bg='cornflowerblue', fg='black', font='Verdana 13 bold', | |
width=, height=1, command=lambda: threading.Thread(target=relax, daemon=True).start()) | |
Button.place(x=70, y=200) | |
# 添加按钮,以及按钮的文本,并通过command 参数设置关闭窗口的功能 | |
button = tk.Button(root, text="退出", fg='black', bg='YellowGreen', width=, command=root.quit) | |
# 将按钮放置在主窗口内 | |
button.place(x=, y=250) | |
#开启主循环,让窗口处于显示状态 | |
root.mainloop() |