【问题标题】:How can I schedule updates (f/e, to update a clock) in tkinter?如何在 tkinter 中安排更新(f/e,更新时钟)?
【发布时间】:2011-01-24 21:49:04
【问题描述】:

我正在用 Python 的 tkinter 库编写一个程序。

我的主要问题是我不知道如何创建 timerclock,例如 hh:mm:ss

我需要它来更新自己(这就是我不知道该怎么做);当我在循环中使用time.sleep() 时,整个 GUI 都会冻结。

【问题讨论】:

标签: python user-interface tkinter


【解决方案1】:

root.after(ms, func) 是您需要使用的方法。只需在主循环开始之前调用它一次,并在每次调用它时在绑定函数内重新调度它。这是一个例子:

from tkinter import *
import time
 

def update_clock():
    timer_label.config(text=time.strftime('%H:%M:%S',time.localtime()),
                  font='Times 25')  # change the text of the time_label according to the current time
    root.after(100, update_clock)  # reschedule update_clock function to update time_label every 100 ms

root = Tk()  # create the root window
timer_label = Label(root, justify='center')  # create the label for timer
timer_label.pack()  # show the timer_label using pack geometry manager
root.after(0, update_clock)  # schedule update_clock function first call
root.mainloop()  # start the root window mainloop

【讨论】:

【解决方案2】:
from tkinter import *

from tkinter import messagebox

root = Tk()

root.geometry("400x400")

root.resizable(0, 0)

root.title("Timer")

seconds = 21

def timer():

    global seconds
    if seconds > 0:
        seconds = seconds - 1
        mins = seconds // 60
        m = str(mins)

        if mins < 10:
            m = '0' + str(mins)
        se = seconds - (mins * 60)
        s = str(se)

        if se < 10:
            s = '0' + str(se)
        time.set(m + ':' + s)
        timer_display.config(textvariable=time)
        # call this function again in 1,000 milliseconds
        root.after(1000, timer)

    elif seconds == 0:
        messagebox.showinfo('Message', 'Time is completed')
        root.quit()


frames = Frame(root, width=500, height=500)

frames.pack()

time = StringVar()

timer_display = Label(root, font=('Trebuchet MS', 30, 'bold'))

timer_display.place(x=145, y=100)

timer()  # start the timer

root.mainloop()

