在Tkinter中使用Arduino数据动态更新标签和实时图 [英] Updating Label and Live Graph dynamically using Arduino Data in Tkinter

查看:100
本文介绍了在Tkinter中使用Arduino数据动态更新标签和实时图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在研究一个项目,该项目更新标签并将其绘制在来自Arduino的实时图形中.它输出温度和湿度.我的arduino串行输出看起来像这样:

I've been working on a project that updates the label and plots it in a live graph that's coming from the Arduino. It outputs temperature and humidity. My arduino serial outputs looks like this:

29,50,44

我使用以下代码行在python中分离串行数据:

I use this line of codes in separating the serial data in python:

ser = serial.Serial('COM3',9600)
pullData = ser.readline().decode('utf-8')
get_data = pullData.split(',')

我可以分开这些值,并在各自的标签中对其进行更新.但是,我无法使用那些相同的串行值来动态更新图形时,却无法正常工作.我想同时更新它们.我收到类似

I get to separate the values and update them in their respective labels. But what I don't get it to work is when I dynamically update the graph using those same serial values. I want to update them both at the same time. I get errors like

 28,90,43
Exception in thread Thread-3:
Traceback (most recent call last):
  File "C:\Users\Deanne\AppData\Local\Programs\Python\Python36\lib\threading.py", line 916, in _bootstrap_inner
    self.run()
  File "C:\Users\Deanne\AppData\Local\Programs\Python\Python36\lib\threading.py", line 864, in run
    self._target(*self._args, **self._kwargs)
  File "c:\Users\Deanne\OneDrive\Documents\Example#1\example#7.py", line 68, in animate
    humid = int(data_1[0])
ValueError: invalid literal for int() with base 10: '\x005\n'
Exception in thread Thread-1:
Traceback (most recent call last):
  File "C:\Users\Deanne\AppData\Local\Programs\Python\Python36\lib\threading.py", line 916, in _bootstrap_inner
    self.run()
  File "C:\Users\Deanne\AppData\Local\Programs\Python\Python36\lib\threading.py", line 864, in run
    self._target(*self._args, **self._kwargs)
  File "c:\Users\Deanne\OneDrive\Documents\Example#1\example#7.py", line 69, in animate
    temp = int(data_1[1])
ValueError: invalid literal for int() with base 10: '\x00\x00\n'
Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\Deanne\AppData\Local\Programs\Python\Python36\lib\tkinter\__init__.py", line 1699, in __call__
    return self.func(*args)
  File "C:\Users\Deanne\AppData\Local\Programs\Python\Python36\lib\tkinter\__init__.py", line 745, in callit
    func(*args)
  File "c:\Users\Deanne\OneDrive\Documents\Example#1\example#7.py", line 86, in GetSerialData
    data_array2 = int(get_data[2])
IndexError: list index out of range

这个错误还在继续,我知道也许我的代码不是实现此目标或实现我想要的目标的最合适方法,但这是我从研究中得出的结果.我尝试了这种方法尝试在python中绘制来自Arduino的实时串行端口数据,但没有结果,因为问题根本没有得到回答.我也读过很多相关的问题,但还是没有运气.我不知道如何掌握队列和线程的主题.我希望有人能指出我做错了什么.任何帮助将不胜感激.

This error just keeps on going, I know that maybe my code is not the most suitable way to approach this or to achieve what I want but this is what I've come up from doing researches. I tried this way of approach Trying to plot real time serial port data from Arduino in Python but to no avail since the question was not answered at all. I've also read many somewhat related problem but still no luck. I don't know how to grasp the topic of queue and threads. I wish someone could point out what I'm doing wrong. Any help will be appreciated.

这是我的完整代码:

from tkinter import *
import serial
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
from matplotlib import style
import threading

my_window = Tk()
my_window.title("Graphical User Interface Demo#1")
my_window.geometry("720x720")

#For raising frames
def raise_frame(frame):
    frame.tkraise()

