使用Python和tkinter实时绘制串行数据

3

我已经花了一些时间来寻找一种方法,使用Python GUI绘制来自Arduino的传入数据的图形。我使用Matplotlib动画函数成功地实现了这一点,读取6个不同的变量并绘制其中4个,分别放在两个子图中。这可以快速完成,以至于它可以实时绘制(每秒20个样本)。

现在我需要修改系统,同时读取12个不同的变量,其中8个被绘制。4个在一个子图上,另外4个在另一个子图上,而且绘图速率应该保持在每秒20个样本。我尝试了一些不同的方法并进行了很多研究,但是在有限的Python知识下无法解决问题。我对多进程或多线程并不是很熟悉,但它们似乎是人们能够加快绘图过程的方式。我知道Matplotlib的动画函数本身是线程化的,因此我不确定线程化会对其有多大帮助,或者是否有一种方法在一个线程中读取数据并在另一个线程中更新图形。我正在使用Arduino支持的最高波特率250000。我还找到了一个示例,其中有人能够在这篇文章中获得非常高速的绘图,但尚未能够修改以适应我的使用: What is the best real time plotting widget for wxPython?

数据从Arduino接收如下:

integer.integer.integer|integer.integer.integer|integer.integer.integer|integer.integer.integer

其中管道表示一个新的执行器(我发送的每个变量来自哪里)

我对Python还比较新,所以如果这不是很符合Python语言习惯,请见谅。以下是两个示例:

这是一个使用动画函数的GUI:

import Tkinter
import serial
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from matplotlib import pyplot as plt
import matplotlib.animation as animation
from collections import deque
import random