【讨论】:

    【解决方案3】:

    我对这个问题有一个简单的答案。我创建了一个线程来更新时间。在线程中我运行一个while循环来获取时间并更新它。检查以下代码,不要忘记将其标记为正确答案。

    from tkinter import *
    from tkinter import *
    import _thread
    import time
    
    
    def update():
        while True:
          t=time.strftime('%I:%M:%S',time.localtime())
          time_label['text'] = t
    
    
    
    win = Tk()
    win.geometry('200x200')
    
    time_label = Label(win, text='0:0:0', font=('',15))
    time_label.pack()
    
    
    _thread.start_new_thread(update,())
    
    win.mainloop()
    

    【讨论】:

    • 这段代码有很多问题。 update() 函数中的 while 循环是一个繁忙的循环。从多个线程访问全局变量 time_label 并不好。
    • 但我觉得,这是最好的方法。因为这不会降低应用程序的性能。
    【解决方案4】:
    from tkinter import *
    import time
    tk=Tk()
    def clock():
        t=time.strftime('%I:%M:%S',time.localtime())
        if t!='':
            label1.config(text=t,font='times 25')
        tk.after(100,clock)
    label1=Label(tk,justify='center')
    label1.pack()
    clock()
    tk.mainloop()
    

    【讨论】:

    • 如果你能添加一些描述会很有帮助。只是复制/粘贴代码很少有用;-)
    • 这段代码给出了当地的准确时间。它也可以用作计时器。
    • 在我看来,使用“%H”而不是“%I”会更好,因为“%I”只显示从 0 到 12 的小时数,而不显示是否时间是上午或下午。或者另一种方式是同时使用“%I”和“%p”(“%p”表示上午/下午)。
    【解决方案5】:

    我刚刚使用 MVP 模式创建了一个简单的计时器(但它可能是 对于那个简单的项目来说太过分了)。它有退出、开始/暂停和停止按钮。时间以 HH:MM:SS 格式显示。时间计数是使用每秒运行几次的线程以及计时器启动时间与当前时间之间的差来实现的。

    Source code on github

    【讨论】:

      【解决方案6】:

      使用 frame.after() 而不是顶级应用程序的 Python3 时钟示例。还显示了使用 StringVar()

      更新标签
      #!/usr/bin/env python3
      
      # Display UTC.
      # started with https://docs.python.org/3.4/library/tkinter.html#module-tkinter
      
      import tkinter as tk
      import time
      
      def current_iso8601():
          """Get current date and time in ISO8601"""
          # https://en.wikipedia.org/wiki/ISO_8601
          # https://xkcd.com/1179/
          return time.strftime("%Y%m%dT%H%M%SZ", time.gmtime())
      
      class Application(tk.Frame):
          def __init__(self, master=None):
              tk.Frame.__init__(self, master)
              self.pack()
              self.createWidgets()
      
          def createWidgets(self):
              self.now = tk.StringVar()
              self.time = tk.Label(self, font=('Helvetica', 24))
              self.time.pack(side="top")
              self.time["textvariable"] = self.now
      
              self.QUIT = tk.Button(self, text="QUIT", fg="red",
                                                  command=root.destroy)
              self.QUIT.pack(side="bottom")
      
              # initial time display
              self.onUpdate()
      
          def onUpdate(self):
              # update displayed time
              self.now.set(current_iso8601())
              # schedule timer to call myself after 1 second
              self.after(1000, self.onUpdate)
      
      root = tk.Tk()
      app = Application(master=root)
      root.mainloop()
      

      【讨论】:

      • 这是一个很好的答案,有一件重要的事情 - 显示的时间实际上是系统时间,而不是一些累积的错误时间(如果你等待“大约 1000 毫秒”60 次,你会得到“大约一分钟”而不是 60 秒,并且误差随着时间的推移而增长)。但是-您的时钟可以跳过显示的秒数-您可以累积亚秒级错误,然后例如向前跳过 2 秒。我建议:self.after(1000 - int(1000 * (time.time() - int(time.time()))) or 1000, self.onUpdate)。在此表达式之前将time.time() 保存到变量中可能会更好。
      • 我渴望能够将 xkcd 嵌入到我的 cmets 中 :)
      • 使用 frame.after() 代替 root.after() 有什么好处?
      【解决方案7】:

      Tkinter 根窗口有一个名为after 的方法,可用于安排在给定时间段后调用的函数。如果该函数本身调用after,则您设置了一个自动重复事件。

      这是一个工作示例:

      # for python 3.x use 'tkinter' rather than 'Tkinter'
      import Tkinter as tk
      import time
      
      class App():
          def __init__(self):
              self.root = tk.Tk()
              self.label = tk.Label(text="")
              self.label.pack()
              self.update_clock()
              self.root.mainloop()
      
          def update_clock(self):
              now = time.strftime("%H:%M:%S")
              self.label.configure(text=now)
              self.root.after(1000, self.update_clock)
      
      app=App()
      

      请记住,after 并不能保证该函数会准时运行。它仅安排在给定时间后运行的作业。如果应用程序很忙,由于 Tkinter 是单线程的,因此在调用它之前可能会有延迟。延迟通常以微秒为单位。

      【讨论】:

      • 对自身的递归调用不会导致“达到python对象的最大递归”错误吗?
      • @SatwikPasani:不,因为它不是递归调用。它只是将作业放入队列中。
      • 如何延迟只运行一次func?
      • @user924: self.root.after(delay, func).
      猜你喜欢
      • 1970-01-01
      • 2010-09-12
      • 1970-01-01
      • 2013-11-01
      • 2017-02-21
      • 2021-10-19
      • 1970-01-01
      • 1970-01-01
      • 2020-10-10
      相关资源
      最近更新 更多