F1 = Frame(my_window, relief = RAISED)
F2 = Frame(my_window, relief = RAISED)
F3 = Frame(my_window, relief = RAISED)
F4 = Frame(my_window, relief = RAISED)

for frame in(F1, F2, F3, F4):
    frame.grid(row = 0, column = 0, sticky = "NSEW")

raise_frame(F1)


List_1 = []
List_2 = []
List_3 = []

#Initialization of Serial Comm
ser = serial.Serial('COM3', 9600)


style.use("ggplot")
a = Figure(figsize = (7,6))
plot_a = a.add_subplot(111)
plot_a.set_title('Temperature Graph')
plot_a.set_ylabel('Temperature')
plot_a.set_xlabel('Time')
plot_a.plot(List_1, 'c', marker = 'o',label = 'Degrees C')
plot_a.legend(loc= 'upper left')

b = Figure(figsize = (7,6))
plot_b = b.add_subplot(111)
plot_b.set_title('Humidity Graph')
plot_b.set_ylabel('Humidity')
plot_b.plot(List_2,'g', marker = 'o', label = 'Percentage %')
plot_c.legend(loc = 'upper right')

c = Figure(figsize = (7,6))
plot_c = c.add_subplot(111)
plot_c.set_title('Solved Water Graph')
plot_c.set_ylabel('Water Volume')
plot_c.plot(List_3,'b', marker = 'o', label = 'mL')
plot_c.legend(loc = 'upper right')


def animate_thread(i):
    threading.Thread(target=animate, args=(i,)).start()

def animate(i):
    pulldata = ser.readline().decode('ascii')
    data_1 = pulldata.split(',')
    humid = int(data_1[0])
    temp = int(data_1[1])
    solved_water = int(data_1[2])
    List_1.append(humid)
    List_2.append(temp)
    List_3.append(solved_water)
    plot_a.set_ylim(0,40)
    plot_a.plot(List_1, 'c', marker = 'o',label = 'Degrees C')
    plot_b.set_ylim(0,100)
    plot_b.plot(List_2, 'g', marker = 'o',label = 'Percentage %')
    plot_c.set_ylim(0,55)
    plot_c.plot(List_3, 'b', marker = 'o',label = 'mL')

def GetSerialData():
    pulldata = ser.readline().decode('ascii')
    get_data = pulldata.split(',')
    data_array = int(get_data[0])
    data_array1 = int(get_data[1])
    data_array2 = int(get_data[2])
    label_2data.config(text = str(data_array))
    label_3data.config(text = str(data_array1))
    label_4data.config(text = str(data_array2))
    print(pulldata)
    my_window.after(10000, GetSerialData)


#For Frame One
label_1 = Label(F1, text = "Homepage of GUI", relief = "solid", font = "Times 22 bold")
label_1.grid(row = 0, column = 3)
button_1 = Button(F1, text = "Page of Humidity", relief = GROOVE, bd = 8, command = lambda:raise_frame(F2))
button_1.grid(row = 1, column = 2)
button_2 = Button(F1, text = "Page of Temperature", relief = GROOVE, bd = 8, command = lambda:raise_frame(F3))
button_2.grid(row = 1, column = 3)
button_3 = Button(F1, text = "Page of Water", relief = GROOVE, bd = 8, command = lambda:raise_frame(F4))
button_3.grid(row = 1, column = 4)


#For Frame Two
label_2 = Label(F2, text = "Temperature", relief = "solid", font = "Times 22 bold")
label_2.grid(row = 0, column = 3)
button_1 = Button(F2, text = "Back To Homepage", command = lambda:raise_frame(F1))
button_1.grid(row = 1, column = 2)
label_2_1 = Label(F2, text = "Current Value: ", relief = "solid", font = "Verdana 10 bold")
label_2_1.grid(row = 2, column = 2)
label_2data = Label(F2, font = "Verdana 10")
label_2data.grid(row = 2, column = 3)
canvas1 = FigureCanvasTkAgg(a, F2)
canvas1.get_tk_widget().grid(row = 3, column = 3)
F2.canvas = canvas1


