This practical is about adding a Graphical User Interface (GUI).
In your local code repository 'src' directory duplicate your 'abm7' directory and call the new directory 'abm8'.
Matplotlib has different 'backends' that allow it to render graphics in different ways. As the GUI will be developed using tkinter, it is necessary to change the matplotlib backend by adding the following at the top of 'model.py' before any other matplotlib imports:
import matplotlib
matplotlib.use('TkAgg')
In Spyder, go to:
Tools > Preferences > IPython console > Graphics tab
Select 'Tkinter' from the 'Backend' drop down list. Click 'ok'. Then select:
File > Restart
Save your files if prompted.
Add the following function to 'model.py':
def run(canvas):
animation = anim.FuncAnimation(fig, update, init_func=plot, frames=gen_function, repeat=False)
animation.new_frame_seq()
canvas.draw()
Add the following import statement to 'model.py':
import tkinter as tk
In 'model.py' replace the following line (not the line in the new run function):
animation = anim.FuncAnimation(fig, update, init_func=plot, frames=gen_function, repeat=False)
With:
# GUI
root = tk.Tk()
root.wm_title("Agent Based Model")
canvas = matplotlib.backends.backend_tkagg.FigureCanvasTkAgg(fig, master=root)
canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
menu_bar = tk.Menu(root)
root.config(menu=menu_bar)
menu_0 = tk.Menu(menu_bar)
menu_bar.add_cascade(label="Model", menu=menu_0)
menu_0.add_command(label="Run model", command=lambda: run(canvas))
menu_0.add_command(label="Write data", command=lambda: output())
menu_0.add_command(label="Exit", command=lambda: exiting())
menu_0.entryconfig("Write data", state="disabled")
# Exit if the window is closed.
root.protocol('WM_DELETE_WINDOW', exiting)
tk.mainloop()
Add the following functions to 'model.py':
def output():
# Write data
print("write data")
io.write_data('../../data/output/out.txt', environment)
imageio.mimsave('../../data/output/out.gif', images, fps=3)
def exiting():
"""
Exit the program.
"""
root.quit()
root.destroy()
#sys.exit(0)
Change the 'gen_function' from:
def gen_function():
global ite
global carry_on
while (ite <= n_iterations) & (carry_on) :
yield ite # Returns control and waits next call.
ite = ite + 1
global data_written
if data_written == False:
# Write data
print("write data")
io.write_data('../../data/output/out.txt', environment)
imageio.mimsave('../../data/output/out.gif', images, fps=3)
data_written = True
To:
def gen_function():
global ite
global carry_on
while (ite <= n_iterations) & (carry_on) :
yield ite # Returns control and waits next call.
ite = ite + 1
global data_written
if data_written == False:
# Set the Write data menu to normal.
menu_0.entryconfig("Write data", state="normal")
data_written = True
Now if the window in which the animation runs is closed, the program should exit and exiting the program is also added as a menu option in the GUI.
The 'output' function now deals with writing the outputs and this becomes actionable from the GUI once the simulation has run.
Run the program.
Commit your code to your local repository and assuming you are using GitHub - push your changes to GitHub.