Ткинтер, выполняющий функции со временем - PullRequest
4 голосов
/ 18 февраля 2012

Я новичок в tkinter и пытаюсь выяснить, как работает поток управления.

Я хочу отобразить прямоугольник и заставить его мигать три раза.Я написал этот код, но он не работает.Я предполагаю, что это потому, что blink выполняется до mainloop, и на самом деле ничего не рисует.Если так, как я могу поменять поток управления между blink и mainloop, чтобы он работал?Заранее спасибо!

Мой код:

from tkinter import *
from time import *

def blink(rectangle, canvas):
    for i in range(3):
        canvas.itemconfigure(rectangle, fill = "red")
        sleep(1)
        canvas.itemconfigure(rectangle, fill = "white")
        sleep(1)

root = Tk()
fr = Frame(root)
fr.pack()
canv = Canvas(fr, height = 100, width = 100)
canv.pack()
rect = canv.create_rectangle(25, 25, 75, 75, fill = "white")
blink(rect, canv)
root.mainloop()

Ответы [ 2 ]

17 голосов
/ 18 февраля 2012

Событийное программирование требует иного мышления, чем процедурный код.Ваше приложение работает в бесконечном цикле, извлекая события из очереди и обрабатывая их.Чтобы сделать анимацию, все, что вам нужно сделать, это поместить элементы в эту очередь в соответствующее время.

У виджетов Tkinter есть метод с именем после , который позволяет планировать выполнение функций через определенный периодвремени.Первым шагом является написание функции, которая делает один «кадр» вашей анимации.В вашем случае вы определяете анимацию как переключение между двумя цветами.Функция, которая проверяет текущий цвет, затем переключается на другой цвет - это все, что вам нужно:

def blink(rect, canvas):
    current_color = canvas.itemcget(rect, "fill")
    new_color = "red" if current_color == "white" else "white"
    canvas.itemconfigure(rect, fill=new_color)

Теперь нам просто нужно запустить эту функцию три раза с интервалом в одну секунду:

root.after(1000, blink, rect, canv)
root.after(2000, blink, rect, canv)
root.after(3000, blink, rect, canv)

Когда вы запускаете свой основной цикл, через одну секунду цвет изменится, через следующую секунду он снова изменится, а через третью секунду он снова изменится.

Это работает для ваших очень специфических потребностей, но это не очень хорошее общее решение.Более общее решение состоит в том, чтобы вызвать blink один раз, а затем через некоторое время blink снова вызвать себя.blink тогда должен нести ответственность, чтобы знать, когда прекратить мигать.Вы можете установить флаг или счетчик, чтобы отслеживать, сколько раз вы моргали.Например:

def blink(rect, canvas):
    ...
    # call this function again in a second to
    # blink forever. If you don't want to blink
    # forever, use some sort of flag or computation
    # to decide whether to call blink again
    canvas.after(1000, blink, rect, canvas)

В качестве последнего совета я рекомендую вам определить свою программу как класс, а затем создать экземпляр этого класса.Это делает так, что вам не нужны глобальные функции, и вам не нужно передавать столько аргументов.Это не имеет значения для программы из 20 строк, но начинает иметь значение, когда вы хотите написать что-то существенное.

Например:

from tkinter import *

class MyApp(Tk):
    def __init__(self):
        Tk.__init__(self)
        fr = Frame(self)
        fr.pack()
        self.canvas  = Canvas(fr, height = 100, width = 100)
        self.canvas.pack()
        self.rect = self.canvas.create_rectangle(25, 25, 75, 75, fill = "white")
        self.do_blink = False
        start_button = Button(self, text="start blinking", 
                              command=self.start_blinking)
        stop_button = Button(self, text="stop blinking", 
                              command=self.stop_blinking)
        start_button.pack()
        stop_button.pack()

    def start_blinking(self):
        self.do_blink = True
        self.blink()

    def stop_blinking(self):
        self.do_blink = False

    def blink(self):
        if self.do_blink:
            current_color = self.canvas.itemcget(self.rect, "fill")
            new_color = "red" if current_color == "white" else "white"
            self.canvas.itemconfigure(self.rect, fill=new_color)
            self.after(1000, self.blink)


if __name__ == "__main__":
    root = MyApp()
    root.mainloop()
3 голосов
/ 18 февраля 2012

Каждый виджет имеет функцию «после» - то есть он может вызывать другую функцию через определенный промежуток времени. Итак, вам нужно вызвать:

root.after( 1000, blink )

Если вы хотите, чтобы это был повторяющийся вызов, просто вызовите «после» снова внутри функции мигания. Единственная проблема, с которой вы столкнетесь, - это передача аргументов для мерцания - возможно, для этого стоит посмотреть на использование lamda внутри 'after'.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...