#For Frame Three
label_3 = Label(F3, text = "Humidity", relief = "solid", font = "Times 22 bold")
label_3.grid(row = 0, column = 3)
button_1 = Button(F3, text = "Back To Homepage", command = lambda:raise_frame(F1))
button_1.grid(row = 1, column = 2)
label_3_1 = Label(F3, text = "Current Value: ", relief = "solid", font = "Verdana 10 bold")
label_3_1.grid(row = 2, column = 2)
label_3data = Label(F3, font = "Verdana 10")
label_3data.grid(row = 2, column = 3)
canvas2 = FigureCanvasTkAgg(b, F3)
canvas2.get_tk_widget().grid(row = 3, column = 3)
F3.canvas = canvas2


#For Frame Four
label_4 = Label(F4, text = "Solved Water Value", relief = "solid", font = "Times 22 bold")
label_4.grid(row = 0, column = 3)
button_1 = Button(F4, text = "Back To Homepage", command = lambda:raise_frame(F1))
button_1.grid(row = 1, column = 2)
label_4_1 = Label(F4, text = "Current Value: ", relief = "solid", font = "Verdana 10 bold")
label_4_1.grid(row = 2, column = 2)
label_4data = Label(F4,font = "Verdana 10")
label_4data.grid(row = 2, column = 3)
canvas3 = FigureCanvasTkAgg(b, F4)
canvas3.get_tk_widget().grid(row = 3, column = 3)
F4.canvas = canvas3


aniA = animation.FuncAnimation(a, animate_thread, interval = 20000, blit = False)
aniB = animation.FuncAnimation(b, animate_thread, interval = 20000, blit = False)
aniC = animation.FuncAnimation(c, animate_thread, interval = 20000, blit = False)
GetSerialData()
my_window.mainloop()

更新:我尝试使用@Novel的整洁代码.这是代码:

Update: I've tried using @Novel's neater code. Here's the code:

#from tkinter import *
import tkinter as tk # proper way to import tkinter
import serial
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
from matplotlib import style
style.use("ggplot")
import threading

class Dee(tk.Frame):
    def __init__(self, master=None, title='', ylabel='', label='', color='c', ylim=1, **kwargs):
        tk.Frame.__init__(self, master, **kwargs)
        self.data = []
        fig = Figure(figsize = (7,6))
        self.plot = fig.add_subplot(111)
        self.plot.set_title(title)
        self.plot.set_ylabel(ylabel)
        self.plot.set_ylim(0, ylim)
        self.line, = self.plot.plot([], [], color, marker = 'o',label = label)
        self.plot.legend(loc='upper left')

        label = tk.Label(self, text = ylabel, font = "Times 22 bold")
        label.grid(row = 0, column = 3)
        button_1 = tk.Button(self, text = "Back To Homepage", command = F1.tkraise)
        button_1.grid(row = 1, column = 2)
        label_1 = tk.Label(self, text = "Current Value: ", font = "Verdana 10 bold")
        label_1.grid(row = 2, column = 2)
        self.label_data = tk.Label(self, font = "Verdana 10")
        self.label_data.grid(row = 2, column = 3)
        canvas = FigureCanvasTkAgg(fig, master=self)
        canvas.get_tk_widget().grid(row = 3, column = 3)

        ani = animation.FuncAnimation(fig, self.update_graph, interval = 1000, blit = False)
        canvas.draw()

    def update_graph(self, i):
        if self.data:
        self.line.set_data(range(len(self.data)), self.data)
        self.plot.set_xlim(0, len(self.data))

    def set(self, value):
        self.data.append(value)
        self.label_data.config(text=value)

my_window = tk.Tk()
my_window.title("Graphical User Interface Demo#1")
my_window.geometry("720x720")

