Tkinter / Turtle Остановка и запуск таймеров чисто - PullRequest
0 голосов
/ 31 марта 2020

Я недавно сталкивался с несколькими ситуациями при написании программ, использующих Python Turtle Graphics, где метод ontimer() turtle был недостаточен для моих нужд, и мне пришлось использовать базовые tkinter методы after() и * 1004. *.

Даже имея эту мощь в моих руках, у меня были реальные проблемы с реализацией функций запуска / остановки на таймерах. Кажется, что каждый раз, когда я запускаю новый таймер, он как-то сочетается с существующим и обратными вызовами go haywire.

Я наконец нашел решение, как показано в коде ниже. Как таковое, сброс нескольких бомб приводит к неконтролируемому ускорению таймера бомбы. После долгих исследований и экспериментов я нашел решение, показанное в комментарии в коде __continue_bomb_drop(), которое заключается в отмене существующего таймера в __continue_bomb_drop(), хотя, похоже, его следовало убить. на after_cancel() в stop_bomb_drop().

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

Может кто-нибудь объяснить, почему мне нужно after_cancel() bomb_timer_id в __continue_bomb_drop() и stop_bomb_drop()? Также, скажите, пожалуйста, есть ли более простой / более канонический способ добиться того, что я сейчас делаю, с помощью трех функций start_bomb_drop(), __continue_bomb_drop() и stop_bomb_drop()?

import turtle
import random


CURSOR_SIZE = 20
PLANE_DELAY = 10 # maybe increase speed as we go.... 10
BOMB_DELAY = 50

def move_plane():
    new_pos = (plane.xcor(), plane.ycor())
    if new_pos[0] > width // 2:
        plane.goto(- width // 2, plane.ycor() - size)
    else:
        plane.goto(plane.xcor() + 12, plane.ycor())
    if tower_collision(plane, towers):
        plane.goto( - width // 2 , height // 2)        
    screen.update()
    turtle.ontimer(move_plane, PLANE_DELAY)


def cell_collision(tur1, tur2):
    if tur1.distance(tur2) <= size / 2:
        return True

def tower_collision(tur, towers):
    for tower in towers:
        for cell in tower:
            if cell_collision(tur, cell):
                return True

def bomb_collision():
    # Should I use global towers or pass as argument?
    pass


def start_bomb_drop():
    global bomb_dropping
    screen.onkey(None, "space")
    bomb.goto(plane.xcor(), plane.ycor())
    bomb.showturtle()
    __continue_bomb_drop()


def __continue_bomb_drop():
    global bomb_dropping, bomb_timer_id
    try:
        pass
        # canvas.after_cancel(bomb_timer_id) # Uncomment and remove "pass" to fix bug.
    except NameError:
        pass
    bomb.goto(bomb.xcor(), bomb.ycor() - 12)
    if bomb.ycor() < - height // 2 or bomb_collision():
        stop_bomb_drop()
    bomb_timer_id  = canvas.after(BOMB_DELAY, __continue_bomb_drop)

def stop_bomb_drop():
    global bomb_dropping, bomb_timer_id
    bomb.hideturtle()
    screen.onkey(start_bomb_drop, "space")
    canvas.after_cancel(bomb_timer_id)

# Screen   
screen = turtle.Screen()
canvas = screen.getcanvas()
screen.title("Alien Bomber")
screen.setup(800, 600)
screen.bgcolor("dark blue")
screen.listen()
screen.onkey(start_bomb_drop, "space")
screen.tracer(0)

# MISC.
cells = 20
cell_colors = ["black", "dark green", "brown"]
width = screen.window_width() - 50
height = screen.window_height() - 50
size = width / cells
offset = (cells % 2) * size/2 + size/2  # Center even and odd cells

# Build towers
towers = []
for col in range(-cells // 2, cells // 2):
    tower = []
    for level in range(random.randrange(1, 11)):
        block = turtle.Turtle(shape="square")
        block.shapesize(size / CURSOR_SIZE)
        block.color(random.choice(cell_colors))
        block.penup()
        block.goto(col * size + offset, - height // 2 + level * size + offset)
        tower.append(block)
    towers.append(tower)

# Plane
plane = turtle.Turtle(shape="triangle", visible=False)
plane.color("yellow")
plane.shapesize(20 / CURSOR_SIZE, 40 / CURSOR_SIZE)
plane.penup()
plane.goto( - width // 2 , height // 2)
plane.showturtle()

# Bomb
bomb = turtle.Turtle(shape="circle")
bomb.color("red")
bomb.shapesize(0.5)
bomb.penup()
bomb.hideturtle()
bomb_dropping = False

# Score
score = 0
high_score = 0
pen = turtle.Turtle()
# pen.shape("square")
pen.color("white")
pen.penup()
pen.hideturtle()
pen.goto(0, 260)
pen.write("Score: 0 High Score: {}".format(high_score), align="center", font=("Courier", 24, "normal"))

# Begin
screen.update()
move_plane()
turtle.done()
...