一、效果展示
这是一个基于Tkinter的桌面倒计时,双击第一行弹出修改窗口,可以修改标题、目标时间、超时标题。该工具会自动倒计时,若超时,会自动显示超时标题,并显示超时多长时间。可自由拖动窗口,小巧,很适合期待下班的打工人使用!想拿来就用的朋友可以直接下载打包文件,想进一步了解的同学,请往下看~~
二、代码详情
1、初始化
从config.txt中读取标题、目标时间、超时标题。将目标时间转化为时间戳,方便计算。
利用Tk的相关方法设置窗口宽高、标题、位置、图标,绘制窗口等。图标可以更替,要求ico格式
-----------config.txt-----------
下班 还有
2023-11-17 11:53:00
加班 已经
def __init__(self):
with open('config.txt','r',encoding='utf8') as f:
self.Target = f.readline().strip()
self.t = f.readline().strip()
self.overTarget = f.readline().strip()
s_t = time.strptime(self.t, "%Y-%m-%d %H:%M:%S") # 返回元祖
self.endtime = int(time.mktime(s_t))
self.window = tk.Tk()
self.x, self.y = 0, 0
self.window_size = '150x150'
# 窗口标题
self.window.title('桌面倒计时')
# 窗口的宽与高,单位是像素。以及位置坐标
self.window.geometry(f"{self.window_size}+1000+100")
# 窗口左上角图标
self.window.iconbitmap('favicon.ico')
self.set_Line()
self.set_text()
self.set_layout()
self.set_others()
2、set_Line函数
绘制两条白色分割线
self.CanvasBg.create_line(0, 73, 405, 73, fill=‘white’)
是指画一条从(0,73)到(405,73)的白色线段
def set_Line(self):
self.CanvasBg = tk.Canvas(self.window, bg='#f9aea8', width=150, height=150)
self.CanvasBg.place(x=0, y=0)
self.CanvasBg.create_line(0, 50, 150, 50, fill='white')
self.CanvasBg.create_line(0, 100, 150, 100, fill='white')
3、set_text函数
创建Label控件,设置字体、颜色、大小等。需要根据是否超时,判断显示哪个标题
def set_text(self):
s = self.endtime - int(time.time())
if s>0 :
t=self.Target
else :
t=self.overTarget
self.LabelTitle = tk.Label(self.window, text=f'距离 {t} ', bg='#f9aea8',fg='white', font=('方正青铜体简体', 10))
self.LabelDays = tk.Label(self.window, text='00 天', bg='#f9aea8', fg='white', font=('方正青铜体简体', 10))
self.LabelHours = tk.Label(self.window, text='00 时', bg='#f9aea8', fg='white', font=('方正青铜体简体', 10))
self.LabelMins = tk.Label(self.window, text='00 分', bg='#f9aea8', fg='white', font=('方正青铜体简体', 10))
self.LabelS = tk.Label(self.window, text='共计 000,000 秒', bg='#f9aea8', fg='white', font=('方正青铜体简体', 10))
4、set_layout函数
使用pack控件布局
def set_layout(self):
tk.Label(self.window, bg='#f9aea8').pack(expand=True)# 仅用来占地方,避免其它控件位置过于靠上
self.LabelTitle.pack(expand=True)
self.LabelDays.pack(expand=True)
self.LabelHours.pack(expand=True)
self.LabelMins.pack(expand=True)
self.LabelS.pack(expand=True)
tk.Label(self.window, bg='#f9aea8').pack()# 仅用来占地方,避免其它控件位置过于靠下
5、set_others函数
设置窗口固定宽高,以及绑定事件
# 给窗口绑定事件
移动事件:可拖动窗口移动;单击事件:获取窗体坐标并保存。
# 给Label控件绑定事件
双击事件:弹出编辑窗口
def set_others(self):
# 第一个参数是宽,第二个参数是高,如果要固定窗口宽与高,可以使用resizeable(0,0)。
self.window.resizable(False, False)
# 窗体控件——显示控件
# 在Windows平台下,这个函数的作用,视觉上窗体整个边框消失(没有最小化最大化关闭这几个按钮,也无法拖动这个窗体),
# 程序的窗体在Windows系统任务栏上也消失(看不到有这么一个程序在运行),但是Alt+F4关闭窗体的功能还存在。
# self.window.overrideredirect(True)
# 窗口移动事件
self.window.bind("<B1-Motion>", self.move)
# 单击事件
self.window.bind("<Button-1>", self.get_point)
# 双击事件
self.LabelTitle.bind("<Double-Button-1>", self.update_window)
Thread(target=get_time,args=(self,)).start()
6、update_window函数
绘制编辑窗口,自动显示当前标题、时间、超时标题。使用grid布局
row是第几行,column是第几列
padx 是单元格左右间距
pady 是单元格上下间距
ipadx是单元格内部元素与单元格左右的间距
ipady是单元格内部元素与单元格上下的间距
def update_window(self,event):
self.UpdateWindow = tk.Toplevel()
self.UpdateWindow['bg'] = '#f6ccb4'
self.UpdateWindow.title('修 改')
self.UpdateWindow.geometry('240x180+600+100')
self.UpdateWindow.iconbitmap('favicon.ico')
self.EntryTarget = tk.Entry(self.UpdateWindow)#目标输入框
self.EntryEndtime = tk.Entry(self.UpdateWindow)#目标时间输入框
self.EntryOverTarget = tk.Entry(self.UpdateWindow)#目标超时输入框
# 写入当前的目标和时间
self.EntryTarget.insert(0,self.Target)
self.EntryEndtime.insert(0,self.t)
self.EntryOverTarget.insert(0,self.overTarget)
# 控件布局
tk.Label(self.UpdateWindow,bg='#f6ccb4', text='目 标').grid(row=1, column=1,pady=10,padx=5)
self.EntryTarget.grid(row=1,column=2)
tk.Label(self.UpdateWindow,bg='#f6ccb4', text='时间').grid(row=2,column=1,pady=10,padx=5)
self.EntryEndtime.grid(row=2,column=2)
tk.Label(self.UpdateWindow,bg='#f6ccb4', text='超时标题').grid(row=3,column=1,pady=10,padx=5)
self.EntryOverTarget.grid(row=3,column=2)
tk.Button(self.UpdateWindow, bg='#f9aea8', fg='white',width='19', text='修改',command=self.update).grid(row=4,column=2)
7、update函数
将修改的内容更新到父窗口,并将时间戳转为日期格式后,更新config.txt
def update(self):# 用于将修改更新到父窗口
if self.EntryEndtime.get():
self.Target = self.EntryTarget.get()
self.overTarget = self.EntryOverTarget.get()
self.t = self.EntryEndtime.get()
s_t = time.strptime(self.t, "%Y-%m-%d %H:%M:%S")
self.endtime = int(time.mktime(s_t))
self.UpdateWindow.destroy()
with open('config.txt', 'w', encoding='utf8') as f:
f.write(f'{self.Target}\n{self.t}\n{self.overTarget}')
8、事件函数
重新计算并设置窗口的位置
event.x :鼠标当前位置横坐标,相对于组件左上角
event.y :鼠标当前位置纵坐标,相对于组件左上角
self.x :窗口之前位置横坐标,相对于组件左上角
self.y :窗口之前位置纵坐标,相对于组件左上角
winfo_x() :获取当前窗口左上角相对于显示器左上角的横坐标
winfo_y() :获取当前窗口左上角相对于显示器左上角的纵坐标
def move(self, event):
"""窗口移动事件"""
new_x = (event.x - self.x) + self.window.winfo_x()
new_y = (event.y - self.y) + self.window.winfo_y()
s = f"{self.window_size}+{new_x}+{new_y}"
self.window.geometry(s)
def get_point(self, event):
"""获取当前窗口位置并保存"""
self.x, self.y = event.x, event.y
9、get_time函数
主要根据时间截之差,计算相差的天数、小时数、分钟数、秒数。在计算之前,判断是否超时,确定标题和时间
def get_time(handle):
while True:
s = handle.endtime - int(time.time())
if s<0 :
# 若已超时
s = int(time.time()) - handle.endtime
t = handle.overTarget
else :
t = handle.Target
days = int(s / 86400)
hours = int((s % 86400) / 3600)
mins = int((s % 3600) / 60)
handle.LabelTitle.config(text=f'距离 {t} ')
handle.LabelDays.config(text=f'{days} 天')
handle.LabelHours.config(text=f'{hours} 时')
handle.LabelMins.config(text=f'{mins} 分')
handle.LabelS.config(text=f'共计 {format(s,",")} 秒')
time.sleep(1)
三、完整代码
# 打包:在当前py文件的目录下打开cmd,输入命令
pyinstaller -F -w -i favicon.ico de_desktop.py
### -i favicon.ico 指定应用图标 de_desktop.py 需要打包的文件名
# 命令行运行代码: python de_desktop.py
import tkinter as tk
from threading import Thread
import time
def get_time(handle):
while True:
s = handle.endtime - int(time.time())
if s<0 :
# 若已超时
s = int(time.time()) - handle.endtime
t = handle.overTarget
else :
t = handle.Target
days = int(s / 86400)
hours = int((s % 86400) / 3600)
mins = int((s % 3600) / 60)
handle.LabelTitle.config(text=f'距离 {t} ')
handle.LabelDays.config(text=f'{days} 天')
handle.LabelHours.config(text=f'{hours} 时')
handle.LabelMins.config(text=f'{mins} 分')
handle.LabelS.config(text=f'共计 {format(s,",")} 秒')
time.sleep(1)
class APP():
def __init__(self):
with open('config.txt','r',encoding='utf8') as f:
self.Target = f.readline().strip()
self.t = f.readline().strip()
self.overTarget = f.readline().strip()
s_t = time.strptime(self.t, "%Y-%m-%d %H:%M:%S") # 返回元祖
self.endtime = int(time.mktime(s_t))
self.window = tk.Tk()
self.x, self.y = 0, 0
self.window_size = '150x150'
# 窗口标题
self.window.title('桌面倒计时')
# 窗口的宽与高,单位是像素。以及位置坐标
self.window.geometry(f"{self.window_size}+1000+100")
# 窗口左上角图标
self.window.iconbitmap('favicon.ico')
self.set_Line()
self.set_text()
self.set_layout()
self.set_others()
def set_Line(self):
self.CanvasBg = tk.Canvas(self.window, bg='#f9aea8', width=150, height=150)
self.CanvasBg.place(x=0, y=0)
self.CanvasBg.create_line(0, 50, 150, 50, fill='white')
self.CanvasBg.create_line(0, 100, 150, 100, fill='white')
def set_text(self):
s = self.endtime - int(time.time())
if s>0 :
t=self.Target
else :
t=self.overTarget
self.LabelTitle = tk.Label(self.window, text=f'距离 {t} ', bg='#f9aea8',fg='white', font=('方正青铜体简体', 10))
self.LabelDays = tk.Label(self.window, text='00 天', bg='#f9aea8', fg='white', font=('方正青铜体简体', 10))
self.LabelHours = tk.Label(self.window, text='00 时', bg='#f9aea8', fg='white', font=('方正青铜体简体', 10))
self.LabelMins = tk.Label(self.window, text='00 分', bg='#f9aea8', fg='white', font=('方正青铜体简体', 10))
self.LabelS = tk.Label(self.window, text='共计 000,000 秒', bg='#f9aea8', fg='white', font=('方正青铜体简体', 10))
def set_layout(self):
tk.Label(self.window, bg='#f9aea8').pack(expand=True)# 仅用来占地方,避免其它控件位置过于靠上
self.LabelTitle.pack(expand=True)
self.LabelDays.pack(expand=True)
self.LabelHours.pack(expand=True)
self.LabelMins.pack(expand=True)
self.LabelS.pack(expand=True)
tk.Label(self.window, bg='#f9aea8').pack()# 仅用来占地方,避免其它控件位置过于靠下
def set_others(self):
# 第一个参数是宽,第二个参数是高,如果要固定窗口宽与高,可以使用resizeable(0,0)。
self.window.resizable(False, False)
# 窗体控件——显示控件
# 在Windows平台下,这个函数的作用,视觉上窗体整个边框消失(没有最小化最大化关闭这几个按钮,也无法拖动这个窗体),
# 程序的窗体在Windows系统任务栏上也消失(看不到有这么一个程序在运行),但是Alt+F4关闭窗体的功能还存在。
# self.window.overrideredirect(True)
# 窗口移动事件
self.window.bind("<B1-Motion>", self.move)
# 单击事件
self.window.bind("<Button-1>", self.get_point)
# 双击事件
self.LabelTitle.bind("<Double-Button-1>", self.update_window)
Thread(target=get_time,args=(self,)).start()
def update_window(self,event):
self.UpdateWindow = tk.Toplevel()
self.UpdateWindow['bg'] = '#f6ccb4'
self.UpdateWindow.title('修 改')
self.UpdateWindow.geometry('240x180+600+100')
self.UpdateWindow.iconbitmap('favicon.ico')
self.EntryTarget = tk.Entry(self.UpdateWindow)#目标输入框
self.EntryEndtime = tk.Entry(self.UpdateWindow)#目标时间输入框
self.EntryOverTarget = tk.Entry(self.UpdateWindow)#目标超时输入框
# 写入当前的目标和时间
self.EntryTarget.insert(0,self.Target)
self.EntryEndtime.insert(0,self.t)
self.EntryOverTarget.insert(0,self.overTarget)
# 控件布局
tk.Label(self.UpdateWindow,bg='#f6ccb4', text='目 标').grid(row=1, column=1,pady=10,padx=5)
self.EntryTarget.grid(row=1,column=2)
tk.Label(self.UpdateWindow,bg='#f6ccb4', text='时间').grid(row=2,column=1,pady=10,padx=5)
self.EntryEndtime.grid(row=2,column=2)
tk.Label(self.UpdateWindow,bg='#f6ccb4', text='超时标题').grid(row=3,column=1,pady=10,padx=5)
self.EntryOverTarget.grid(row=3,column=2)
tk.Button(self.UpdateWindow, bg='#f9aea8', fg='white',width='19', text='修改',command=self.update).grid(row=4,column=2)
def update(self):# 用于将修改更新到父窗口
if self.EntryEndtime.get():
self.Target = self.EntryTarget.get()
self.overTarget = self.EntryOverTarget.get()
self.t = self.EntryEndtime.get()
s_t = time.strptime(self.t, "%Y-%m-%d %H:%M:%S")
self.endtime = int(time.mktime(s_t))
self.UpdateWindow.destroy()
with open('config.txt', 'w', encoding='utf8') as f:
f.write(f'{self.Target}\n{self.t}\n{self.overTarget}')
def move(self, event):
"""窗口移动事件"""
new_x = (event.x - self.x) + self.window.winfo_x()
new_y = (event.y - self.y) + self.window.winfo_y()
s = f"{self.window_size}+{new_x}+{new_y}"
self.window.geometry(s)
def get_point(self, event):
"""获取当前窗口位置并保存"""
self.x, self.y = event.x, event.y
if __name__ == '__main__':
myapp = APP()
# mainloop( )方法可以让程序继续执行,同时进入等待与处理窗口事件,若是单击窗口右上方的“关闭”按钮,此程序才会结束。
myapp.window.mainloop()