Tkinter .after永远运行,.mainloop永远不会运行

时间:2016-06-30 17:25:08

标签: python python-2.7 tkinter raspberry-pi raspberry-pi3

我正在我的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然后按回车键运行程序。

固定/可替代?

2 个答案:

答案 0 :(得分:1)

处理事件

你犯了两个常见的错误:你不应该after(0, ...),你不应该打sleep

  1. after(0, ...)表示每次处理事件时,都会立即添加另一个事件。事件循环永远不会有机会处理队列中的其他事件,包括处理滑块的事件,屏幕更新等。

  2. 当您致电sleep时,GUI就是这样:它会睡觉。当它处于睡眠状态时,它无法处理任何事件。

  3. 解决方案是仅在合理的时间范围内使用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)
    

    另一种方式,如果你不断想要每半秒切换一次引脚,那就是:

    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。然后,您需要致电getset来获取或设置值。

    例如:

    freq = IntVar()
    freq.set(1)
    
    def update(value=GPIO.HIGH):
        period = 1.0/float(freq.get())
        ...
    

答案 1 :(得分:0)

mainloop()正在运行,但Tkinter除非处于空闲状态,否则不会更新视图。 KeyboardInterrupt会告诉它清理,此时它会完成事件队列(包括更新界面)并退出。

解决方案:让mainloop有空闲时间 - 你真的只需要将after(0, update)更改为内部有几毫秒或告诉master .update_idletasks()更新GUI。

稍微好一点的解决方案是将你的高/低部分放入他们自己的函数中,在所需的延迟后调用每个部分 - 在mainloop gui中sleep是一个坏主意,因为你的GUI无法更新如果程序正在睡觉。 (它也不能接受输入等)你会有毫秒帧来改变输入再次更新之前,同时使用两个相互调用的函数after所选择的毫秒可以让你在等待时调整时间等等打开/关闭另一个。