class App:
    def __init__(self, master):

        self.arduinoData = serial.Serial('com5', 250000)#115200)

        frame = Tkinter.Frame(master)

        self.running = False
        self.ani = None

        self.start = Tkinter.LabelFrame(frame, text="Start", borderwidth=10, relief=Tkinter.GROOVE, padx=10, pady=10)
        self.start.grid(row=0, column=0, padx=20, pady=20)

        self.run = Tkinter.Button(self.start, text="RUN", bd=10, height=5, width=10, command=self.getData)
        self.run.grid(row=0, column=0, padx=5, pady=5)

        self.stop_frame = Tkinter.LabelFrame(frame, text="STOP", borderwidth=10, relief=Tkinter.GROOVE, padx=10, pady=10 )
        self.stop_frame.grid(row=0, column=1, padx=20, pady=20)

        self.stop = Tkinter.Button(self.stop_frame, text="STOP", bd=10, height=5, width=10, command=self.stopTest)
        self.stop.grid(row=0, column=0, padx=5, pady=5)

        self.fig = plt.Figure()
        self.ax1 = self.fig.add_subplot(211)
        self.line0, = self.ax1.plot([], [], lw=2)
        self.line1, = self.ax1.plot([], [], lw=2)
        self.line2, = self.ax1.plot([], [], lw=2)
        self.line3, = self.ax1.plot([], [], lw=2)
        self.ax2 = self.fig.add_subplot(212)
        self.line4, = self.ax2.plot([], [], lw=2)
        self.line5, = self.ax2.plot([], [], lw=2)
        self.line6, = self.ax2.plot([], [], lw=2)
        self.line7, = self.ax2.plot([], [], lw=2)
        self.canvas = FigureCanvasTkAgg(self.fig,master=master)
        self.canvas.show()
        self.canvas.get_tk_widget().grid(row=0, column=4, padx=20, pady=20)
        frame.grid(row=0, column=0, padx=20, pady=20)

    def getData(self):
        if self.ani is None:
            self.k = 0
            self.arduinoData.flushInput()
            self.arduinoData.write("<L>")
            return self.start()
        else:
            self.arduinoData.write("<L>")
            self.arduinoData.flushInput()
            self.ani.event_source.start()
        self.running = not self.running

    def stopTest(self):
        self.arduinoData.write("<H>")
        if self.running:
            self.ani.event_source.stop()
        self.running = not self.running

    def resetTest(self):
        self.k = 0
        self.xdata = []
        self.pressure1 = []
        self.displacement1 = []
        self.cycle1 = []
        self.pressure2 = []
        self.displacement2 = []
        self.cycle2 = []
        self.pressure3 = []
        self.displacement3 = []
        self.cycle3 = []
        self.pressure4 = []
        self.displacement4 = []
        self.cycle4 = []
        self.line1.set_data(self.xdata, self.ydata1)
        self.line2.set_data(self.xdata, self.ydata2)
        self.ax1.set_ylim(0,1)
        self.ax1.set_xlim(0,1)
        self.ax2.set_ylim(0,1)
        self.ax2.set_xlim(0,1)

    def start(self):
        self.xdata = []
        self.pressure1 = []
        self.displacement1 = []
        self.cycle1 = []
        self.pressure2 = []
        self.displacement2 = []
        self.cycle2 = []
        self.pressure3 = []
        self.displacement3 = []
        self.cycle3 = []
        self.pressure4 = []
        self.displacement4 = []
        self.cycle4 = []
        self.k = 0
        self.arduinoData.flushInput()
        self.ani = animation.FuncAnimation(
            self.fig,
            self.update_graph,
            interval=1,
            repeat=True)
        self.arduinoData.write("<L>")
        self.running = True
        self.ani._start()

    def update_graph(self, i):
        self.xdata.append(self.k)
        while (self.arduinoData.inWaiting()==0):
            pass
        x = self.arduinoData.readline()
        strip_data = x.strip()
        split_data = x.split("|")
        actuator1 = split_data[0].split(".")
        actuator2 = split_data[1].split(".")
        actuator3 = split_data[2].split(".")
        actuator4 = split_data[3].split(".")
        self.pressure1.append(int(actuator1[0]))
        self.displacement1.append(int(actuator1[1]))
        self.cycle1 = int(actuator1[2])
        self.pressure2.append(int(actuator2[0]))
        self.displacement2.append(int(actuator2[1]))
        self.cycle2 = int(actuator2[2])
        self.pressure3.append(int(actuator3[0]))
        self.displacement3.append(int(actuator3[1]))
        self.cycle3 = int(actuator3[2])
        self.pressure4.append(int(actuator4[0]))
        self.displacement4.append(int(actuator4[1]))
        self.cycle4 = int(actuator4[2])
        self.line0.set_data(self.xdata, self.pressure1)
        self.line1.set_data(self.xdata, self.pressure2)
        self.line2.set_data(self.xdata, self.pressure3)
        self.line3.set_data(self.xdata, self.pressure4)
        self.line4.set_data(self.xdata, self.displacement1)
        self.line5.set_data(self.xdata, self.displacement2)
        self.line6.set_data(self.xdata, self.displacement3)
        self.line7.set_data(self.xdata, self.displacement4)
        if self.k < 49:
            self.ax1.set_ylim(min(self.pressure1)-1, max(self.pressure3) + 1)
            self.ax1.set_xlim(0, self.k+1)
            self.ax2.set_ylim(min(self.displacement1)-1, max(self.displacement3) + 1)
            self.ax2.set_xlim(0, self.k+1)
        elif self.k >= 49:
            self.ax1.set_ylim(min(self.pressure1[self.k-49:self.k])-1, max(self.pressure3[self.k-49:self.k]) + 1)
            self.ax1.set_xlim(self.xdata[self.k-49], self.xdata[self.k-1])
            self.ax2.set_ylim(min(self.displacement1[self.k-49:self.k])-1, max(self.displacement3[self.k-49:self.k]) + 1)
            self.ax2.set_xlim(self.xdata[self.k-49], self.xdata[self.k-1])
        self.k += 1




root = Tkinter.Tk()
app = App(root)
root.mainloop()

这是一个能够打印到监视器上的图形用户界面(GUI):
import Tkinter
import serial
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from matplotlib import pyplot as plt
import matplotlib.animation as animation
import time

