Как я могу запланировать обновления (f / e, чтобы обновить часы) в tkinter?

Я пишу программу с библиотекой Python tkinter.

Моя основная проблема в том, что я не знаю, как создать таймер или часы, например hh:mm:ss.

Мне нужно, чтобы он обновлялся (это то, что я не умею делать); когда я использую time.sleep() в цикле, весь графический интерфейс зависает.


person Diego Castro    schedule 08.03.2010    source источник


Ответы (6)


В корневых окнах 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 является однопоточным. Задержка обычно измеряется в микросекундах.

person Bryan Oakley    schedule 08.03.2010
comment
Разве рекурсивные вызовы сами по себе не вызовут максимальную рекурсию для достижения объекта Python? - person stochastic13; 29.12.2017
comment
@SatwikPasani: нет, потому что это не рекурсивный вызов. Он просто ставит задание в очередь. - person Bryan Oakley; 29.12.2017
comment
как запустить func только один раз с задержкой? - person user924; 17.04.2018
comment
@ user924: self.root.after(delay, func). - person Bryan Oakley; 17.04.2018

Пример часов Python3 с использованием frame.after (), а не приложения верхнего уровня. Также показано обновление метки с помощью 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()
person David Poole    schedule 01.12.2015
comment
Это хороший ответ с одной важной вещью: отображаемое время на самом деле является системным временем, а не некоторым накопленным временем ошибки (если вы подождете около 1000 мс 60 раз, вы получите около минуты, а не 60 сенсекунд, и ошибка растет с время). Однако - ваши часы могут пропускать секунды на дисплее - вы можете накапливать ошибки менее секунды, а затем, например, перейти на 2 секунды вперед. Я бы посоветовал: self.after(1000 - int(1000 * (time.time() - int(time.time()))) or 1000, self.onUpdate). Возможно, лучше сохранить time.time() в переменную перед этим выражением. - person Tomasz Gandor; 12.06.2017
comment
Я стремлюсь быть достаточно крутым, чтобы вставить xkcd в свои комментарии :) - person bitsmack; 21.06.2017
comment
В чем преимущество использования frame.after () вместо root.after ()? - person Kai Wang; 04.09.2019

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
person Demian Wolf    schedule 19.02.2020
comment
... просто примечание, after - это универсальный метод виджета, чтобы его можно было вызывать и на timer_label. - person Wolf; 04.05.2021

Я только что создал простой таймер, используя шаблон MVP (однако это может оказаться излишним для этого простого проекта). У него есть выход, пуск / пауза и кнопка остановки. Время отображается в формате ЧЧ: ММ: СС. Подсчет времени реализован с использованием потока, который выполняется несколько раз в секунду, и разницы между временем запуска таймера и текущим временем.

Исходный код на github

person Community    schedule 03.06.2017

У меня есть простой ответ на эту проблему. Я создал ветку, чтобы обновить время. В потоке я запускаю цикл 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()
person Hardeep Singh    schedule 28.09.2019
comment
В этом коде множество проблем. Цикл while в функции update () - это цикл занятости. Доступ к глобальной переменной time_label из нескольких потоков не очень удобен. - person Kenji Noguchi; 01.10.2019
comment
но я чувствую, что это лучший способ сделать это. потому что это не снижает производительность приложения. - person Hardeep Singh; 12.10.2019

person    schedule
comment
Было бы полезно, если бы вы могли добавить некоторое описание. Просто скопировать / вставить код редко бывает полезно ;-) - person Martin Tournoij; 26.09.2017
comment
этот код дает точное время местности. он также служит таймером. - person Ravikiran D; 26.09.2017
comment
Мне кажется, было бы лучше использовать% H вместо% I, потому что% I показывает только часы с 0 до 12 и не показывает, какое время - AM или PM. Или другой способ - использовать как% I, так и% p (% p указывает AM / PM). - person Demian Wolf; 19.02.2020