【问题标题】:Using multiprocessing/threading to read the serial port and live graph data with Tkinter使用多处理/线程通过 Tkinter 读取串行端口和实时图形数据
【发布时间】:2017-06-15 18:11:15
【问题描述】:

我需要以高速率实时显示图表数据。我需要不断查询串行端口以在可用时检索数据。然后数据会修改我正在绘制的实例变量。我在两个子图上绘制 4 条不同的线,因此总共 8 条线。我收到了 12 个不同的变量,但只有 8 个图形。我需要在运行一次函数后进程不要消失,这样我可以继续接收新数据。我希望在另一个进程中完成绘图,这样它就不会阻止读取串行端口。如果我已经拥有所有数据,但如果它不断接收新数据并且我是多处理/线程的新手,因此很难理解如何修改它以持续阅读从串行端口。我可以以 10Hz 的速率完成我需要的工作,但我需要以 20Hz 的频率进行采样,似乎唯一的方法是通过多处理/线程,除非我错过了可以优化绘图或读取时间的东西。这是一个 Tkinter 窗口,当按下 go 按钮并绘制图形时,它将查询串行端口:

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
import time

class App:
    def __init__(self, master):
        self.t = 0
        self.arduinoData = serial.Serial('com5', 250000, timeout=None)

        frame = Tkinter.Frame(master)

        self.running = False
        self.ani = None

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

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

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

        self.stop = Tkinter.Button(self.test_options, 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 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.limit = 300
        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):
        if (self.arduinoData.inWaiting()>0):
            self.xdata.append(self.k)
            x = self.arduinoData.readline()
            self.setData(x)
            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.pressure4) + 1)
                self.ax1.set_xlim(0, self.k+1)
                self.ax2.set_ylim(min(self.displacement1)-1, max(self.displacement4) + 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.pressure4[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.displacement4[self.k-49:self.k]) + 1)
                self.ax2.set_xlim(self.xdata[self.k-49], self.xdata[self.k-1])
            if self.cycle1 >= self.limit:
                self.running = False
                self.ani = None
                self.ani.event_source.stop()
                self.running = not self.running
            self.k += 1



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

如果有人能帮助我理解多处理或提供一个很好的例子。谢谢

【问题讨论】:

  • 您可以设置一个计时器,在这么多滴答声之后重复该功能。如果您只想不断运行该功能,您可以使用.after() 执行此操作。 self.function_to_get_data.after(100, lambda: self.function_to_get_data())
  • 我正在使用的获取数据函数是由动画函数调用的,之后我将如何使用它们以便它们都被调用并且不会阻止彼此的执行
  • 动画函数是否被多次调用?
  • 动画函数中设置的间隔应该在函数完成后立即调用它,它的迭代读取然后图形
  • 您可以有一个在计时器上调用的函数,该函数将首先运行获取数据的函数,然后运行绘制数据图表的函数。

标签: python tkinter python-multithreading python-multiprocessing pyserial


【解决方案1】:

如果您提供一个计时器,您就可以拥有一个为您管理事件的功能。

例如:

import time
import tkinter as tk

root = tk.Tk()

def getData():
    print("getData")

def update_graph():
    print("update_graph")

def timed_events():
    getData()
    update_graph()
    root.after(1000, timed_events) # you can change the number to anything you need

timed_events()

root.mainloop()

这将每 1 秒按顺序调用每个函数并打印:

getData
update_graph

这只是一个简单的示例,用于说明如何在计时器上处理数据。

更新:

这是您的代码的另一个可能选项。 在您的start(self): 方法的末尾添加一个if 语句以重复方法if self.x == True:。然后你需要做的就是创建一个按钮来切换变量self.x,这样你就可以在你想要的时候停止循环。

self.x == True

    def start(self):
        # all your code so far
        if self.x == True:
            self.start

【讨论】:

  • 对不起,我在尝试以 20Hz 运行它后说得太早了,它仍然太慢,它运行得很好,略高于我之前的 10Hz
  • 啊。好吧,我想通过更多的研究为您提供一个解决方案。今天下班回家我会看看有没有人给你提供更好的选择:)
  • 非常感谢您竭尽全力帮助我已经坚持了一段时间
  • 对我来说是关于学习过程的。我现在只编程了 4 个多月,我发现最好的学习方法是解决其他问题:)
  • @emg184:我希望我对多处理库有更多的经验。我只是不需要使用它,而且我没有可以测试类似情况的设置来为您提供经过测试的答案。
猜你喜欢
  • 1970-01-01
  • 2022-01-20
  • 2014-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多