在Tkinter中使用Arduino数据动态更新标签和实时图

时间:2018-08-19 15:05:36

标签: python multithreading tkinter arduino

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

29,50,44

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

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

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

的错误
 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

这个错误还在继续,我知道也许我的代码不是实现此目标或实现我想要的目标的最合适方法,但这是我从研究中得出的结果。我尝试了这种方法Trying to plot real time serial port data from Arduino in Python,但由于问题没有得到答案,因此毫无用处。我也读过很多相关的问题,但还是没有运气。我不知道如何掌握队列和线程的主题。我希望有人能指出我做错了什么。任何帮助将不胜感激。

这是我的完整代码:

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的整洁代码。这是代码:

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

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

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

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

1 个答案:

答案 0 :(得分:0)

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

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