3

I have a program that has 2 very simple threads:

  • One for listening to serial port
  • One for a text UI

I also have a matplotlib animation running in my main(). It has a scope class that is the example from matplotlib.

When the program starts to run, it shows the plot and everything is OK. The problem is that as soon as the user enters a key, the program crashes and python exits with a fatal error.

The ui thread has nothing to do with matplotlib and scope class. If I delete the code that creates the plots, the ui thread has no problem and program runs smoothly. I also notice matplotlib on my system uses tkinter for creating windows.

Do you have any hints or experiences with why the matplotlib animation causes problem? Can't a thread be used with a matplotlib plot?

I am running this in a command line window in Windows7 with Python 2.7.

matplotlib version : 2.0.2 Tkinter version : 8.5

Error:

Fatal Python error: GC object already tracked

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

Or this error:

TclStackFree: incorrect freePtr. Call out of sequence?

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

Code:

    import threading
import serial
from matplotlib.lines import Line2D
import matplotlib.pyplot as plt
import matplotlib.animation as animation

class listner(threading.Thread):

        def __init__(self,port):
            threading.Thread.__init__(self)
            self.sport=None
            self.is_running=True
            self.init_port(port)

        def run(self):
            print ' Reading from Port'
            while self.is_running:
                try:
                    self.sport.read(1)
                except:
                    print 'Error reading port'

        def init_port(self,port):
            print '1'
            if self.sport==None or not self.sport.is_open :
                try:

                    self.sport = serial.Serial(port,115200)
                    self.sport.timeout = 1
                    self.sport.reset_input_buffer()
                    self.sport.reset_output_buffer()
                    self.port_open=True
                except:
                    print "    Port error Listener Initing\n",self.port_open,'\n',self.sport
            else:
                pass

        def process(self):
            pass


class ui(threading.Thread):

        def __init__(self):
            threading.Thread.__init__(self)     
            self.running = True

        def run(self):

            print 'Starting UI:\n'
            while self.running:
                print ' Enter input ''S'':\n'
                user = raw_input()



def main(port):

        listner_thread = None
        try:
            listner_thread = listner(port)
            listner_thread.start();
        except:
            print "Listener Thread Failed To Start"
            return

        ui_thread=None
        try:
            ui_thread = ui()
            ui_thread.start()          
        except:
            print "UI Thread Failed To Start"
            return

        run_charts()



def run_charts():
        fig, (ax1, ax2) = plt.subplots(2, 1)

        scope1 = Scope(ax1)
        ani1 = animation.FuncAnimation(fig, scope1.update, emit_ch1, interval=10,blit=True)

        scope2 = Scope(ax2)
        ani2 = animation.FuncAnimation(fig, scope2.update, emit_ch2, interval=10,blit=True)

        plt.show()

def emit_ch1():
    yield 0.001

def emit_ch2():
    yield -0.001

class Scope(object):
        def __init__(self, ax, maxt=2, dt=0.02):
            self.ax = ax
            self.dt = dt
            self.maxt = maxt
            self.tdata = [0]
            self.ydata = [0]
            self.line = Line2D(self.tdata, self.ydata)
            self.ax.add_line(self.line)
            self.ax.set_ylim(-.009, 0.009)
            self.ax.set_xlim(0, self.maxt)

        def update(self, y):
            t = self.tdata[-1] + self.dt
            self.tdata.append(t)
            self.ydata.append(y)
            self.line.set_data(self.tdata, self.ydata)
            return self.line,



if __name__ == '__main__':
        main('COM11')
doubleE
  • 1,027
  • 1
  • 12
  • 32
  • It may be easier for people to debug this if you provide a more minimal example that still has this problem. See https://stackoverflow.com/help/mcve – timotree Jun 17 '17 at 04:00
  • When I put this code in python I get the error: `AttributeError: type object 'listner' has no attribute 'listner'` because of this line: `listner_thread = listner.listner(port)` Are you sure the code you provided is the code which works until the user hits a key? – timotree Jun 17 '17 at 04:25
  • I updated the code. This is fully working and reproduce the problem. – doubleE Jun 19 '17 at 17:51

1 Answers1

2

The first error, "Fatal Python Error:GC object Already Tracked" was closed in 2013 with a status of "CLOSED WONTFIX" See the bug report on Bugzilla

It seems the interim fix when it was raised again in 2015 [in relation to dask] was to use a single thread using this code:

   import dask
   dask.set_options(get=dask.async.get_sync)

but that the problem actually lay with dataframe.read_csv problems.

The problem was eventually solved in a later version of pandas. If you upgrade your version of matplotlib, the problem will most likely be resolved also with a similar fix.

Hope this helps

Rachel Gallen
  • 27,943
  • 21
  • 72
  • 81
  • Thanks for info. I guess something is wrong with `Tkinter`. I ran the same code on another machine which used a `MPL WebAgg` GUI framework and it ran smoothly. It opens the graph in browser however which was not desirable. – doubleE Aug 07 '17 at 18:08
  • 1
    @dandikain Insert this line 'matplotlib.use('Agg')' before you import pyplot to avoid a window opening - [Source](http://matplotlib.org/1.4.2/faq/howto_faq.html#howto-batch) Also recommended to save your chart before show() too e.g. plt.savefig('fig') – Rachel Gallen Aug 07 '17 at 20:05
  • Tried that and put it the very start, but it doesn't take effect and warns that I should put it before calling plot!(which I do, I called it before everything.) – doubleE Aug 07 '17 at 22:04
  • According to the code above, it should be line 4, and then import pyplot. (As opposed to putting the line at the very start). Did you try that? – Rachel Gallen Aug 07 '17 at 22:13
  • Yes I did that. It warned with following: `c:\Python27\lib\site-packages\matplotlib\__init__.py:1405: UserWarning: This call to matplotlib.use() has no effect because the backend has already been chosen; matplotlib.use() must be called *before* pylab, matplotlib.pyplot, or matplotlib.backends is imported for the first time.` – doubleE Aug 07 '17 at 22:28
  • 1
    @dandikain can you not debug your code to find WHERE it is being imported for the first time then (as evidently it has being imported before this piece of code)... and insert the line there? Other than that I'm out of suggestions. Hope you resolve the issue. Glad it's partially resolved. – Rachel Gallen Aug 07 '17 at 22:42