文章目录
- 需求和框架
- 布局和主流程
- 计时函数
需求和框架
本文试图实现一个简单的秒表,内容如下
这个软件非常简单,其UI元素只有一个文字标签外加三个按钮,这三个按钮的功能如下
- 点击Start按钮,开始进行计时,同时Start变灰,无法点击
- 点击Stop按钮,停止计时,同时Stop变灰,无法点击
- 点击Reset按钮,则计时归0,若当前处于停止状态,则Reset变灰
则整个软件所需导入的模块以及整体框架如下
import tkinter as Tkinter
from datetime import datetime
class Clock():
def __init__(self) -> None:
root = Tkinter.Tk()
root.title("Stopwatch")
root.minsize(width=250, height=70)
self.counter = 0
self.running = False
self.initWidgets(root)
root.mainloop()
def initWidgets(self, root): pass
def count(self): pass
def Start(self): pass
def Stop(self): pass
def Reset(self): pass
其中,counter为计数值,即点击Start之后经过的秒数;running为当前状态,如果正在计时,则为True,否则为False。
initWidgets是布局函数,Start, Stop, Reset分别是三个按钮的回调函数,count则用于计时。
下面的代码均为Clock中的方法,在使用时注意缩进。
布局和主流程
整个程序分为两个部分,上面的Label是第一部分,下面三个按钮共同组成第二部分,其布局代码如下
def initWidgets(self, root):
self.label = Tkinter.Label(root,
text='Ready!', font='Verdana 30 bold')
self.label.pack()
f = Tkinter.Frame(root)
self.start = Tkinter.Button(f, text='Start', width=6,
command=self.Start)
self.stop = Tkinter.Button(f, text='Stop', width=6,
state='disabled', command=self.Stop)
self.reset = Tkinter.Button(f, text='Reset', width=6,
state='disabled', command=self.Reset)
f.pack(anchor='center', pady=5)
self.start.pack(side='left')
self.stop.pack(side='left')
self.reset.pack(side='left')
三个按钮的三个回调函数如下,通过state来调整当前按钮的可用状态,若为disabled即表示不可用。当点击Start时,会调用计时函数count,这个函数在后面马上实现。
当点击Reset后,除了需要将label中的值初始化之外,还需要判断当前是否处于计时状态,如果已经停止计时,且已经Reset,则Reset按钮变灰。
def Start(self):
self.running = True
self.count()
self.start['state'] = 'disabled'
self.stop['state'] = 'normal'
self.reset['state'] = 'normal'
def Stop(self):
self.start['state'] = 'normal'
self.stop['state'] = 'disabled'
self.reset['state'] = 'normal'
self.running = False
def Reset(self):
self.counter = 0
if not self.running:
self.reset['state'] = 'disabled'
self.label['text'] = '00:00:00'
计时函数
一般来说,计时肯定要用到time.sleep,但在UI中,这种计时容易导致阻塞,进而窗口卡死,而要采用多线程,显然会增加程序的复杂程度。为此,可动用tkinter组件的after方法,以label.after(a,b)为例,表示经过a毫秒后,执行b。
下面即为采用after方法的计时函数。当程序不处于计时状态时,直接跳出。否则开始计时,当计数值为0时,标签显示Ready,否则显示的则是秒数。这里比较巧妙的方法是,等待1000毫秒,再重新调用自己,从而实现计时功能。
def count(self):
if not self.running:
return
if self.counter == 0:
self.label['text'] = 'Ready!'
else:
tt = datetime.utcfromtimestamp(self.counter)
self.label['text'] = tt.strftime('%H:%M:%S')
self.label.after(1000, self.count)
self.counter += 1