F1 = tk.Frame(my_window)
F2 = Dee(my_window, title='Temperature Graph', ylabel='Temperature', color='c', label='Degrees C', ylim=40)
F3 = Dee(my_window, title='Humidity Graph', ylabel='Humidity', color='g', label='Percentage %', ylim=100)
F4 = Dee(my_window, title='Solved Water Graph', ylabel='Water Volume', color='b', label='mL', ylim=55)

#For Frame One
label_1 = tk.Label(F1, text = "Homepage of GUI", font = "Times 22 bold")
label_1.grid(row = 0, column = 3)
button_1 = tk.Button(F1, text = "Page of Humidity", bd = 8, command = F2.tkraise)
button_1.grid(row = 1, column = 2)
button_2 = tk.Button(F1, text = "Page of Temperature", bd = 8, command = F3.tkraise)
button_2.grid(row = 1, column = 3)
button_3 = tk.Button(F1, text = "Page of Water",  bd = 8, command = F4.tkraise)
button_3.grid(row = 1, column = 4)

for frame in(F1, F2, F3, F4):
    frame.grid(row = 0, column = 0, sticky = "NSEW")

F1.tkraise()

def get_data():
    #Initialization of Serial Comm
    ser = serial.Serial('COM3', 9600)
    while True:
        pulldata = ser.readline().decode('ascii')
        get_data = pulldata.split(',')
        F2.set(int(get_data[0]))
        F3.set(int(get_data[1]))
        F4.set(int(get_data[2]))
        print(pulldata)

# start the thread that will poll the arduino
t = threading.Thread(target=get_data)
t.daemon = True
t.start()

my_window.mainloop()

图形和标签正在更新,但是现在出现的问题是冻结.这也给我这个错误:

The graphs and labels are updating but the problem that now arises is that it's freezing. Also this gives me this error:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\Deanne\AppData\Local\Programs\Python\Python36\lib\tkinter\__init__.py", line 1699, in __call__
    return self.func(*args)
  File "C:\Users\Deanne\AppData\Local\Programs\Python\Python36\lib\tkinter\__init__.py", line 745, in callit
    func(*args)
  File "C:\Users\Deanne\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\backends\_backend_tk.py", line 310, in idle_draw
    self.draw()
  File "C:\Users\Deanne\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\backends\backend_tkagg.py", line 12, in draw
    super(FigureCanvasTkAgg, self).draw()
  File "C:\Users\Deanne\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\backends\backend_agg.py", line 433, in draw
    self.figure.draw(self.renderer)
  File "C:\Users\Deanne\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\artist.py", line 55, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "C:\Users\Deanne\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\figure.py", line 1475, in draw
    renderer, self, artists, self.suppressComposite)
  File "C:\Users\Deanne\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\image.py", line 141, in _draw_list_compositing_images
    a.draw(renderer)
  File "C:\Users\Deanne\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\artist.py", line 55, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "C:\Users\Deanne\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\axes\_base.py", line 2607, in draw
    mimage._draw_list_compositing_images(renderer, self, artists)
  File "C:\Users\Deanne\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\image.py", line 141, in _draw_list_compositing_images
    a.draw(renderer)
  File "C:\Users\Deanne\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\artist.py", line 55, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "C:\Users\Deanne\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\lines.py", line 738, in draw
    self.recache()
  File "C:\Users\Deanne\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\lines.py", line 661, in recache
    self._xy = np.column_stack(np.broadcast_arrays(x, y)).astype(float)
  File "C:\Users\Deanne\AppData\Local\Programs\Python\Python36\lib\site-packages\numpy\lib\stride_tricks.py", line 249, in broadcast_arrays
    shape = _broadcast_shape(*args)
  File "C:\Users\Deanne\AppData\Local\Programs\Python\Python36\lib\site-packages\numpy\lib\stride_tricks.py", line 184, in _broadcast_shape
    b = np.broadcast(*args[:32])
ValueError: shape mismatch: objects cannot be broadcast to a single shape

我可以说它仍在工作,因为它仍在输出数据.任何帮助再次将不胜感激.谢谢.

I can tell that it's still working because it still outputs data. Any help again would be appreciated. Thank you.