class App:
    def __init__(self, master):

        self.arduinoData = serial.Serial('com5', 250000, timeout=0)

        frame = Tkinter.Frame(master)

        self.go = 0

        self.start = Tkinter.LabelFrame(frame, text="Start", borderwidth=10, relief=Tkinter.GROOVE, padx=10, pady=10)
        self.start.grid(row=0, column=0, padx=20, pady=20)

        self.run = Tkinter.Button(self.start, text="RUN", bd=10, height=5, width=10, command=self.getData)
        self.run.grid(row=0, column=0, padx=5, pady=5)

        self.stop_frame = Tkinter.LabelFrame(frame, text="STOP", borderwidth=10, relief=Tkinter.GROOVE, padx=10, pady=10 )
        self.stop_frame.grid(row=0, column=1, padx=20, pady=20)

        self.stop = Tkinter.Button(self.stop_frame, text="STOP", bd=10, height=5, width=10, command=self.stopTest)
        self.stop.grid(row=0, column=0, padx=5, pady=5)

        self.fig = plt.Figure()
        self.ax1 = self.fig.add_subplot(211)
        self.line0, = self.ax1.plot([], [], lw=2)
        self.line1, = self.ax1.plot([], [], lw=2)
        self.line2, = self.ax1.plot([], [], lw=2)
        self.line3, = self.ax1.plot([], [], lw=2)
        self.ax2 = self.fig.add_subplot(212)
        self.line4, = self.ax2.plot([], [], lw=2)
        self.line5, = self.ax2.plot([], [], lw=2)
        self.line6, = self.ax2.plot([], [], lw=2)
        self.line7, = self.ax2.plot([], [], lw=2)
        self.canvas = FigureCanvasTkAgg(self.fig,master=master)
        self.canvas.show()
        self.canvas.get_tk_widget().grid(row=0, column=4, padx=20, pady=20)
        frame.grid(row=0, column=0, padx=20, pady=20)

    def getData(self):
        self.k = 0
        self.xdata = []
        self.pressure1 = []
        self.displacement1 = []
        self.cycle1 = []
        self.pressure2 = []
        self.displacement2 = []
        self.cycle2 = []
        self.pressure3 = []
        self.displacement3 = []
        self.cycle3 = []
        self.pressure4 = []
        self.displacement4 = []
        self.cycle4 = []
        self.arduinoData.flushInput()
        self.go = 1
        self.readData()

    def readData(self):
        if self.go == 1:
            self.xdata.append(self.k)
            while (self.arduinoData.inWaiting()==0):
                pass
            x = self.arduinoData.readline()
            strip_data = x.strip()
            split_data = x.split("|")
            actuator1 = split_data[0].split(".")
            actuator2 = split_data[1].split(".")
            actuator3 = split_data[2].split(".")
            actuator4 = split_data[3].split(".")
            self.pressure1.append(int(actuator1[0]))
            self.displacement1.append(int(actuator1[1]))
            self.cycle1 = int(actuator1[2])
            self.pressure2.append(int(actuator2[0]))
            self.displacement2.append(int(actuator2[1]))
            self.cycle2 = int(actuator2[2])
            self.pressure3.append(int(actuator3[0]))
            self.displacement3.append(int(actuator3[1]))
            self.cycle3 = int(actuator3[2])
            self.pressure4.append(int(actuator4[0]))
            self.displacement4.append(int(actuator4[1]))
            self.cycle4 = int(actuator4[2])
            self.printData()
            root.after(0, self.readData)


    def printData(self):
        print str(self.pressure1[self.k-1]) + " " + 
        str(self.displacement1[self.k-1]) + " " + str(self.cycle1) + " " + 
        str(self.pressure2[self.k-1]) + " " + str(self.displacement2[self.k-
        1]) + " " + str(self.cycle2) + " " + str(self.pressure3[self.k-1]) + 
        " " + str(self.displacement3[self.k-1]) + " " + str(self.cycle3) + " 
        " + str(self.pressure4[self.k-1]) + " " + 
        str(self.displacement4[self.k-1]) + " " + str(self.cycle4)

    def stopTest(self):
        self.arduinoData.write("<H>")
        self.go = 0


    def resetTest(self):
        self.k = 0
        self.xdata = []
        self.pressure1 = []
        self.displacement1 = []
        self.cycle1 = []
        self.pressure2 = []
        self.displacement2 = []
        self.cycle2 = []
        self.pressure3 = []
        self.displacement3 = []
        self.cycle3 = []
        self.pressure4 = []
        self.displacement4 = []
        self.cycle4 = []
        self.line1.set_data(self.xdata, self.ydata1)
        self.line2.set_data(self.xdata, self.ydata2)
        self.ax1.set_ylim(0,1)
        self.ax1.set_xlim(0,1)
        self.ax2.set_ylim(0,1)
        self.ax2.set_xlim(0,1)

    def start(self):
        self.xdata = []
        self.pressure1 = []
        self.displacement1 = []
        self.cycle1 = []
        self.pressure2 = []
        self.displacement2 = []
        self.cycle2 = []
        self.pressure3 = []
        self.displacement3 = []
        self.cycle3 = []
        self.pressure4 = []
        self.displacement4 = []
        self.cycle4 = []
        self.k = 0
        self.arduinoData.write("<L>")

