Python tkinter:倒数计时器不准确

Python tkinter: Countdown Timer not accurate

我在 python 中使用 Tkinter 制作了一个倒数计时器,但我唯一的问题是计时器中的一秒比实际秒长一点。

我使用 after() 函数每毫秒从时钟中删除一毫秒(0.001 秒)。

我不知道它是不是这样做的,因为时钟的代码需要一些额外的时间来执行,如果是这样的话,我怎么能用更少的时间制作一个完全相同的 UI 的时钟执行?

这是一个Video showing the problem

程序:

from tkinter import *

class root(Tk):
    def __init__(self):
        super(root, self).__init__()

        self.title("Timer")
        
        self.buttonplay = Button(self, text = "Play", fg= 'green', command = self.play)
        self.buttonplay.pack()

        self.buttonpause = Button(self, text = "Pause", fg = "red", command=self.pause)
        self.buttonpause.pack()

        self.createTimers()

    def play(self):
        self.timeit=True
        self.timer1.configure(bg='#1C953D')
        self.doTimer()

    def pause(self):
        self.timeit=False
        self.timer1.configure(bg='#454545')
        
    def reset(self):
        self.timer1.destroy()
        self.createTimers()

    def createTimers(self):
        self.minute = 0
        self.second = 5
        self.ms = 0
        self.total = self.second + self.minute *60 + self.ms*0.001
        self.time1 = StringVar()
        self.time1.set(str(self.minute).rjust(2, '0') + ':' + str(self.second).rjust(2, '0') +'.'+ str(self.ms).rjust(3, '0'))
        self.timer1 = Label(self, textvariable=self.time1, bg='#454545', fg='white', font ="Gadugi 40 bold")
        self.timer1.pack()
        self.timer1.configure(bg='#454545')

    def doTimer(self):
        self.time = self.second + self.minute *60 + self.ms*0.001
        if self.time !=0: #Checks if the timer ended
            if self.timeit:
                self.timer1.configure(bg='#1C953D')
                
                self.ms = self.ms -1
                if self.ms <0:
                    self.second = self.second -1
                    self.ms = 999

                if self.second == -1:
                    self.minute = self.minute -1
                    self.second = 59

                self.time1.set(str(self.minute).rjust(2, '0') + ':' + str(self.second).rjust(2, '0') +'.'+ str(self.ms).rjust(3, '0'))
                
                if self.timeit:
                    self.after(1, self.doTimer)
        else:
            self.ended = 1
            self.timer1.configure(bg='#FF0000')
            self.after(3000, self.reset)


root = root()
root.mainloop()

I don't know if it's doing that because the code of the clock takes some extra time to execute

在这种情况下你是对的,计时器的速度取决于代码的运行时间。所以让这个脚本需要更多的计算机资源也会减慢计时器,反之亦然。

"Don't reinvent the wheel." - Programming proverb.

使用 time 模块在您的计时器中获得更准确的时间。更具体地说 time.time() 并对其进行格式化以使其可读。

编辑:

这里有一个例子可以说明你的问题。

import time

time_zone = 0 # UTC
while True:
    current_time = time.time()

    ms = (current_time * 1000) % 1000
    s = (current_time % 60)
    m = (current_time % (60*60))//60
    h = (current_time % (60*60*24))//(60*60)

    print(f"h:{int(h+time_zone)} - m:{int(m)} - s:{int(s)} - ms:{int(ms)}")
    time.sleep(1)

理论上这个脚本应该精确地每秒打印一次时间。然而,由于代码运行时间,增加了几毫秒。 删除 time.sleep(1) 应该会让你非常接近实时时钟。

所以如果你想要接近计时器或秒表的东西,你会得到当前纪元时间的时间戳(time.time())更新你的计时器大约每秒 x-amount 滴答声以获得计算 ms 的效果,当再次按下按钮时你会得到一个新的时间戳,然后你将它与第一个时间戳进行比较并减去以获得每次按钮按下之间的确切时间。


我制作了一个模板供您修改:

import tkinter as tk
import time

time_zone = 0  # UTC

# Convert epoch time to readable format
def readable_epoch(time_epoch):
    ms = (time_epoch * 1000) % 1000
    s = (time_epoch % 60)
    m = (time_epoch % (60*60))//60
    h = (time_epoch % (60*60*24))//(60*60)
    return (h, m, s, ms)


class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.time1 = 0
        self.time2 = 0

        self.geometry("300x200")
        self.button1 = tk.Button(
            self, text="Click me first", command=self.button1, bg="teal")
        self.button1.pack(ipadx=10, ipady=10, expand=True, side=tk.LEFT)
        self.button2 = tk.Button(
            self, text="Click me second", command=self.button2, bg="pink")
        self.button2.pack(ipadx=10, ipady=10, expand=True, side=tk.LEFT)

    # Get current time
    def button1(self):
        self.current_time = time.time()
        h, m, s, ms = readable_epoch(self.current_time)
        print(f"h:{int(h+time_zone)}, m:{int(m)}, s:{int(s)}, ms:{int(ms)}")

    # Elapsed time since button 1 was pressed:
    def button2(self):
        new_time = time.time()
        new_time = new_time - self.current_time
        h, m, s, ms = readable_epoch(new_time)
        print(f"h:{int(h+time_zone)}, m:{int(m)}, s:{int(s)}, ms:{int(ms)}")


if __name__ == "__main__":
    app = App()
    app.mainloop()