Python Сброс итерации во время итерации по таймеру - PullRequest
0 голосов
/ 15 марта 2020

Я получаю странное поведение, когда нажимаю кнопку reset в программе ниже, пока путь не будет завершен. Когда я это делаю, черепаха ускоряется и двигается хаотично, в то же время все еще в основном придерживаясь пути. Чем больше раз я нажимаю reset, тем больше стираем c движение.

Может кто-нибудь объяснить, как кнопка reset полностью сбрасывает путь и позволяет избежать проблемы, описанной выше?

import turtle
import tkinter as tk

def create_reset_button_tkinter():
    """An alternative approach to creating a button using Tkinter."""
    canvas = screen.getcanvas()
    button = tk.Button(canvas.master, text="Reset", background="green", foreground="white", bd=0,
                       activebackground="green", activeforeground="white", command=reset)
    canvas.create_window(-100, 100, window=button)

def reset():
    global path_iter
    bob.goto(0, 0)
    path = [(0, 0), (20, 0), (20, 20), (20, 30), (30, 20), (40, 20), (40, 30), (50, 30), (60, 30), (60, 40)]
    path_iter = iter(path)
    go_to_next_coord()


def go_to_next_coord():
    try:
        next_coord = next(path_iter)
    except StopIteration:
        return
    bob.goto(next_coord)
    turtle.ontimer(go_to_next_coord, 500)


screen = turtle.Screen()
create_reset_button_tkinter()
bob = turtle.Turtle("square")
bob.penup()
path = [(0, 0), (20, 0), (20, 20), (20, 30), (30, 20), (40, 20), (40, 30), (50, 30), (60, 30), (60, 40)]
path_iter = iter(path)
go_to_next_coord()

turtle.done()

Ответы [ 2 ]

0 голосов
/ 16 марта 2020

Мне не ясно, какое преимущество вы получаете, используя iter, особенно когда исправление @ acw1668 в вашем коде должно преобразовать iter обратно в list:

num_of_items = len(list(path_iter))

Почему бы просто не использовать list во-первых:

from turtle import Screen, Turtle
from tkinter import Button

PATH = [ \
    (0, 0), (20, 0), (20, 20), (20, 30), (30, 20), \
    (40, 20), (40, 30), (50, 30), (60, 30), (60, 40), \
]

semaphore = 0

def reset():
    global semaphore

    if semaphore:
        return  # reset already executing when 'Reset' pressed

    semaphore += 1
    empty = len(path_list) == 0

    path_list[:] = PATH  # reload path coordinates

    turtle.home()

    if empty:
        screen.ontimer(go_to_next_coord)  # execute shortly after this function exits

    semaphore -= 1

def go_to_next_coord():
    if not semaphore:
        try:
            turtle.goto(path_list.pop(0))
        except IndexError:
            return

    screen.ontimer(go_to_next_coord, 500)

def create_reset_button_tkinter():
    """ An alternative approach to creating a button using Tkinter. """

    canvas = screen.getcanvas()
    button = Button(canvas.master, text="Reset", command=reset)

    canvas.create_window(-100, 100, window=button)

screen = Screen()

create_reset_button_tkinter()

turtle = Turtle("square")
turtle.penup()

path_list = list(PATH)

go_to_next_coord()

screen.mainloop()

Я обнаружил две проблемы, которые все еще могут вызывать поведение erati c в любой реализации: во-первых, go_to_next_coord() может выполняться во время reset() метод. (Например, когда вы просите черепаху что-то сделать, это на короткое время возвращает управление обработчику событий и позволяет таймерам срабатывать.) Это вызывало редкие неожиданные визуальные сбои.

Во-вторых, если кнопка была вызвана turtle onclick(), я бы обычно сбрасывал отображение обработчика событий внутри кода обработчика событий (до тех пор, пока он не вернется), чтобы предотвратить укладку событий. Однако, так как это кнопка tkinter, это вне контроля черепахи. (Нажмите «Сброс» снова и снова.)

Чтобы смягчить обе проблемы, я ввел простую переменную semaphore. (Хотя вы также можете отключить и снова включить событие кнопки с помощью команд tkinter.) Не идеально, но лучше.

0 голосов
/ 16 марта 2020

Вы можете проверить, есть ли еще элементы в текущем итераторе в функции Reset(), если нет, вызвать go_to_next_coord(), чтобы перезапустить таймер черепахи:

def reset():
    global path_iter
    num_of_items = len(list(path_iter)) # get the number of items in current iterator
    #bob.goto(0, 0)  # no need to call here as it will be executed in next call of go_to_next_coord()
    path = [(0, 0), (20, 0), (20, 20), (20, 30), (30, 20), (40, 20), (40, 30), (50, 30), (60, 30), (60, 40)]
    path_iter = iter(path)
    if num_of_items == 0:
        # restart turtle timer
        go_to_next_coord()
...