0

essentially, what I'm trying to do is move a ball across the screen, but like, small movements every few milliseconds, but tkinter takes the loop, adds all the milliseconds together and moves the ball where is should be (i.e, 250ms delay in a for loop for 4 increments, the ball moves after 1 whole second)

example code:

import tkinter *
root = Tk()

global x
global y
w = 200
h = 200
x = w/2
y = h/2

pic = Canvas(root,width=w,height=h)
pic.pack()

mover = pic.create_oval(x,y, x+10,y+10,fill="red")

def left(event):
    global x
    for num in range(4):
        x = -5
        y = 0
        pic.move(mover,x,y)
        root.after(250)

root.bind("<Left>",left)

what should be happening is 4 movements in 250ms intervals, however, it makes one large movement after a single second.

time.sleep() also causes the same error, globalizing y had no effect either(as expected)

I couldn't find another already answered question, so any help would appreciates, please and thanks!

  • There are many questions on this site related to moving an object on a canvas. See [here](https://stackoverflow.com/a/7987967/7432) and [here](https://stackoverflow.com/a/36200248/7432) and [here](https://stackoverflow.com/search?q=%5Btkinter%5D+move+ball+canvas+) – Bryan Oakley Aug 14 '20 at 18:01
  • use `after` without `for`-loop because `after` will repeate it. – furas Aug 14 '20 at 18:52
  • All GUIs to works smoother don't update window directly when you change item but after endinng your function - this way it has to update window only once and it works smoother. – furas Aug 14 '20 at 18:54

1 Answers1

0

All GUIs to work smoother they don't update/readraw window directly when you change item but after ending your function. This way it has to update window only once (when you make many changes in function) and it works smoother (it is less bliting).


If you want to use after() to repeate move then you need after(250, left) but you don't need for-loop

after() will send information 250, left to mainloop() and it will execute left() after 250ms - and later you end your function so mainloop() may update window after one move.


Minimal working code

import tkinter as tk  # PEP8: `import *` is not preferred

# --- functions ---

def left(event):
    move(-5, 0, 4)
    
def right(event):
    move(5, 0, 4)

def up(event):
    move(0, -5, 4)

def down(event):
    move(0, 5, 4)

def move(dx, dy, n):    
    pic.move(mover, dx, dy)

    n -= 1

    if n > 0:
        # repeate it again after 50ms
        root.after(50, move, dx, dy, n)
    
    # end of function so it goes back to mainloop and it can update/redraw window
        
# --- main ---

w = 200
h = 200
x = w/2
y = h/2

root = tk.Tk()

pic = tk.Canvas(root, width=w, height=h)  # PEP8: spaces after `,`
pic.pack()

mover = pic.create_oval(x, y, x+10, y+10, fill="red")  # PEP8: spaces after `,`

root.bind("<Left>", left)  # PEP8: spaces after `,`
root.bind("<Right>", right)
root.bind("<Up>", up)
root.bind("<Down>", down)

root.mainloop()

If you use after(250) or time.sleep(0.25) to sleep then you need root.update() to force mainloop() to update/redraw window.


Minimal working code

import tkinter as tk  # PEP8: `import *` is not preferred

# --- functions ---

def left(event):
    move(-5, 0, 4)
    
def right(event):
    move(5, 0, 4)

def up(event):
    move(0, -5, 4)

def down(event):
    move(0, 5, 4)

def move(dx, dy, n):
    for _ in range(n):
        pic.move(mover, dx, dy)

        root.update()   # <-- force `mainloop()` to update window

        root.after(50)
        #time.sleep(0.25)
        
# --- main ---

w = 200
h = 200
x = w/2
y = h/2

root = tk.Tk()

pic = tk.Canvas(root, width=w, height=h)  # PEP8: spaces after `,`
pic.pack()

mover = pic.create_oval(x, y, x+10, y+10, fill="red")  # PEP8: spaces after `,`

root.bind("<Left>", left)  # PEP8: spaces after `,`
root.bind("<Right>", right)
root.bind("<Up>", up)
root.bind("<Down>", down)

root.mainloop()

BTW: Both versions have one problem - when you press many keys then it will run move for all keys at the same time so it may move in strange way. It would need variable is_moving = True/False to control if it can move for other keys.

furas
  • 134,197
  • 12
  • 106
  • 148
  • Thank you for your response, but just for clarification, the idea is, for smoothing purposes, canvas redraws after a loop completes, unless told specifically otherwise? – That boi with a scythe Aug 17 '20 at 16:38