pythontkintercanvasorbital-mechanics

Modeling simultaneous moving bodies in canvas


I'm strugling with canvas.move in a simulation of a celestial system. How can I simultaneously move multiple objects in a defined Space instance? I guess I have to do something with the identity of the body objects. But I cannot find it out. Perhaps I should use repeating draw en delete methods in stead of canvas.move? See a simplified version of the coden below. Does some body has a suggestion? Many thanks

import tkinter as tk

class Space(tk.Frame):
    def __init__(self, master, size, bg=None):
        super().__init__(master)
        frame = tk.Frame(master, border = 5)
        frame.pack()
        self.width, self.height = size[0], size[1]
        self.canvas = tk.Canvas(frame,
                                width = self.width,
                                height = self.height,
                                borderwidth= 0,
                                highlightthickness= 0,
                                bg=bg)
        self.canvas.pack()

    def place_body(self, body):
        x1, y1 = body.loc[0], body.loc[1]
        x2, y2 = x1+body.size, y1+body.size
        self.canvas.create_oval(x1,y1,x2,y2, fill=body.color)

    def distance_step(self):
        pass

    def move_body(self, body):
        # in stead of distance_step:
        dx, dy = body.speed[0], body.speed[1]
        self.canvas.move(body, dx, dy)
        self.canvas.after(1, lambda: self.move_body(body))         

class CelestialBody:
    def __init__(self, name, size, mass, loc, speed, color="white"):
        self.name = name
        self.size = size
        self.mass = mass
        self.loc = loc
        self.speed = speed
        self.color = color

    def __repr__(self):
        return f"{self.name}"

class App:
    def __init__(self):
        x, y = 1000, 800
        size = (x,y)
        space = Space(root,size, bg = 'black')         
        sun1_size = 30
        sun1_mass = 10
        sun1_loc = (700, 450)        
        sun1_speed = (-200,0)
        
        sun2_size = 30
        sun2_mass = 10        
        sun2_loc = (300, 350)
        sun2_speed = (200,0)       
       
        sun1 = CelestialBody("sun1", sun1_size, sun1_mass, sun1_loc, sun1_speed, color = "yellow")
        sun2 = CelestialBody("sun2", sun2_size, sun2_mass, sun2_loc, sun2_speed,  color ="yellow")

        space.place_body(sun1)
        space.place_body(sun2)
        
        space.move_body(sun1)
        space.move_body(sun2)       

        print(sun1, sun2)
        root.mainloop()

root = tk.Tk()
root.title('UNIVERSE')
app = App()```

Solution

  • You need to keep track of the tag returned from canvas.create_oval(). See .tk_tag below. I had to slow down your speeds because the object immediately left the screen. Also note: instead of dx, dy = body.speed[0], body.speed[1], you can just do dx, dy = body.speed.

    import tkinter as tk
    
    
    class Space(tk.Frame):
        def __init__(self, master, size, bg=None):
            super().__init__(master)
            frame = tk.Frame(master, border=5)
            frame.pack()
            self.width, self.height = size
            self.canvas = tk.Canvas(frame,
                                    width=self.width,
                                    height=self.height,
                                    borderwidth=0,
                                    highlightthickness=0,
                                    bg=bg)
            self.canvas.pack()
    
        def place_body(self, body):
            x1, y1 = body.loc
            x2, y2 = x1 + body.size, y1 + body.size
            body.tk_tag = self.canvas.create_oval(x1, y1, x2, y2, fill=body.color)
    
        def distance_step(self):
            pass
    
        def move_body(self, body):
            # in stead of distance_step:
            dx, dy = body.speed
            dx, dy = dx/100, dy/100
            self.canvas.move(body.tk_tag, dx, dy)
            self.canvas.after(1, lambda: self.move_body(body))
    
    
    class CelestialBody:
        def __init__(self, name, size, mass, loc, speed, color="white"):
            self.name = name
            self.size = size
            self.mass = mass
            self.loc = loc
            self.speed = speed
            self.color = color
            self.tk_tag = None
    
        def __repr__(self):
            return f"{self.name}"
    
    
    class App:
        def __init__(self):
            x, y = 1000, 800
            size = (x, y)
            space = Space(root, size, bg='black')
            sun1_size = 30
            sun1_mass = 10
            sun1_loc = (700, 450)
            sun1_speed = (-200, 0)
    
            sun2_size = 30
            sun2_mass = 10
            sun2_loc = (300, 350)
            sun2_speed = (200, 0)
    
            sun1 = CelestialBody("sun1", sun1_size, sun1_mass, sun1_loc, sun1_speed, color="yellow")
            sun2 = CelestialBody("sun2", sun2_size, sun2_mass, sun2_loc, sun2_speed, color="yellow")
    
            space.place_body(sun1)
            space.place_body(sun2)
    
            space.move_body(sun1)
            space.move_body(sun2)
    
            print(sun1, sun2)
            root.mainloop()
    
    
    root = tk.Tk()
    root.title('UNIVERSE')
    app = App()