Why can't matplotlib plot in a different thread?

As other people have told, Matplotlib is not thread safe, one option you have is to use multiprocessing. You say that this is not good for you, because you need access to the axes from different process, but you can overcome this by sharing data between the simulation process and the root process and then managing all the plotting related activities in the root process. For example

import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
import multiprocessing
import time
import random
from Tkinter import *


#Create a window
window=Tk()



def main():
    #Create a queue to share data between process
    q = multiprocessing.Queue()

    #Create and start the simulation process
    simulate=multiprocessing.Process(None,simulation,args=(q,))
    simulate.start()

    #Create the base plot
    plot()

    #Call a function to update the plot when there is new data
    updateplot(q)

    window.mainloop()
    print 'Done'


def plot():    #Function to create the base plot, make sure to make global the lines, axes, canvas and any part that you would want to update later

    global line,ax,canvas
    fig = matplotlib.figure.Figure()
    ax = fig.add_subplot(1,1,1)
    canvas = FigureCanvasTkAgg(fig, master=window)
    canvas.show()
    canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)
    canvas._tkcanvas.pack(side=TOP, fill=BOTH, expand=1)
    line, = ax.plot([1,2,3], [1,2,10])




def updateplot(q):
    try:       #Try to check if there is data in the queue
        result=q.get_nowait()

        if result !='Q':
             print result
                 #here get crazy with the plotting, you have access to all the global variables that you defined in the plot function, and have the data that the simulation sent.
             line.set_ydata([1,result,10])
             ax.draw_artist(line)
             canvas.draw()
             window.after(500,updateplot,q)
        else:
             print 'done'
    except:
        print "empty"
        window.after(500,updateplot,q)


def simulation(q):
    iterations = xrange(100)
    for i in iterations:
        if not i % 10:
            time.sleep(1)
                #here send any data you want to send to the other process, can be any pickable object
            q.put(random.randint(1,10))
    q.put('Q')

if __name__ == '__main__':
    main()

I had a similar problem where I wanted to update a mapltolib plot from a different thread, and I am posting my solution here in case others have a similar problem in the future.

As noted the tkagg are not threading safe so you must make sure all calls to matplotlib are from a single thread. This means that the threads must communicate, so that the 'plotting thread' always executes matplotlib functions.

My solution was to create a decorator, that will execute all decorated functions in the 'plotting thread', and then to decorate all the relevant functions. This allows you to do what you want without any change to syntax in the main code.

i.e. when you call ax.plot(...) in one thread, you will have it automatically executed in a different thread.

import matplotlib.pyplot as plt
import matplotlib
import threading
import time
import queue
import functools


#ript(Run In Plotting Thread) decorator
def ript(function):
    def ript_this(*args, **kwargs):
        global send_queue, return_queue, plot_thread
        if threading.currentThread() == plot_thread: #if called from the plotting thread -> execute
            return function(*args, **kwargs)
        else: #if called from a diffrent thread -> send function to queue
            send_queue.put(functools.partial(function, *args, **kwargs))
            return_parameters = return_queue.get(True) # blocking (wait for return value)
            return return_parameters
    return ript_this

#list functions in matplotlib you will use
functions_to_decorate = [[matplotlib.axes.Axes,'plot'],
                         [matplotlib.figure.Figure,'savefig'],
                         [matplotlib.backends.backend_tkagg.FigureCanvasTkAgg,'draw'],
                         ]
#add the decorator to the functions
for function in functions_to_decorate:
    setattr(function[0], function[1], ript(getattr(function[0], function[1])))

# function that checks the send_queue and executes any functions found
def update_figure(window, send_queue, return_queue):
    try:
        callback = send_queue.get(False)  # get function from queue, false=doesn't block
        return_parameters = callback() # run function from queue
        return_queue.put(return_parameters)
    except:
        None
    window.after(10, update_figure, window, send_queue, return_queue)

# function to start plot thread
def plot():
    # we use these global variables because we need to access them from within the decorator
    global plot_thread, send_queue, return_queue
    return_queue = queue.Queue()
    send_queue = queue.Queue()
    plot_thread=threading.currentThread()
    # we use these global variables because we need to access them from the main thread
    global ax, fig
    fig, ax = plt.subplots()
    # we need the matplotlib window in order to access the main loop
    window=plt.get_current_fig_manager().window
    # we use window.after to check the queue periodically
    window.after(10, update_figure, window, send_queue, return_queue)
    # we start the main loop with plt.plot()
    plt.show()


def main():
    #start the plot and open the window
    thread = threading.Thread(target=plot)
    thread.setDaemon(True)
    thread.start()
    time.sleep(1) #we need the other thread to set 'fig' and 'ax' before we continue
    #run the simulation and add things to the plot
    global ax, fig
    for i in range(10):
        ax.plot([1,i+1], [1,(i+1)**0.5])
        fig.canvas.draw()
        fig.savefig('updated_figure.png')
        time.sleep(1)
    print('Done')
    thread.join() #wait for user to close window
main()

Note that if you forget to decorate any functions, you may get a segmentation fault.

Also, in this example the child thread handles the plot and the main thread the simulation. In general it is advised to do the reverse, (i.e. let the main thread have the graphics).


The simplest answer probably is:

Because the backends aren't thread safe. Most GUI frameworks rely on calling "GUI" methods/functions from only one thread ("gui thread") and require more advanced methods when communicating with different threads ("worker threads").

You can find this in the documentation for Qt (PyQt/PySide), wxWidgets and (didn't find a more official source) Tkinter.