Hello,

I have just started playing around a bit with Python/Tkinter. What I'd like to happen is to draw a circle and then have the circle change its coordinate. The way I was thinking is to have some integer i to be defined 0 and then be updated every frame (ie i+=1), with the drawing algorithm modified appropriately. However, since mainloop() seems to act as a sort of global while(1) loop, this doesn't seem possible. What I have now:

from Tkinter import *

root = Tk()
def drawcircle(canv,x,y,rad):
    canv.create_oval(x-rad,y-rad,x+rad,y+rad,width=0,fill='blue')

canvas = Canvas(width=600, height=200, bg='white')  
canvas.pack(expand=YES, fill=BOTH) 

text = canvas.create_text(50,10, text="tk test")

#i'd like to recalculate these coordinates every frame
circ1=drawcircle(canvas,100,100,20)          
circ2=drawcircle(canvas,500,100,20)

root.mainloop()

So, how do I go about doing this?

Another question, could anyone suggest a good Tkinter/Python tutorial? What I need is maximally simple and relatively short tutorial that will allow me to make *simple* 2D simulations (ie, my goal for a first Python algorithm is a 1D molecular dynamics simulation using Lennard-Jones potential).

Thank you very much in advance,

Ilya

Dani AI

Generated

In Tkinter you do not drive animation with your own while-loop; you schedule small, periodic updates on the event loop. Keep a reference to the canvas item ID, update its position, then reschedule the next tick with after. Use canvas.move(item, dx, dy) for relative motion or canvas.coords(item, x1, y1, x2, y2) for absolute placement. Avoid time.sleep() or busy loops, which will freeze the GUI.

Example: simple horizontal movement with edge bounce at ~60 FPS.

from Tkinter import *
root = Tk()

W, H = 600, 200
canvas = Canvas(root, width=W, height=H, bg='white')
canvas.pack()

r = 20
ball = canvas.create_oval(100-r, 100-r, 100+r, 100+r, fill='blue', width=0)
vx = 3  # pixels per frame

def step():
    global vx
    canvas.move(ball, vx, 0)
    x1, y1, x2, y2 = canvas.coords(ball)
    if x1 <= 0 or x2 >= W:  # bounce on walls
        vx = -vx
    root.after(16, step)  # schedule next frame

step()
root.mainloop()

For simulations, decouple physics from rendering: compute positions/velocities in a fixed-timestep update, then draw the latest state each tick. If computation becomes heavy, keep it off the GUI thread and communicate results back via a queue polled with after. See after in the Tkinter docs (tkinter.Widget.after) and Canvas operations like coords in the Tk manual (Canvas). If you later want a plotting-focused route for animations, matplotlib.animation is also well documented (Matplotlib animation API).

Recommended Answers

All 3 Replies

Hi Ilya,

If you want to do interactive simulations, I think you might be better off checking out pygame, which is designed for games but can easily be adapted for this kind of thing. I have written simple 2D epithelial tumor growth simulations in pygame - I can't imagine how you could accomplish such a thing in Tkinter, short of setting up a separate thread to mess with the Tkinter root or canvas, and from what I remember that introduces all kinds of synchronization issues.

The pygame tutorials and API documentation are also available on the site I linked.

Hope this helps!

I agree with G-Do's advice, but if you are stuck using Tkinter, then you want to think like this: events instead of timeline.

In other words, something has to trigger your application to move the circle.

Here's a version that fixes a bug and also moves the circles on every mouse click.

from Tkinter import *

root = Tk()

# All of this would do better as a subclass of Canvas with specialize methods

def drawcircle(canv,x,y,rad):
    # changed this to return the ID
    return canv.create_oval(x-rad,y-rad,x+rad,y+rad,width=0,fill='blue')

def movecircle(canv, cir):
    canv.move(cir,-1,-1)

def callback(event):
    movecircle(canvas, circ1)
    movecircle(canvas, circ2)
    
canvas = Canvas(width=600, height=200, bg='white')
canvas.bind("<Button-1>", callback)
canvas.pack(expand=YES, fill=BOTH)


text = canvas.create_text(50,10, text="tk test")

#i'd like to recalculate these coordinates every frame
circ1=drawcircle(canvas,100,100,20)          
circ2=drawcircle(canvas,500,100,20)

root.mainloop()

All right, thank you.

Ilya

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.