推荐答案

这是一个疯狂的猜测,它显示了如何使单个线程轮询arduino,以及如何通过使用类而不是复制/粘贴使代码更整洁:

Here's a wild guess that shows how to make a single thread for polling the arduino and also how to make your code neater by using a class instead of copy / paste:

from tkinter import *
import tkinter as tk # proper way to import tkinter
import serial
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
from matplotlib import style
style.use("ggplot")
import threading

class Dee(tk.Frame):
    def __init__(self, master=None, title='', ylabel='', label='', color='c', ylim=1, **kwargs):
        tk.Frame.__init__(self, master, **kwargs)
        self.data = []
        fig = Figure(figsize = (7,6))
        self.plot = fig.add_subplot(111)
        self.plot.set_title(title)
        self.plot.set_ylabel(ylabel)
        self.plot.set_ylim(0, ylim)
        self.line, = self.plot.plot([], [], color, marker = 'o',label = label)
        self.plot.legend(loc='upper left')

        label = Label(self, text = ylabel, relief = "solid", font = "Times 22 bold")
        label.grid(row = 0, column = 3)
        button_1 = Button(self, text = "Back To Homepage", command = F1.tkraise)
        button_1.grid(row = 1, column = 2)
        label_1 = Label(self, text = "Current Value: ", relief = "solid", font = "Verdana 10 bold")
        label_1.grid(row = 2, column = 2)
        self.label_data = Label(self, font = "Verdana 10")
        self.label_data.grid(row = 2, column = 3)
        canvas = FigureCanvasTkAgg(fig, master=self)
        canvas.get_tk_widget().grid(row = 3, column = 3)

        ani = animation.FuncAnimation(fig, self.update_graph, interval = 1000, blit = False)
        canvas.draw()

    def update_graph(self, i):
        if self.data:
            self.line.set_data(range(len(self.data)), self.data)
            self.plot.set_xlim(0, len(self.data))

    def set(self, value):
        self.data.append(value)
        self.label_data.config(text=value)

my_window = Tk()
my_window.title("Graphical User Interface Demo#1")
my_window.geometry("720x720")

F1 = Frame(my_window, relief = RAISED)
F2 = Dee(my_window, title='Temperature Graph', ylabel='Temperature', color='c', label='Degrees C', ylim=40, relief = RAISED)
F3 = Dee(my_window, title='Humidity Graph', ylabel='Humidity', color='g', label='Percentage %', ylim=100, relief = RAISED)
F4 = Dee(my_window, title='Solved Water Graph', ylabel='Water Volume', color='b', label='mL', ylim=55, relief = RAISED)

#For Frame One
label_1 = Label(F1, text = "Homepage of GUI", relief = "solid", font = "Times 22 bold")
label_1.grid(row = 0, column = 3)
button_1 = Button(F1, text = "Page of Humidity", relief = GROOVE, bd = 8, command = F2.tkraise)
button_1.grid(row = 1, column = 2)
button_2 = Button(F1, text = "Page of Temperature", relief = GROOVE, bd = 8, command = F3.tkraise)
button_2.grid(row = 1, column = 3)
button_3 = Button(F1, text = "Page of Water", relief = GROOVE, bd = 8, command = F4.tkraise)
button_3.grid(row = 1, column = 4)

for frame in(F1, F2, F3, F4):
    frame.grid(row = 0, column = 0, sticky = "NSEW")

F1.tkraise()

def get_data():
    #Initialization of Serial Comm
    ser = serial.Serial('COM3', 9600)
    while True:
        pulldata = ser.readline().decode('ascii')
        get_data = pulldata.split(',')
        F2.set(int(get_data[0]))
        F3.set(int(get_data[1]))
        F4.set(int(get_data[3]))

# start the thread that will poll the arduino
t = threading.Thread(target=get_data)
t.daemon = True
t.start()

my_window.mainloop()

这篇关于在Tkinter中使用Arduino数据动态更新标签和实时图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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