python tkinter gui window 使用 arduino 串行通信一段时间后冻结
python tkinter gui window freeze after a while with arduino serial communication
我正在使用带有 tkinter gui 的 pychem 通过串行通信从 arduino 收集数据并在 tkinter 上显示 window。该脚本在启动时运行良好,但 tkinter gui window 冻结并在几分钟后停止更新值。工作一段时间后,我意识到冻结所需的时间与从 arduino 收集的数据数量直接相关。如果我每 0.1 秒发送一次数据,window 会在 8 分 30 秒后冻结,如果我每 0.2 秒发送一次数据,则时间几乎翻倍到秒。即使在 window 被冻结之后,串行通信仍然正常工作,数据正在被 python 脚本收集,但只是没有在 tkinter window 上更新。
任何建议表示赞赏。我已经在这上面堆了一段时间了。我试过穿线但遇到了同样的问题。
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()
我之前没有使用过 serial
,所以我不知道这是否可行。该代码对我来说看起来很实用。我更改了你的导入,然后是你的函数,然后从 while
中删除了一些东西。您每次 while
运行 都创建新的小部件,因此您可能已经创建了数千个小部件,这可能是 tkinter 开始冻结的原因。此外,使用 time.sleep()
不会更新 GUI。所以我为此使用了root.after()
。
快速解决方案:
将 xvalue
、yvalue
、w
、w1
、display1
和 display2
移出循环。这样小部件只创建一次
避免的不同方法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
,您可以只使用入口小部件本身的 delete
和 insert
方法。
我正在使用带有 tkinter gui 的 pychem 通过串行通信从 arduino 收集数据并在 tkinter 上显示 window。该脚本在启动时运行良好,但 tkinter gui window 冻结并在几分钟后停止更新值。工作一段时间后,我意识到冻结所需的时间与从 arduino 收集的数据数量直接相关。如果我每 0.1 秒发送一次数据,window 会在 8 分 30 秒后冻结,如果我每 0.2 秒发送一次数据,则时间几乎翻倍到秒。即使在 window 被冻结之后,串行通信仍然正常工作,数据正在被 python 脚本收集,但只是没有在 tkinter window 上更新。 任何建议表示赞赏。我已经在这上面堆了一段时间了。我试过穿线但遇到了同样的问题。
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()
我之前没有使用过 serial
,所以我不知道这是否可行。该代码对我来说看起来很实用。我更改了你的导入,然后是你的函数,然后从 while
中删除了一些东西。您每次 while
运行 都创建新的小部件,因此您可能已经创建了数千个小部件,这可能是 tkinter 开始冻结的原因。此外,使用 time.sleep()
不会更新 GUI。所以我为此使用了root.after()
。
快速解决方案:
将 xvalue
、yvalue
、w
、w1
、display1
和 display2
移出循环。这样小部件只创建一次
避免的不同方法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
,您可以只使用入口小部件本身的 delete
和 insert
方法。