Agent Based Model Practical 8

1. Introduction and Preparation

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'.

2. Setting up Tkinter

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.

3. Organising the GUI

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.