Python update Matplotlib from threads

廉价感情. 提交于 2019-12-03 22:25:00

问题


I'm pretty new to the python world and unfortunately I could not find any solution to this yet.

I'm running python 3.6 with matplotlib==1.3.1 on Mac OS X 10.13.2

Currently I'm trying to build a small software which fetches data at 4Hz and displays the fetched data in a plot at 1Hz. Therefore I created a class which runs in a thread to fetch the data and another class to update the actual plots. In-between there is a data class which will hold the data and is used as interface inbetween the two classes.

import matplotlib.pyplot as plt
import numpy as np
import threading
import random
import time

class MyDataClass():

    def __init__(self):

        self.XData = [0]
        self.YData = [0]


class MyPlotClass(threading.Thread):

    def __init__(self, dataClass):

        threading.Thread.__init__(self)

        self._dataClass = dataClass
        self._period = 1
        self._nextCall = time.time()

        self.hLine, = plt.plot(0, 0)

        plt.ion()

    def run(self):

        while True:
            self.hLine.set_data(self._dataClass.XData, self._dataClass.YData)
            plt.draw()
            print("updated %i datapoints" % len(self._dataClass.XData))
            # sleep until next execution
            self._nextCall = self._nextCall + self._period
            time.sleep(self._nextCall - time.time())


class MyDataFetchClass(threading.Thread):

    def __init__(self, dataClass):

        threading.Thread.__init__(self)

        self._dataClass = dataClass
        self._period = 0.25
        self._nextCall = time.time()

    def run(self):

        while True:
            # add data to data class
            self._dataClass.XData.append(self._dataClass.XData[-1] + 1)
            self._dataClass.YData.append(random.randint(0, 256))
            print("Added (%i, %i)" % (self._dataClass.XData[-1], self._dataClass.YData[-1]))
            # sleep until next execution
            self._nextCall = self._nextCall + self._period
            time.sleep(self._nextCall - time.time())


data = MyDataClass()
fetcher = MyDataFetchClass(data)
plotter = MyPlotClass(data)

fetcher.start()
plotter.start()

fetcher.join()
plotter.join()

I can see that the threads are running due to the command line output. But for some reason the figure holding the plots won't show up.

The rocket symbol will just bounce up and down instead of showing up. See attached Screenshot.

Simple examples where the plot is only created ones and the plt.show() command is used works fine.

I can't figure out what I'm doing wrong. I hope anyone of you might have an idea.

Thanks!

Edit: Solutions presented here How to update a plot in matplotlib? will not work for me because I don't want to be limited to a certain number of frames (using the animation framework of Matplotlib). I need to update the plot with 1Hz continiously.

Figure not showing up


回答1:


I don't think you can run matplotlib GUI outside the main thread. So keeping the plotting in the main thread and using a FuncAnimation to steer the plotting, the following seems to work fine.

Due to the while True loop it will run forever, even after closing the window, so for any real world application this should still be adjusted.

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
import threading
import random
import time

class MyDataClass():

    def __init__(self):

        self.XData = [0]
        self.YData = [0]


class MyPlotClass():

    def __init__(self, dataClass):

        self._dataClass = dataClass

        self.hLine, = plt.plot(0, 0)

        self.ani = FuncAnimation(plt.gcf(), self.run, interval = 1000, repeat=True)


    def run(self, i):  
        print("plotting data")
        self.hLine.set_data(self._dataClass.XData, self._dataClass.YData)
        self.hLine.axes.relim()
        self.hLine.axes.autoscale_view()


class MyDataFetchClass(threading.Thread):

    def __init__(self, dataClass):

        threading.Thread.__init__(self)

        self._dataClass = dataClass
        self._period = 0.25
        self._nextCall = time.time()


    def run(self):

        while True:
            print("updating data")
            # add data to data class
            self._dataClass.XData.append(self._dataClass.XData[-1] + 1)
            self._dataClass.YData.append(random.randint(0, 256))
            # sleep until next execution
            self._nextCall = self._nextCall + self._period;
            time.sleep(self._nextCall - time.time())


data = MyDataClass()
plotter = MyPlotClass(data)
fetcher = MyDataFetchClass(data)

fetcher.start()
plt.show()
#fetcher.join()


来源:https://stackoverflow.com/questions/48389470/python-update-matplotlib-from-threads

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!