Python从线程更新Matplotlib [英] Python update Matplotlib from threads

查看:1328
本文介绍了Python从线程更新Matplotlib的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对python世界还很陌生,不幸的是我还没有找到任何解决方案.

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

我正在Mac OS X 10.13.2上以matplotlib == 1.3.1运行python 3.6

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

当前,我正在尝试构建一个小型软件,该软件以4Hz的频率获取数据并以1Hz的频率在图中显示所获取的数据.因此,我创建了一个在线程中运行以获取数据的类,并创建了另一个类以更新实际图.在它们之间有一个数据类,它将保存数据,并用作这两个类之间的接口.

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.

仅创建图并使用plt.show()命令的简单示例可以很好地工作.

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.

谢谢!

此处介绍的解决方案如何在matplotlib中更新图?对我不起作用,因为我不想局限于一定数量的帧(使用Matplotlib动画框架).我需要连续以1Hz的频率更新图.

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.

图形未显示

推荐答案

我认为您不能在主线程之外运行matplotlib GUI.因此,将绘图保留在主线程中,并使用FuncAnimation来控制绘图,以下似乎可以正常工作.

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.

由于while True循环,即使关闭了窗口,它也将永远运行,因此对于任何实际应用程序,仍应对其进行调整.

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()

这篇关于Python从线程更新Matplotlib的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