3

I'm trying to plot live the output of a generator.

The following code works as expected (Ctrl-C terminates execution):

import numpy as np
import pylab as p
from Queue import Queue
from threading import Thread
import time

def dataGenerator():
    while True:
        yield np.random.random()

def populate():
    f = dataGenerator()
    while True:
        x = f.next(); y = f.next()
        q.put([x,y])

q = Queue()

p.figure(); p.hold(True); p.show(block=False)

populatorThread = Thread(target=populate)
populatorThread.daemon = True
populatorThread.start()

while True:
    data = q.get()
    x = data[0]
    y = data[1]
    p.plot(x,y,'o')
    p.draw()
    q.task_done()

populatorThread.join()

However, if instead I put the plotting in a thread, I get RuntimeError: main thread is not in main loop:

import numpy as np
import pylab as p
from Queue import Queue
from threading import Thread
import time

def dataGenerator():
    while True:
        yield np.random.random()

def plotter():
    while True:
        data = q.get()
        x = data[0]
        y = data[1]
        p.plot(x,y,'o')
        p.draw()
        print x,y
        q.task_done()

q = Queue()

p.figure(); p.hold(True); p.show(block=False)

plotThread = Thread(target=plotter)
plotThread.daemon = True
plotThread.start()

f = dataGenerator()
while True:
    x = f.next()
    y = f.next()
    q.put([x,y])

plotThread.join()

Why does matplotlib care which thread does the plotting?

EDIT: I'm not asking how to solve this but rather why is this happening in the first place.

Sparkler
  • 2,581
  • 1
  • 22
  • 41
  • possible duplicate of [RuntimeError: main thread is not in main loop](http://stackoverflow.com/questions/14694408/runtimeerror-main-thread-is-not-in-main-loop) –  Jul 17 '15 at 02:18

2 Answers2

3

It's probably the GUI that you're using for backend. The GUI likely expects to find itself in the main thread, but it isn't when matplotlib calls get_current_fig_manager().canvas.draw().

For example, when I do this, I get the following traceback:

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 810, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 763, in run
    self.__target(*self.__args, **self.__kwargs)
  File "tmp.py", line 18, in plotter
    p.draw()
  File "/usr/lib/pymodules/python2.7/matplotlib/pyplot.py", line 555, in draw
    get_current_fig_manager().canvas.draw()
  File "/usr/lib/pymodules/python2.7/matplotlib/backends/backend_tkagg.py", line 349, in draw
    tkagg.blit(self._tkphoto, self.renderer._renderer, colormode=2)
  File "/usr/lib/pymodules/python2.7/matplotlib/backends/tkagg.py", line 13, in blit
    tk.call("PyAggImagePhoto", photoimage, id(aggimage), colormode, id(bbox_array))
RuntimeError: main thread is not in main loop

Note the tk.call(...) line. The exception you get is not raised from matplotlib, it's raised from TkInter.

  • then why TkInter cares if plotting is in the main loop or not? – Sparkler Jul 16 '15 at 04:16
  • @Sparkler what gui/backend are you using yourself? –  Jul 16 '15 at 04:43
  • The default, I guess it's TkInter. – Sparkler Jul 16 '15 at 20:17
  • 1
    @Sparkler In that case, you may want to have a look at [some](http://stackoverflow.com/questions/10556479/running-a-tkinter-form-in-a-separate-thread) other [questions](http://stackoverflow.com/questions/14694408/runtimeerror-main-thread-is-not-in-main-loop) here on SO. Or read through one the [issues opened](https://bugs.python.org/issue11077) about this. –  Jul 17 '15 at 02:21
  • I'm not asking how to solve this but rather why is this happening in the first place. – Sparkler Jul 17 '15 at 05:02
  • @Sparkler I doubt there's a definite answer to that question. It's probably how TkInter has evolved, and potentially even goes back to Tcl/Tk. Possibly threading wasn't really taught about when it was conceived, or made the code base much complicated than just running it in the main thread. My answer is just about what's causing it, not why. –  Jul 17 '15 at 05:24
1

Why does matplotlib care which thread does the plotting? I'm not asking how to solve this but rather why is this happening in the first place.

@Evert is right, it's not matplotlib, it's your GUI toolkit (one of the backends that matplotlib uses to create a window with a plot for you). It happens because GUI toolkits are event-driven (you don't want blocking behavior for the user interface, right?) and they have internal event loop, that controls program execution. The idea is that events are monitored by the event loop and dispatched to the callbacks. To do this, event loop should be started in the main thread, while callbacks for long-running tasks are moved to separate threads.

wombatonfire
  • 4,585
  • 28
  • 36