Python 基于串行数据动态更新Tkinter窗口

Python 基于串行数据动态更新Tkinter窗口,python,tkinter,Python,Tkinter,我正在尝试编写一个程序,从串行端口连接获取数据,并根据该数据实时自动更新Tkinter窗口 我尝试为窗口创建一个单独的线程,定期从主线程获取当前数据并更新窗口,如下所示: serialdata = [] data = True class SensorThread(threading.Thread): def run(self): serial = serial.Serial('dev/tty.usbmodem1d11', 9600) try:

我正在尝试编写一个程序,从串行端口连接获取数据,并根据该数据实时自动更新Tkinter窗口

我尝试为窗口创建一个单独的线程,定期从主线程获取当前数据并更新窗口,如下所示:

serialdata = []
data = True

class SensorThread(threading.Thread):
    def run(self):
        serial = serial.Serial('dev/tty.usbmodem1d11', 9600)
        try:
            while True:
                serialdata.append(serial.readline())
        except KeyboardInterrupt:
            serial.close()
            exit()

class GuiThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.root = Tk()
        self.lbl = Label(self.root, text="")

    def run(self):
        self.lbl(pack)
        self.lbl.after(1000, self.updateGUI)
        self.root.mainloop()

    def updateGUI(self):
        msg = "Data is True" if data else "Data is False"
        self.lbl["text"] = msg
        self.root.update()
        self.lbl.after(1000, self.updateGUI)

if __name == "__main__":
    SensorThread().start()
    GuiThread().start()

    try:
        while True:
            # A bunch of analysis that sets either data = True or data = False based on serialdata
    except KeyboardInterrupt:
        exit()
def poll_serial_port(self):
    if serial.has_data():
        data = serial.readline()
        self.lbl.configure(text=data)
    self.after(100, self.poll_serial_port)
运行它会导致以下错误:

线程2中的异常: 回溯(最近一次呼叫最后一次): 文件“/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/threading.py”,第522行,在bootstrap\u内部 self.run() 运行中第52行的文件“analysis.py” self.lbl1.pack() 文件“/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-tk/Tkinter.py”,第1764行,在pack_configure中 +自选配件(cnf,kw)) 运行时错误:主线程不在主循环中


当我用谷歌搜索这个错误时,我得到的大多数帖子都是人们试图从两个不同的线程与窗口交互,但我认为我没有这样做。有什么想法吗?非常感谢

不要从线程运行TK gui—从主进程运行它。我把你的例子拼凑成了一个能说明原理的东西

from time import sleep
import threading
from Tkinter import *

serialdata = []
data = True

class SensorThread(threading.Thread):
    def run(self):
        try:
            i = 0
            while True:
                serialdata.append("Hello %d" % i)
                i += 1
                sleep(1)
        except KeyboardInterrupt:
            exit()

class Gui(object):
    def __init__(self):
        self.root = Tk()
        self.lbl = Label(self.root, text="")
        self.updateGUI()
        self.readSensor()

    def run(self):
        self.lbl.pack()
        self.lbl.after(1000, self.updateGUI)
        self.root.mainloop()

    def updateGUI(self):
        msg = "Data is True" if data else "Data is False"
        self.lbl["text"] = msg
        self.root.update()
        self.lbl.after(1000, self.updateGUI)

    def readSensor(self):
        self.lbl["text"] = serialdata[-1]
        self.root.update()
        self.root.after(527, self.readSensor)

if __name__ == "__main__":
    SensorThread().start()
    Gui().run()

您需要将GUI放在主线程中,并使用单独的线程轮询串行端口。从串行端口读取数据时,可以将其推送到队列对象上

在主GUI线程中,您可以设置轮询以定期检查队列,方法是使用
after
计划轮询。调用一个函数,该函数消耗队列,然后在之后使用
调用自身,以有效地模拟无限循环

如果来自传感器的数据以相当慢的速度传输,并且您可以轮询串行端口而不阻塞,那么您可以在主线程中完成这一切—而不是从队列中推拉,您的主线程只需查看是否有可用数据,并在有可用数据时读取即可。只有在可以不阻塞地读取的情况下才能执行此操作,否则GUI将在等待数据时冻结

例如,您可以使其工作如下:

serialdata = []
data = True

class SensorThread(threading.Thread):
    def run(self):
        serial = serial.Serial('dev/tty.usbmodem1d11', 9600)
        try:
            while True:
                serialdata.append(serial.readline())
        except KeyboardInterrupt:
            serial.close()
            exit()

class GuiThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.root = Tk()
        self.lbl = Label(self.root, text="")

    def run(self):
        self.lbl(pack)
        self.lbl.after(1000, self.updateGUI)
        self.root.mainloop()

    def updateGUI(self):
        msg = "Data is True" if data else "Data is False"
        self.lbl["text"] = msg
        self.root.update()
        self.lbl.after(1000, self.updateGUI)

if __name == "__main__":
    SensorThread().start()
    GuiThread().start()

    try:
        while True:
            # A bunch of analysis that sets either data = True or data = False based on serialdata
    except KeyboardInterrupt:
        exit()
def poll_serial_port(self):
    if serial.has_data():
        data = serial.readline()
        self.lbl.configure(text=data)
    self.after(100, self.poll_serial_port)

上述操作将每秒检查串行端口10次,每次检查一个项目。当然,您必须根据您的实际数据条件对其进行调整。这假设您有一些方法,如
has_data
,当且仅当读取不会阻塞时,该方法才能返回True

您是否尝试过在线程中运行TK部件?Ie只是在一个线程中运行串口,TK可以留在主进程中。我怀疑这可能会起作用…比如一个线程用于获取串行端口数据,另一个线程用于数据分析循环?我来试一试。你应该使用线程安全的
队列
对象在线程之间进行通信,而不是使用简单的列表变量。这会更好。是的,但我的目的是向OP展示问题的解决方案,而不是教他们python IPC机制;-)