Rastko Jović
Rastko Jović

Reputation: 139

Python tkinter Canvas root.after() maximum recursion depth exceeded

from tkinter import *

root = Tk()
canvas = Canvas(root, width=400, height=400, bg="white")
canvas.pack()
rect = canvas.create_rectangle(100, 100, 110, 110, fill='blue')
def move_down(event):
    canvas.move(rect, 0, 10)
    root.after(1, move_down(event))
root.bind('<Down>', move_down)
root.mainloop()

I can't seem to figure out how to make root.after() work. How can I fix this so the rectangle keeps moving down?

Upvotes: 2

Views: 3523

Answers (2)

dominik findeisen
dominik findeisen

Reputation: 62

I would recommend not using root.after(), so it will move when you click, and not move when you stop clicking

Upvotes: 0

Kevin
Kevin

Reputation: 76204

Short version: you can't put parentheses on the function you pass to after.

    root.after(1,move_down(event))

This line does not register the function move_down as the callback of the after event. Instead, it calls move_down immediately, and would register the return value of move_down as the callback, if you didn't enter an infinite recursion.

To solve this, use just move_down without actually calling it, and make event an optional variable because after isn't going to supply a value. You should probably also use a time larger than 1 ms, or else your rectangle will zip off the screen in the blink of an eye.

from tkinter import *
root = Tk()
canvas = Canvas(root, width=400, height= 400, bg="white")
canvas.pack()
rect = canvas.create_rectangle(100, 100, 110, 110, fill='blue')
def move_down(event=None):
    canvas.move(rect, 0, 10)
    root.after(100,move_down)
root.bind('<Enter>', move_down) #or whatever you're binding it to
root.mainloop()

Bonus info: If you're about to ask "ok, now how do I get the rectangle to stop moving when I release the key? And how do I make it move in each other direction when I press the other arrow keys?" That requires a more sophisticated design. You need the function registered to root.after to move a variable number of pixels depending the rectangle's velocity, which gets changed based on key events happening independently. Sample implementation:

from tkinter import *
root = Tk()
canvas = Canvas(root, width=400, height= 400, bg="white")
canvas.pack()
rect = canvas.create_rectangle(100, 100, 110, 110, fill='blue')
x_velocity = 0
y_velocity = 0

keys_being_held_down = set()
key_accelerations = {
    "Up": (0, -10),
    "Down": (0, 10),
    "Left": (-10, 0),
    "Right": (10, 0)
}

def key_pressed(event):
    global x_velocity, y_velocity

    #ignore autorepeat events
    if event.keysym in keys_being_held_down: 
        return

    keys_being_held_down.add(event.keysym)
    acceleration = key_accelerations[event.keysym]
    x_velocity += acceleration[0]
    y_velocity += acceleration[1]

def key_released(event):
    global x_velocity, y_velocity
    keys_being_held_down.remove(event.keysym)
    acceleration = key_accelerations[event.keysym]
    x_velocity -= acceleration[0]
    y_velocity -= acceleration[1]

def tick():
    canvas.move(rect, x_velocity, y_velocity)
    print(x_velocity, y_velocity)
    root.after(100,tick)

for key in key_accelerations:
    root.bind("<{}>".format(key), key_pressed)
    root.bind("<KeyRelease-{}>".format(key), key_released)

root.after(100, tick)
root.mainloop()

(This isn't necessarily the best way to do it, but it demonstrates the basic approach)

Upvotes: 11

Related Questions