patternpythonMinor
Display a simulation using Tkinter
Viewed 0 times
displaytkinterusingsimulation
Problem
I wrote a module to simulate physics of 2D elastic balls and the community helped me to improve it on this post.
Now I implemented a GUI using Tkinter to display the simulation in a window.
I'm a beginner in programming GUI and I don't know if my script can be more efficient and/or simpler.
Indeed, I'm not really satisfied by my
Moreover, when I push the start button twice, I also need to push the pause button twice to stop the simulation. I don't understand this behaviour !
Import modules
You'll find the solver module here. This isn't the object of this post. If you've comments or remarks about it, post them on this post I spoke above.
Surrounding functions
```
def _create_circle(self, x, y, r, **kwargs):
"""Create a circle
x the abscissa of centre
y the ordinate of centre
r the radius of circle
**kwargs optional arguments
return the drawing of a circle
"""
return self.create_oval(x-r, y-r, x+r, y+r, **kwargs)
tk.Canvas.create_circle = _create_circle
def _coords_circle(self, target, x, y, r, **kwargs):
"""Define a circle
target the circle object
x the abscissa of centre
y the ordinate of centre
r the radius of circle
**kwargs optional arguments
return the circle drawing with updated coordinates
"""
return self.coords(target, x-r, y-r, x+r, y+r, **kwargs)
tk.Canvas.coords_circle = _coords_circle
def create(balls, canvas):
"""Create a drawing item for each solver.Ball object
balls the list of solver.Ball objects
canvas the Tkinter.Canvas oject
return a dictionary with solver.Ball objects as keys and their circle drawings as items
"""
return {ball: canvas.create_circle(ball.position[0], ball.position[1], ball.radius, fill="white") for ball in balls}
def update(drawing, canvas, step, size):
"""Update the drawing i
Now I implemented a GUI using Tkinter to display the simulation in a window.
I'm a beginner in programming GUI and I don't know if my script can be more efficient and/or simpler.
Indeed, I'm not really satisfied by my
display function because it includes definitions of other functions dedicated to the buttons commands.Moreover, when I push the start button twice, I also need to push the pause button twice to stop the simulation. I don't understand this behaviour !
Import modules
import Tkinter as tk
import solverYou'll find the solver module here. This isn't the object of this post. If you've comments or remarks about it, post them on this post I spoke above.
Surrounding functions
```
def _create_circle(self, x, y, r, **kwargs):
"""Create a circle
x the abscissa of centre
y the ordinate of centre
r the radius of circle
**kwargs optional arguments
return the drawing of a circle
"""
return self.create_oval(x-r, y-r, x+r, y+r, **kwargs)
tk.Canvas.create_circle = _create_circle
def _coords_circle(self, target, x, y, r, **kwargs):
"""Define a circle
target the circle object
x the abscissa of centre
y the ordinate of centre
r the radius of circle
**kwargs optional arguments
return the circle drawing with updated coordinates
"""
return self.coords(target, x-r, y-r, x+r, y+r, **kwargs)
tk.Canvas.coords_circle = _coords_circle
def create(balls, canvas):
"""Create a drawing item for each solver.Ball object
balls the list of solver.Ball objects
canvas the Tkinter.Canvas oject
return a dictionary with solver.Ball objects as keys and their circle drawings as items
"""
return {ball: canvas.create_circle(ball.position[0], ball.position[1], ball.radius, fill="white") for ball in balls}
def update(drawing, canvas, step, size):
"""Update the drawing i
Solution
As I didn't get any answer, I propose the following improvements.
First I didn't change the functions
I found a more satisfying solution for the
In this way, I can define the
The objects
Moreover, the start button calls the
You'll find the complete code here.
First I didn't change the functions
create_circle and coords_circle.I found a more satisfying solution for the
display function. Indeed, I create a class Display and implement the former display function in its __init__ method.class Display:
"""Define the window used to display a simulation"""
def __init__(self, balls, step, size):
"""Initialize and launch the display"""
self.balls = balls
self.step = step
self.size = size
self.window = tk.Tk()
self.canvas = tk.Canvas(self.window, width=self.size, height=self.size, bg="black")
self.canvas.pack()
self.canvas.focus_set()
self.drawing = self.create()
self.started = False
start_button = tk.Button(self.window, text="Start", command=self.start)
stop_button = tk.Button(self.window, text="Pause", command=self.stop)
start_button.pack()
stop_button.pack()
self.window.mainloop()In this way, I can define the
animate and stop functions as methods of the class Display rather than inside __init__.The objects
balls, step, size, window, canvas, drawing, start_button and stop_button become attributes of the class Display. So I don't need to put them in arguments of create and update methods.def create(self):
"""Create a drawing item for each solver.Ball object
return a dictionary with solver.Ball objects as keys and their circle drawings as items
"""
return {ball: self.canvas.create_circle(ball.position[0], ball.position[1], ball.radius, fill="white") for ball in self.balls}
def update(self):
"""Update the drawing items for a time step"""
solver.solve_step(self.balls, self.step, self.size)
for ball in self.balls:
self.canvas.coords_circle(self.drawing[ball], ball.position[0], ball.position[1], ball.radius)
self.canvas.update()Moreover, the start button calls the
start method that calls the animate method only if the value of started is False. Thus, we don't have the bad behaviour raised in the question (need to push the pause button twice after pushing the start button twice).def start(self):
"""Start the animation"""
if not self.started:
self.started = True
self.animate()
def animate(self):
"""Animate the drawing items"""
if self.started:
self.update()
self.window.after(0, self.animate)
def stop(self):
"""Stop the animation"""
self.started = FalseYou'll find the complete code here.
Code Snippets
class Display:
"""Define the window used to display a simulation"""
def __init__(self, balls, step, size):
"""Initialize and launch the display"""
self.balls = balls
self.step = step
self.size = size
self.window = tk.Tk()
self.canvas = tk.Canvas(self.window, width=self.size, height=self.size, bg="black")
self.canvas.pack()
self.canvas.focus_set()
self.drawing = self.create()
self.started = False
start_button = tk.Button(self.window, text="Start", command=self.start)
stop_button = tk.Button(self.window, text="Pause", command=self.stop)
start_button.pack()
stop_button.pack()
self.window.mainloop()def create(self):
"""Create a drawing item for each solver.Ball object
return a dictionary with solver.Ball objects as keys and their circle drawings as items
"""
return {ball: self.canvas.create_circle(ball.position[0], ball.position[1], ball.radius, fill="white") for ball in self.balls}
def update(self):
"""Update the drawing items for a time step"""
solver.solve_step(self.balls, self.step, self.size)
for ball in self.balls:
self.canvas.coords_circle(self.drawing[ball], ball.position[0], ball.position[1], ball.radius)
self.canvas.update()def start(self):
"""Start the animation"""
if not self.started:
self.started = True
self.animate()
def animate(self):
"""Animate the drawing items"""
if self.started:
self.update()
self.window.after(0, self.animate)
def stop(self):
"""Stop the animation"""
self.started = FalseContext
StackExchange Code Review Q#132413, answer score: 2
Revisions (0)
No revisions yet.