root = Tkinter.Tk()
app = App(root)
root.mainloop()

以下是一个Arduino代码示例:

int analog0 = 0;
int analog1 = 1;
int analog2 = 2;

int sensor0;
int sensor1;
int sensor2;

String pot0;
String pot1;
String Force;

int pot0holder;
int pot1holder;
String Forceholder;

unsigned long i = 0;
String Is;

int val = 0;

boolean Sensordata = false;
int cycles;

const byte numChars = 32;
char receivedChars[numChars];
boolean newData = false;

unsigned long CurrentMillis = 0;
unsigned long PrintMillis = 0;
int PrintValMillis = 50;
unsigned long SensorMillis = 0;
int SensorValMillis = 0;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(250000);
}

void loop()
{
  CurrentMillis = millis();
  recvWithStartEndMarkers();
  commands();
  sensordata();
}

void sensordata()
{
  if (CurrentMillis - SensorMillis >= SensorValMillis)
  {
    sensor0 = analogRead(analog0);
    pot0holder = sensor0;
    sensor1 = analogRead(analog1);
    pot1holder = sensor1;
    i += 1;
    String potcolumn = String(pot0holder) + "." + String(pot1holder) + "." +  String(i) + "|" + String(int(pot0holder)+30) + "." + String(int(pot1holder)+30) + "." +  String(i) + "|" + String(int(pot0holder)+60) + "." + String(int(pot1holder)+60) + "." +  String(i) + "|" + String(int(pot0holder)+90) + "." + String(int(pot1holder)+90) + "." +  String(i);
    Serial.println(potcolumn);
    SensorMillis += SensorValMillis;
   }
}

void recvWithStartEndMarkers()
{
    static boolean recvInProgress = false; //creates variable visible to only one function with boolean
    static byte ndx = 0;
    char startMarker = '<'; //sets begin condition
    char endMarker = '>'; //sets end condition
    char rc; //sets variable type to char

    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read(); //sets rc equal to serial value

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }
        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

void commands()
{
  if (newData == true)
  {
    if (receivedChars[0] == 'T')
    {
      PrintValMillis = atoi(&receivedChars[1]); //atoi -> Converting strings to integer
    }
    else if (receivedChars[0] == 'S')
    {
      cycles = atoi(&receivedChars[1]);
      i = 0;
    }
        else if (receivedChars[0] == 'L')
    {
      val = atoi(&receivedChars[1]);
      i = 0;
    }
  }
  newData = false;
}

非常感谢任何人提供的帮助和建议。


在对代码进行分析后,我发现在图表的前300次迭代中,更新方程的平均时间为0.0429900026321秒。这应该还有充足的时间,但即使图形只需要每0.05秒更新一次,它仍然落后了。 - emg184
首先你需要找到瓶颈所在。是数据读取还是绘图?然后你可以把它们放在单独的进程中,其中读取器将管道提供给打印机。 - RaJa
此外,您应该对代码进行一些优化:可以使用"sep".join([str1, str2, ...])来加速printData函数,其中sep是您的空格。 实时matplot绘图,您应该查看https://dev59.com/2Gct5IYBdhLWcg3wuPq6 - RaJa
在超过300次迭代中,我发现代码执行的平均时间为0.040306673050,其中读取功能需要0.039366672834,而设置行和轴数据需要0.000940000216。@RaJa - emg184
我只是使用printData函数来查看我是否能够快速读取数据。通过我发布的代码,我能够快速读取数据并打印出来,然后我想看看在确认我能够快速读取和至少打印数据之后,是否能够绘制数据图表。对于没有解释清楚这一点,我很抱歉@RaJa。 - emg184
2个回答

