tkinter: как мне вставить задержку между изменениями виджета в одном и том же обработчике?

#tkinter #sleep #mainloop

#tkinter #сон #mainloop

Вопрос:

Я задавал аналогичный вопрос около двух лет назад, когда пытался эмулировать светодиоды на Tkinter canvas. Тогда решением было использовать метод canvas after() вместо функции sleep() для введения задержки между обновлениями виджета.

С тех пор я обнаружил модуль tk_tools, который имеет встроенную функцию для создания светодиодов, отлично! Но теперь у меня та же проблема, что и раньше, а именно: как добиться задержки в 1 секунду между включением (переключением на зеленый) каждого светодиода?

Что на самом деле происходит при выполнении приведенного ниже кода, так это то, что светодиоды отображаются в выключенном состоянии (серым цветом), затем, когда я нажимаю кнопку «Пуск», возникает 4-секундная задержка, после которой все светодиоды включаются одновременно.

Спасибо. Johnnym

 # LED array simulation

from tkinter import *
import tk_tools as tkt
from time import *

# turn on LEDs (change to green) with a 1-second delay between each
def turn_on():
    for led in range(4):
        led_array[led].to_green()
        sleep(1)

# list to hold a 4-LED array
led_array = []

# GUI
root = Tk()

# create 4 LED widgets, store them in led_array[], display them
for i in range(4):
    led_array.append(tkt.Led(root, size=30))
    led_array[i].grid(row=0, column=i)

# create button to initiate LED turn-on sequence
start = Button(root, text='Start', padx=20, command=turn_on)
start.grid(row=1, columnspan=4)



root.mainloop()
  

Ответ №1:

Лучше избегать сна, который останавливает приложение и использование after . Либо соедините вызовы с обратным вызовом after, чтобы каждый вызывал следующий, либо вызывайте их в цикле для каждого светодиода, но с задержкой 1, 2, 3, затем 4 секунды.

Цепная версия:

 # LED array simulation

from tkinter import *
import tk_tools as tkt

# list to hold a 4-LED array
led_array = []

# GUI
root = Tk()

# create 4 LED widgets, store them in led_array[], display them
for i in range(4):
    led_array.append(tkt.Led(root, size=30))
    led_array[i].grid(row=0, column=i)

# Both answers are common to here.

def on_after( led_list ):
    led_list[0].to_green() # Set first item in the list.
    if len( led_list ) > 1:
        # Call on_after again with a shortened list.
        root.after( 1000, on_after, led_list[1:] )
    else:
        # Enable the start button
        start.config( state = NORMAL )

# turn on LEDs (change to green) with a 1-second delay between each
def turn_on():
    start.config( state = DISABLED ) # Disable the button
    root.after( 1000, on_after, led_array)

# create button to initiate LED turn-on sequence
start = Button(root, text='Start', padx=20, command=turn_on)
start.grid(row=1, columnspan=4)

root.mainloop()
  

Зацикленная версия:

 # Both answers are common to here.
def on_after( led ):
    led.to_green()

def enable():
    start.config( state = NORMAL )

# turn on LEDs (change to green) with a 1-second delay between each
def turn_on():
    start.config( state = DISABLED ) # Disable the button
    for ix, led in enumerate( led_array ):
        # Call on_after 4 times with 1 to 4 second delays
        root.after( 1000 * (1 ix), on_after, led )
    root.after( 1000*(ix 1), enable )

# create button to initiate LED turn-on sequence
start = Button(root, text='Start', padx=20, command=turn_on)
start.grid(row=1, columnspan=4)

root.mainloop()
  

Я бы, вероятно, использовал цепную версию, хотя в этом контексте зацикленную версию, вероятно, легче понять.

Ответ №2:

Я предполагаю, что все светодиоды светятся одновременно, когда функции обратного вызова завершают свое выполнение. Итак, я решил создать поток и использовать after() для остановки времени ( sleep() останавливает основной поток, что составляет 4 секунды)

 # LED array simulation

from tkinter import *
import tk_tools as tkt
from time import *
import threading
import sys

class Testing(Tk):
    def __init__(self):
        super().__init__()
        self.led_array = []
        for i in range(4):
            self.led_array.append(tkt.Led(self, size=30))
            self.led_array[i].grid(row=0, column=i 1)
        self.start = Button(self, text='Start', padx=20, command=self.zawardu)
        self.start.grid(row=1, columnspan=4)
    def zawardu(self):
        a=threading.Thread(target=self.turn_on)
        a.start()
    def turn_on(self):
        self.start.config(state=DISABLED) # to stop creating another thread unnec..
        for led in range(len(self.led_array)):
            self.led_array[led].to_green()
            self.after(1000) # stops time
        self.start.config(state=NORMAL) 
        sys.exit(0) # ensures after exec. thread is killed
# class format to use after() and threading to reset the button status
Testing().mainloop()
  

Комментарии:

1. Извините, но я не знаком с ООП, поэтому я не могу следовать этому ответу. Я искал способ решить эту проблему в парадигме, которую я использовал, т. Е. функциональное программирование.