Liveplotter can't be run multiple times


#1

If I create a plot using start_liveplotter, then close it, I get the following exception:

In [80]: Exception in thread Thread-10:
Traceback (most recent call last):
  File "/usr/lib/python3.5/threading.py", line 914, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.5/threading.py", line 862, in run
    self._target(*self._args, **self._kwargs)
  File "/home/alex/.local/lib/python3.5/site-packages/odrive/utils.py", line 74, in plot_data
    fig.canvas.start_event_loop(1/plot_rate)
  File "/home/alex/.local/lib/python3.5/site-packages/matplotlib/backend_bases.py", line 2251, in start_event_loop
    self.flush_events()
  File "/home/alex/.local/lib/python3.5/site-packages/matplotlib/backends/_backend_tk.py", line 491, in flush_events
    self._master.update()
  File "/usr/lib/python3.5/tkinter/__init__.py", line 1028, in update
    self.tk.call('update')
_tkinter.TclError: can't invoke "update" command: application has been destroyed

odrivetool still works, however if I try to start another plot, the whole thing crashes with the following error:

In [80]: start_liveplotter(lambda: [odrv0.axis0.encoder.vel_estimate])
Out[80]: <fibre.utils.Event at 0x7f7964edf470>

In [81]: Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7f7968fa54a8>>
Traceback (most recent call last):
  File "/usr/lib/python3.5/tkinter/__init__.py", line 3359, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Tcl_AsyncDelete: async handler deleted by the wrong thread
Aborted (core dumped)

This is on Ubuntu 16.04.


#2

Yeah, the plotting stuff doesn’t like to live on anything but the main thread. However the main thread also is where live commands are parsed. So right now I just kill odrivetool and restart it again every time I want to use the liveplotter. This isn’t a big deal since odrivetool doesn’t really have any state, it’s all on the ODrive.

That said, of course the user experience isn’t great. I think a good solution would be to run the liveplotter in a different process using the python multiprocessing module. If you are interested in looking into that that would be very welcomed.