0

所以你的阅读过程占用了大部分时间。我会将阅读放在一个单独的任务中,在主(绘图)进程中进行数据的评估/拆分。不幸的是,我不是tkinter用户,所以我写这篇文章时没有使用任何特殊的GUI框架。但我认为你可以根据自己的需求进行调整。

这将看起来像这样:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import multiprocessing as mp
import time


# global variables
fig = plt.figure(1)
# first sub-plot
ax1 = fig.add_subplot(211)
line1, = ax1.plot([], [], lw=2)
ax1.grid()
xdata1, ydata1 = [], []
# second sub-plot
ax2 = fig.add_subplot(212)
line2, = ax2.plot([], [], lw=2)
ax2.grid()
xdata2, ydata2 = [], []

# the multiprocessing queue
q = mp.Queue()

# data generator in separate process
# here would be your arduino data reader
def dataGen(output):
    for x in range(50):
        output.put((x, np.sin(x)))

# update first subplot
def update1(data):
    # update the data
    t, y = data
    xdata1.append(t)
    ydata1.append(y)
    xmin, xmax = ax1.get_xlim()
    ymin, ymax = ax1.get_ylim()

    if t >= xmax:
        ax1.set_xlim(xmin, 2*xmax)
    if y >= ymax:
        ax1.set_ylim(ymin, 2*ymax)
    if y <= ymin:
        ax1.set_ylim(2*ymin, ymax)
    line1.set_data(xdata1, ydata1)

    return line1,

# update second subplot
def update2(data):
    # update the data
    t, y = data
    xdata2.append(t)
    ydata2.append(y)
    xmin, xmax = ax2.get_xlim()
    ymin, ymax = ax2.get_ylim()

    if t >= xmax:
        ax2.set_xlim(xmin, 2*xmax)
    if y >= ymax:
        ax2.set_ylim(ymin, 2*ymax)
    if y <= ymin:
        ax2.set_ylim(2*ymin, ymax) 
    line2.set_data(xdata2, ydata2)

    return line2,

# called at each drawing frame
def run(data):
    # get data from queue, which is filled in separate process, blocks until
    # data is available
    data = q.get(block=True, timeout=.5)
    # put here your variable separation
    data1 = (2*data[0], 3*data[1])
    data2 = (data[0], data[1])
    #provide the data to the plots
    a = update1(data1)
    b = update2(data2)
    fig.canvas.draw()
    return a+b

if __name__ == "__main__":
    # count of reader processes
    n_proc = 1
    # setup workers
    pool = [mp.Process(target=dataGen, args=(q,)) for x in range(n_proc)]
    for p in pool:
        p.daemon = True
        p.start()

    # wait a few sec for the process to become alive
    time.sleep(3)

    # start your drawing
    ani = animation.FuncAnimation(fig, run, frames=60, blit=True, interval=10,
                                  repeat=False)
    plt.show()

    print('done')

感谢 @RaJa 的帮助,我对多进程和线程还不熟悉,所以我会努力让它接收 Arduino 并绘制。 - emg184
我遇到的问题是dataGen函数一次性生成所有数据,然后将这些数据发送到其他函数,完成后程序才继续进行图形化。我需要能够每次接收一个点并更新图形。最好可以并行处理,其中新的Arduino数据修改我正在使用的实例变量,然后图形看到已添加新数据并将该点添加到图形中。 - emg184
data = q.get(block=True) 实际上会等待数据可用。因此,除非有新数据可用,否则图形不会更新。接下来:队列是先进先出的。因此,读取器发送的内容会直接绘制 - 逐点绘制。但是,读取器发送的数据不必直接到达图表。您可以添加另一个步骤,处理数据并将其发送到图表中。您还可以通过队列发送数据包。或者读取器自己进行所有处理。目前,我没有看到问题。抱歉。 - RaJa

0
我的问题和你的非常相似。 我需要每80毫秒从一个Profibus网络中获取数据,并且希望在采样时将数据绘制出来。
为了解决这个问题,我使用了多进程。使用Pipe进行两个进程之间的通信。 当绘图器从收集器那里获取到数据并准备绘制时,绘图器会向收集器发送一条消息,然后收集器停止发送数据并将数据放入一个列表中。 当绘图器完成工作后,它会告诉收集器“现在你可以发送数据了”,然后收集器会发送数据并清空列表。
import time
import numpy as np
from matplotlib import pyplot as plt
import multiprocessing
from multiprocessing import Process, Pipe
from random import random

