создание и удаление объекта (например, прямоугольника) снова и снова с временной задержкой в ​​tkinter - PullRequest
0 голосов
/ 07 февраля 2020

Я довольно новичок в python. В настоящее время я стремлюсь написать программу, которая через несколько секунд должна создавать и удалять объект (прямоугольник, круг и т. Д. c.). Если я использую метод .after в отдельной функции, я получаю ошибку: UnboundLocalError: local variable 'counter' ссылается перед присваиванием, когда увидите код В качестве альтернативы, если я помещаю повторяющийся код в основную часть программы, он показывает только последнее состояние, а не промежуточное состояние (см. Описание между "" "и" "" в коде. Я искал inte rnet какое-то время, но я не мог вынести это. Я надеюсь, что вы можете мне помочь.

Вот упрощенная версия моего кода (на самом деле я работаю со списком re c), который показывает моя проблема:

import tkinter as tk


def master_field(text):
    """ work field (master) construction"""
    master.geometry('500x500+0+0') 
    master.title(text) 

def stop_button():
    """exit"""
    b = tk.Button(master, text = 'Exit', width=2, command = master.destroy)
    b.place(x = 50, y = 0, width = 100)

def built_canvas():
    """create a canvas"""
    canvas=tk.Canvas(master, width = canvas_width, height = canvas_height, borderwidth = 1, bg='light grey', highlightthickness=0, highlightbackground="blue")
    canvas.place(x = 0, y = 20)
    return canvas

def update_canvas():

    while counter<10:
        canvas.delete(rec)
        rec = canvas.create_rectangle(x1+i*10,y1+i*10,x2+i*10,y2+i*10, width=0, fill="green")
        counter +=1
        master.after(500,update_canvas)


# MAIN
master = tk.Tk()
master_field('MY FIELD')
stop_button()

canvas_width = 200
canvas_height = 200
canvas = built_canvas()

cell_width = 10
x1 = 10
y1 = 10
x2 = x1+10
y2 = y1+10
rec = canvas.create_rectangle(x1,y1,x2,y2, width=0, fill="green")

counter = 0
master.after(500,update_canvas)

"""
# The code below only gives the final state

for i in range(1,10):
    master.after(500)
    canvas.delete(rec)
    rec = canvas.create_rectangle(x1+i*10,y1+i*10,x2+i*10,y2+i*10, width=0, fill="green")
"""
master.mainloop()

Ответы [ 2 ]

2 голосов
/ 07 февраля 2020

Функция tkinter after делает две совершенно разные вещи - если вы называете это как в разделе, который вы помещаете между "" "" "":

master.after(500)

Это делает Ваша python программа ждет полсекунды, а затем продолжает.

Если вы называете это как ваш l oop in update_canvas:

master.after(500, update_canvas)

затем он возвращает немедленно , без ожидания, но планирует update_canvas, чтобы это произошло через полсекунды в будущем.

Таким образом, у вас есть версия выше с

 while counter<10:
     canvas.delete(rec)
     rec = canvas.create_rectangle(x1+i*10,y1+i*10,x2+i*10,y2+i*10, width=0, fill="green")
     counter +=1
     master.after(500,update_canvas)

Не дает правильной временной задержки, поскольку l oop проходит все 10 итераций без ожидания и планирует update_canvas, чтобы происходить 10 раз, все через полсекунды с сейчас , а не с половиной секунды друг от друга.

Версия в кавычках:

for i in range(1,10):
    master.after(500)
    canvas.delete(rec)
    rec = canvas.create_rectangle(x1+i*10,y1+i*10,x2+i*10,y2+i*10, width=0, fill="green")

Есть другая проблема - это запускает l oop несколько раз, и каждый раз ждет полсекунды, но окно еще даже не видно, так как вы не достигли master.mainloop() - поэтому оно оживляет объект, но вы не можете пока что.

Чтобы исправить это, вам нужно использовать первую версию, которая запланирована на будущее, так что вы можете запланировать ее, а затем вызвать mainloop(), а затем первую запланировать следующую.

Если мы сделаем это вместо:

def update_canvas():
    global counter, rec # global lets you change counter and rec from inside the function - avoids 'Unbound local variable'
    if counter >= 10:
        return #  If the counter is big enough, stop and do nothing more
    else:
        canvas.delete(rec) # Update the rectangle
        i = counter
        rec = canvas.create_rectangle(x1+i*10,y1+i*10,x2+i*10,y2+i*10, width=0, fill="green")
        counter +=1 # Add to the counter so we don't animate forever
        master.after(500,update_canvas) # Schedule the next update for half a second in the future

Тогда он должен оживить квадрат. Таким образом, l oop исчезает, но в первый раз, когда мы вызываем update_canvas, сама функция update_canvas просит tkinter вызвать ее позже. В конце концов счетчик становится достаточно большим, и он не запрашивает еще один вызов, и анимация останавливается.

0 голосов
/ 08 февраля 2020

Вам не нужно пока l oop в update_canvas(). Также вам не нужно воссоздавать прямоугольник, просто переместите его:

def update_canvas(counter=0):
    if counter < 10:
        canvas.move(rec, 10, 10)
        master.after(500, update_canvas, counter+1)
...