1

I'm learning python tkinter. I want to write a feature like that when I press "a" then the ball stop fall down. Why I press the keyboard, the function can be run, but it doesn't change the canvas?

tk = tkinter.Tk()
canvas = tkinter.Canvas(tk,width=500, height=400, bd=0, highlightthickness=0)
canvas.pack()

circle=canvas.create_oval(50,50,80,80, fill="yellow")

dir = True

def stop(event):
    dir = False

def move_func():
    canvas.move(circle, 0, 1)

def key_stop():
    tk.bind_all("a",stop)

while dir:
    move_func()
    key_stop()
    canvas.update_idletasks()
    canvas.update()

tk.mainloop()

I have tried to print something in stop function, it works well. But if I want to add an oval or change things on canvas, it doesn't work. Thanks.

ArthurXD
  • 13
  • 3
  • 1
    I think you could take a look at this https://stackoverflow.com/a/29158947/13629335 – Thingamabobs Oct 23 '20 at 16:15
  • Why are you binding in a loop? You only need to bind one time. More notably, why did you dump the bind in a function? What do you gain by trading one line for a different one-liner? – OneMadGypsy Oct 23 '20 at 16:32
  • @Michael Guidry Because this is only a simple part of a whole game. – ArthurXD Oct 24 '20 at 07:21

2 Answers2

2

It is because dir in stop() is a local variable, not the global one. You need to add global dir inside stop():

def stop():
    global dir
    dir = False

However, using while loop in a tkinter app is not recommended. Use after() instead:

import tkinter

tk = tkinter.Tk()

canvas = tkinter.Canvas(tk,width=500, height=400, bd=0, highlightthickness=0)
canvas.pack()

circle = canvas.create_oval(50, 50, 80, 80, fill="yellow")

dir = True

def stop(event):
    global dir
    dir = False

def move_func():
    if dir:
        canvas.move(circle, 0, 1)
        tk.after(5, move_func)

tk.bind_all("a", stop)
move_func() # start moving the circle

tk.mainloop()
acw1668
  • 40,144
  • 5
  • 22
  • 34
0

Instead of making arbitrary vars to manage the state, you can use variables that get created anyway and hold more meaning than your arbitrary ones. Let's consider something. Right now as your code stands it is very small, but is it going to stay that way? What will dir mean to you in 1000 lines ... 5000 lines? Is it a direction? Which direction? Is it a directory? Which directory? What if you need your move code to absolutely stop when you press "a", as opposed to enacting one last lingering process? Your dir var can't manage that.

Another thing to consider is your bind_all. Is that really necessary? Do you really want to add an event listener to every last child of root? As it stands currently, all that is necessary is to force the focus back to the root and only use 1 event listener. That could break for you in the future, but it doesn't have to.

Moving your ball 1 pixel at a time is not smart. Doing so forces you to rely on how fast you can call the move function again for speed. This will be different from one computer to the next and your game will never run reliably. Your player should have a move speed and that speed should become smaller or greater to compensate for the current frames-per-second. This way, the game always runs at the exact same speed, no-matter-what. It will just appear more choppy as the fps drops, but appearance or not the actual distance traveled of everything is identical. If everything remains identical regardless of performance, then everything also remains predictable. Imagine a multiplayer game played between 2 people, one running at 60 fps and the other at 30. How could you be sure of anything if everything moves half as much for the second person?

import tkinter as tk

root = tk.Tk()

canvas = tk.Canvas(root, width=500, height=400, bd=0, highlightthickness=0)
canvas.pack()
circle = canvas.create_oval(50, 50, 80, 80, fill="yellow")

update_id = None

def update():
    global update_id
    canvas.move(circle, 0, 5)
    #this id gets created whether you use it or not ... so use it.
    update_id = root.after(1000//60, update)

root.bind("a", lambda e:root.after_cancel(update_id))
root.focus_force()

update()

root.mainloop()
OneMadGypsy
  • 4,640
  • 3
  • 10
  • 26