class DataGennerator(object):
    """docstring for DataGennerator"""
    def __init__(self, data_pipe):
        super(DataGennerator, self).__init__()
        print('Data Gennerator Init...')
        self.data_buffer = []
        self.t = 0
        self.start_time = 0
        self.data_pipe = data_pipe
        self.plot_inprogess = False
        self.data_ready = False
 
    def run(self):
        self.start_time = time.time()
        for i in range(0, 400):
            self.loop_cycle()
        print('Total Time:', time.time()-self.start_time)
        print('Run completion......')

    def loop_cycle(self):
        self.t = time.time()-self.start_time
        new_data = [time.time()-self.start_time, np.sin(self.t), np.cos(2*self.t), np.cos(self.t*4), random()]
        self.send_data(new_data)
        time.sleep(0.08)

    def send_data(self, new_data):
        if self.plot_inprogess or not self.data_ready:
            self.data_buffer.append(new_data)
            self.data_ready = True
            # Wait 1ms to read plotter's msg
            if self.data_pipe.poll(0.0001):
                self.plot_inprogess = self.data_pipe.recv()
        else:
            self.data_pipe.send(self.data_buffer)
            self.data_buffer = []
            self.data_ready = False

# Function to collect data by using DataGennerator
def get_data(data_pipe):
    dg = DataGennerator(data_pipe)
    dg.run()
    data_pipe.send('EXIT')
    print('>>> Finished')


# use plotter_pipe to communicate with data collector
# and when get data from the collector, updata the figure
def updata_plot(plotter_pipe, plot_inprogess=True):
    plot_inprogess = True
    fig, ax = plt.subplots(nrows=4, ncols=1, figsize=(6,8), sharex=True)
    fig.set_tight_layout(True)
    styles = ['rs-', 'gs-', 'bs-', 'ro-', 'go-', 'bo-']*10
    lines = []
    for index, name in enumerate(['sin(t)', 'cos(t)', 'cos(2t)', 'random']):
        line, = ax[index].plot([],[], styles[index],label=name, markersize=4, markerfacecolor='w')
        ax[index].set_ylabel(name, color=styles[index][0], fontweight='bold')
        lines.append(line)
    ax[-1].set_xlabel('Time /s')
    fig.align_ylabels(ax)
    plt.ion()
    plt.show(block=False)
    plt.draw()

    # Read the 1st data package and convert it to Numpy Array
    data_array = np.array(plotter_pipe.recv())

    while True:
        try:
            # Read data_buffer sent by Data Collector
            data_buffer = plotter_pipe.recv()   #[ [data1], [data2]...]
            # If the DataCollector says EXIT, then break the while loop
            if data_buffer == 'EXIT': break
            # Raise a flag to indicate that Plot is in progress
            plotter_pipe.send(True)
            # Append data_buffer to Data Array
            data_array = np.append(data_array, np.array(data_buffer), axis=0)
            for i in range(0, 4):
                lines[i].set_xdata(data_array[:,0])
                lines[i].set_ydata(data_array[:,i+1])
                ax[i].relim()
                ax[i].autoscale_view()
            fig.canvas.draw()
            plt.pause(0.001)
            # Tell data collector that Plot has been finished
            plotter_pipe.send(False)
        except Exception as e:
            raise e
        finally:
            pass
    print('>>> Stop receiving data')
    data_content = '\n'.join([', '.join(map(str,data_line)) for data_line in data_array])
    with open('data.txt', 'w', encoding='UTF-8') as f:
        f.write('time, xx, yy, zz, bb\n')
        f.writelines(data_content)
    plt.show(block=True)

if __name__ == '__main__':

    plot_inprogess = True
    data_pipe, plotter_pipe = multiprocessing.Pipe(True)
    P1 = Process(target=get_data   , args=(data_pipe,))
    P2 = Process(target=updata_plot, args=(plotter_pipe,))
    P1.start()
    P2.start()
    P1.join()
    P2.join()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接