首页 > 解决方案 > python tkinter gui窗口在一段时间后与arduino串行通信冻结

问题描述

我正在使用带有 tkinter gui 的 pychem 通过串行通信从 arduino 收集数据并显示在 tkinter 窗口上。该脚本在启动时运行良好,但 tkinter gui 窗口冻结并在几分钟后停止更新值。经过一段时间的研究,我意识到冻结所需的时间与从 arduino 收集的数据数量直接相关。如果我每 0.1 秒发送一次数据,窗口会在 8 分 30 秒后完全冻结,如果我每 0.2 秒发送一次数据,时间几乎翻了一番。即使在窗口被冻结后,串行通信仍然可以正常工作,并且 python 脚本正在收集数据,但只是没有在 tkinter 窗口上更新。任何建议表示赞赏。我已经有一段时间了。我尝试过线程,但也出现了同样的问题。

import tkinter as tk
import tkinter
from tkinter import *
import time
import serial

root = tk.Tk()
root.geometry("500x500")
ser = serial.Serial('COM10', 9600)


def write_read(x):
    ser.write(bytes(x, 'utf-8'))
    time.sleep(0.001)
    data = ser.readline()
    return data


while True:
    xv = '1'
    yv = '2'
    xvalue = StringVar()
    xvalue.set(write_read(xv))
    yvalue = StringVar()
    yvalue.set(write_read(yv))
    w = tk.Label(root, text="X").place(x=10, y=10)
    w1 = tk.Label(root, text="Y").place(x=10, y=40)
    display1 = Entry(root, font=("Courier", 16), justify='right', textvariable=xvalue).place(x=50, y=10)
    display2 = Entry(root, font=("Courier", 16), justify='right', textvariable=yvalue).place(x=50, y=40)
    root.update_idletasks()
    root.update()

标签: pythontkinterarduinopyserial

解决方案


我以前没有合作过,serial所以我不知道这是否可行。代码对我来说看起来很实用。我更改了您的导入,然后更改了您的功能,然后从while. 您每次while运行时都会创建新的小部件,因此您可能已经创建了数千个小部件,这可能就是 tkinter 开始冻结的原因。此外,使用time.sleep()不会更新 GUI。所以我已经习惯root.after()了。

快速解决方案:xvalueyvalueww1display1display2移出循环。这样小部件只创建一次

避免的不同方法time.sleep()

from tkinter import tk
import serial

root = tk.Tk()
root.geometry("500x500")
ser = serial.Serial('COM10', 9600)

def foo(x,caller):
    ser.write(bytes(x, 'utf-8'))
    def bar():
        data = ser.readline()
        if caller == 'x': # If called by x
            xvalue.set(data) # Then write to x
        else:  
            yvalue.set(data) # Else write to y
    root.after(1,bar) # Same as 0.001s 

xv = '1'
yv = '2'

xvalue = tk.StringVar()
yvalue = tk.StringVar()

w = tk.Label(root, text="X").place(x=10, y=10)
w1 = tk.Label(root, text="Y").place(x=10, y=40)

display1 = tk.Entry(root, font=("Courier", 16), justify='right', textvariable=xvalue).place(x=50, y=10)
display2 = tk.Entry(root, font=("Courier", 16), justify='right', textvariable=yvalue).place(x=50, y=40)

# Call functions initially
foo(xv,'x')
foo(yv,'y')

root.mainloop()

这里不需要使用StringVar,你可以使用入口小部件本身的deleteinsert方法。


推荐阅读