40

I'm asking this question because I can't solve one problem in Python/Django (actually in pure Python it's ok) which leads to RuntimeError: tcl_asyncdelete async handler deleted by the wrong thread. This is somehow related to the way how I render matplotlib plots in Django. The way I do it is:

...
import matplotlib.pyplot as plt
...
fig = plt.figure()
...
plt.close()

I extremely minimized my code. But the catch is - even if I have just one line of code:

fig = plt.figure()

I see this RuntimeError happening. I hope I could solve the problem, If I knew the correct way of closing/cleaning/destroying plots in Python/Django.

garg10may
  • 5,794
  • 11
  • 50
  • 91
Jacobian
  • 10,122
  • 29
  • 128
  • 221
  • Can you post the traceback? – knbk Nov 26 '14 at 14:04
  • I'm having the same issue with Pyramid 1.3.4 / Python 2.7, there's no traceback just this -- Exception RuntimeError: RuntimeError('main thread is not in main loop',) in > ignored Tcl_AsyncDelete: async handler deleted by the wrong thread – Alex Volkov Mar 20 '15 at 16:34

5 Answers5

86

By default matplotlib uses TK gui toolkit, when you're rendering an image without using the toolkit (i.e. into a file or a string), matplotlib still instantiates a window that doesn't get displayed, causing all kinds of problems. In order to avoid that, you should use an Agg backend. It can be activated like so --

import matplotlib
matplotlib.use('Agg')
from matplotlib import pyplot

For more information please refer to matplotlib documentation -- http://matplotlib.org/faq/howto_faq.html#matplotlib-in-a-web-application-server

Alex Volkov
  • 2,812
  • 23
  • 27
  • No, I do not render them to an image. I encode them with base64 and render in browser. – Jacobian Mar 20 '15 at 17:40
  • I mean that you're not using TK toolkit, I'll clarify my answer. – Alex Volkov Mar 20 '15 at 20:06
  • 2
    A bit late, but this solved a very subtle and annoying bug for me. Thanks! The error message from pyplot could certainly be more helpful in this regard. – nslamberth Jul 02 '18 at 21:59
  • 2
    @AlexVolkov thxs so much this solved such a cryptic error for me. Nothing just `Tcl_AsyncDelete: async handler deleted by the wrong thread` and I was using flask, matplotlib, python-pptx. I wasn't even aware `matplotlib` is causing this. – garg10may Feb 06 '19 at 08:25
16

The above (accepted) answer is a solution in a terminal environment. If you debug in an IDE, you still might wanna use 'TkAgg' for displaying data. In order to prevent this issue, apply these two simple rules:

  1. everytime you display your data, initiate a new fig = plt.figure()
  2. don't close old figures manually (e.g. when using a debug mode)

Example code:

import matplotlib
matplotlib.use('TkAgg')
from matplotlib import pyplot as plt

fig = plt.figure()
plt.plot(data[:,:,:3])
plt.show()

This proves to be the a good intermediate solution under MacOS and PyCharm IDE.

whiletrue
  • 10,500
  • 6
  • 27
  • 47
0

If you don't need to show plots while debugging, the following works:

import matplotlib
matplotlib.use('Agg')
from matplotlib import pyplot as plt

However, if you would like to plot while debugging, you need to do 3 steps:

1.Keep backend to 'TKAgg' as follows:

import matplotlib
matplotlib.use('TKAgg')
from matplot.lib import pyplot as plt

or simply

import matplotlib.pyplot as plt

2.As Fábio also mentioned, you need to add fig(no. #i)=plt.figure(no.#i) for each figure #i. As the following example for plot no.#1, add:

fig1 = plt.figure(1)
plt.plot(yourX,yourY)
plt.show()

3.Add breakpoints. You need to add two breakpoints at least, one somewhere at the beginning of your codes (before the first plot), and the other breakpoint at a point where you would like all plots (before to the second breakpoint) are plotted. All figures are plotted and you even don't need to close any figure manually.

Samtry
  • 35
  • 4
0

For me, this happened due to parallel access to data by both Matplotlib and by Tensorboard, after Tensorboard's server was running for a week straight.

Rebotting tensorboard tensorboard --logdir . --samples_per_plugin images=100 solved this for me.

Gulzar
  • 23,452
  • 27
  • 113
  • 201
0

I encountered this problem when plotting graphs live with matplotlib in my tkinter application.

The easiest solution I found, was to always delete subplots. I found you didn't need to instantiate a new figure, you only needed to delete the old subplot (using del subplot), then remake it.

Before plotting a new graph, make sure to delete the old subplot. Example:

f = Figure(figsize=(5,5), dpi=100)
a = f.add_subplot(111)
(For Loop code that updates graph every 5 seconds):
    del a #delete subplot
    a = f.add_subplot(111) #redefine subplot

Finding this simple solution to fix this "async handler bug" was excruciatingly painful, I hope this helps someone else :)