Tkinter .after 永远运行并且 .mainloop 永远不会运行
Tkinter .after runs forever and .mainloop never runs
我正在我的 RPi3 上创建一个 python 程序,它根据 Tkinter 比例更改 GPIO 引脚的频率。
这是我的代码:
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
from Tkinter import *
import time
freq = 1.0
master = Tk()
def update():
period = 1.0/float(freq)
GPIO.output(8, GPIO.HIGH)
time.sleep(period/2.0)
GPIO.output(8, GPIO.LOW)
time.sleep(period/2.0)
master.after(0, update)
scale = Scale(master, from_=1, to=20000, orient=HORIZONTAL, variable=freq)
scale.pack()
GPIO.setup(8, GPIO.OUT)
master.after(0, update)
master.mainloop()
GPIO.cleanup()
出于某种原因,master.after(0, update)
永远运行而 master.mainloop()
永远不会运行。我知道是因为刻度永远不会出现,引脚 8 打开半秒,然后关闭半秒,然后循环重复。
如果我按 Ctrl+C 然后 master.after(0, update)
停止 运行 并且 master.mainloop()
开始 运行,比例出现,但是当我向左拖动滑块时没有任何反应正确的。
我通过在终端中输入 sudo python tone.py
然后按回车键来 运行 程序。
Fix/Alternative?
mainloop()
是 运行,但 Tkinter
除非处于空闲状态,否则不会更新视图。 KeyboardInterrupt
将告诉它进行清理,此时它完成其事件队列(包括更新界面)并退出。
解决方案:给 mainloop 时间空闲 - 你真的只需要改变你的 after(0, update)
以在内部有几毫秒或告诉 master
到 .update_idletasks()
更新 GUI。
一个稍微好一点的解决方案是让你的 high/low 部分成为它们自己的函数,这些函数在需要的延迟之后调用每个 - sleep
在主循环 gui 中运行是个坏主意,因为你的 GUI如果程序正在休眠,则无法更新任何内容。 (它也不能接受输入等。)在再次更新之前,您将有毫秒帧来更改输入,同时使用两个相互调用的函数 after
选择的毫秒可以让您在等待时调整时间等翻到另一个 on/off.
处理事件
您犯了两个比较常见的错误:您不应该 after(0, ...)
,并且您不应该调用 sleep
。
after(0, ...)
表示每处理完一个事件,就立马追加一个事件。事件循环永远没有机会处理队列中的其他事件,包括处理滑块、屏幕更新等的事件
当您调用 sleep
时,GUI 会执行此操作:它会休眠。当它处于休眠状态时,它无法处理任何事件。
解决办法是只使用after
合理的时间跨度,根本不调用sleep
。
例如:
def update():
...
# set the pin high
GPIO.output(8, GPIO.HIGH)
# set the pin low in half the period
master.after(period/2, GPIO.output, 8, GPIO.LOW)
# do this again based on the period
master.after(period, update)
另一种方式,如果你想每半秒连续切换一次 pin,将是这样的:
def update(value=GPIO.HIGH):
GPIO.output(8, value)
next_value = GPIO.LOW if value == GPIO.HIGH else GPIO.HIGH
master.after(500, update, next_value)
使用滑块
当您使用滑块的 variable
属性时,变量必须是特殊 tkinter 变量之一的实例,例如 IntVar
。然后您需要调用 get
或 set
来获取或设置该值。
例如:
freq = IntVar()
freq.set(1)
def update(value=GPIO.HIGH):
period = 1.0/float(freq.get())
...
我正在我的 RPi3 上创建一个 python 程序,它根据 Tkinter 比例更改 GPIO 引脚的频率。
这是我的代码:
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
from Tkinter import *
import time
freq = 1.0
master = Tk()
def update():
period = 1.0/float(freq)
GPIO.output(8, GPIO.HIGH)
time.sleep(period/2.0)
GPIO.output(8, GPIO.LOW)
time.sleep(period/2.0)
master.after(0, update)
scale = Scale(master, from_=1, to=20000, orient=HORIZONTAL, variable=freq)
scale.pack()
GPIO.setup(8, GPIO.OUT)
master.after(0, update)
master.mainloop()
GPIO.cleanup()
出于某种原因,master.after(0, update)
永远运行而 master.mainloop()
永远不会运行。我知道是因为刻度永远不会出现,引脚 8 打开半秒,然后关闭半秒,然后循环重复。
如果我按 Ctrl+C 然后 master.after(0, update)
停止 运行 并且 master.mainloop()
开始 运行,比例出现,但是当我向左拖动滑块时没有任何反应正确的。
我通过在终端中输入 sudo python tone.py
然后按回车键来 运行 程序。
Fix/Alternative?
mainloop()
是 运行,但 Tkinter
除非处于空闲状态,否则不会更新视图。 KeyboardInterrupt
将告诉它进行清理,此时它完成其事件队列(包括更新界面)并退出。
解决方案:给 mainloop 时间空闲 - 你真的只需要改变你的 after(0, update)
以在内部有几毫秒或告诉 master
到 .update_idletasks()
更新 GUI。
一个稍微好一点的解决方案是让你的 high/low 部分成为它们自己的函数,这些函数在需要的延迟之后调用每个 - sleep
在主循环 gui 中运行是个坏主意,因为你的 GUI如果程序正在休眠,则无法更新任何内容。 (它也不能接受输入等。)在再次更新之前,您将有毫秒帧来更改输入,同时使用两个相互调用的函数 after
选择的毫秒可以让您在等待时调整时间等翻到另一个 on/off.
处理事件
您犯了两个比较常见的错误:您不应该 after(0, ...)
,并且您不应该调用 sleep
。
after(0, ...)
表示每处理完一个事件,就立马追加一个事件。事件循环永远没有机会处理队列中的其他事件,包括处理滑块、屏幕更新等的事件当您调用
sleep
时,GUI 会执行此操作:它会休眠。当它处于休眠状态时,它无法处理任何事件。
解决办法是只使用after
合理的时间跨度,根本不调用sleep
。
例如:
def update():
...
# set the pin high
GPIO.output(8, GPIO.HIGH)
# set the pin low in half the period
master.after(period/2, GPIO.output, 8, GPIO.LOW)
# do this again based on the period
master.after(period, update)
另一种方式,如果你想每半秒连续切换一次 pin,将是这样的:
def update(value=GPIO.HIGH):
GPIO.output(8, value)
next_value = GPIO.LOW if value == GPIO.HIGH else GPIO.HIGH
master.after(500, update, next_value)
使用滑块
当您使用滑块的 variable
属性时,变量必须是特殊 tkinter 变量之一的实例,例如 IntVar
。然后您需要调用 get
或 set
来获取或设置该值。
例如:
freq = IntVar()
freq.set(1)
def update(value=GPIO.HIGH):
period = 1.0/float(freq.get())
...