Tkinter GUI 冻结 - 解除阻塞/线程的提示?

Posted

技术标签:

【中文标题】Tkinter GUI 冻结 - 解除阻塞/线程的提示?【英文标题】:Tkinter GUI Freezes - Tips to Unblock/Thread? 【发布时间】:2016-06-10 14:52:48 【问题描述】:

python3 新手,开始了我的第一个项目,使用树莓派 3 创建一个界面来监视和控制温室中的元素。目前该程序通过 DHT11 传感器读取温度和湿度,并通过 GPIO 引脚控制多个继电器和伺服。

我创建了一个 GUI 来显示每 250 毫秒读取和更新的温度和湿度。还有一些按钮可以控制特定的继电器/伺服。

我现在遇到了一些问题,即按下按钮时 tkinter GUI 冻结。我在论坛上看了一点,但不明白如何实现线程或检查功能以防止我的 GUI 冻结。

代码如下:

from tkinter import *
import tkinter.font
import RPi.GPIO as GPIO
import time
import Adafruit_DHT

#Logic Setup

temp = 0
humd = 0

#GPIO Setup
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(16, GPIO.OUT) #Water Pump
GPIO.setup(18, GPIO.IN)  #Tank Float Switch
GPIO.output(16, GPIO.LOW)

#Window Setup

win = Tk()
win.title("Test")
win.geometry("200x300+0+0")

#Label Setup

Label (win, text="Temperature", fg="red", bg="black", font="24").grid(row=0, column=0)
Label (win, text="Humidity", fg="red", bg="black", font="24").grid(row=0, column=2)
Label (win, text="Water System", fg="red", bg="black", font="24").grid(row=3, column=0)

TEMP = Label (win, text="", fg="black", bg="white", font="36")
TEMP.grid(row=1, column=0)

HUMD = Label (win, text="", fg="black", bg="white", font="36")
HUMD.grid(row=1, column=2)

#Functions

def wait(time_lapse):
    time_start = time.time()
    time_end = (time_start+time_lapse)

    while time_end >= time.time():
        pass

def RTEMP ():
    global temp
    humidity, temperature = Adafruit_DHT.read_retry(11, 27)
    temp = temperature * 9/5.0 + 32
    TEMP.configure(text=str(temp))

def RHUMD ():
    global humd
    humidity, temperature = Adafruit_DHT.read_retry(11, 27)
    humd = humidity
    HUMD.configure(text=str(humd))        

def READ ():
    RTEMP()
    RHUMD()
    win.after(250, READ)

def PUMP ():
    if GPIO.input(18):
        WTR.config(bg="green")
        GPIO.output(16, GPIO.HIGH)
        wait (10)
        GPIO.output(16, GPIO.LOW)
        WTR.config(text="Water", bg="grey")
    else:
        GPIO.output(16, GPIO.LOW)
        WTR.config(text="LOW WATER", bg="red")

#Buttons

WTR = Button(win, text="Water", bg="grey", command = PUMP, height = 2, width = 8)
WTR.grid(row=4, column=0) #Water Pump Control

#Function Calls

READ()

mainloop()

【问题讨论】:

一个问题的代码太多了。您能否将其缩减为仍然可以重现您的问题但代码行数要少得多的东西?例如,如果问题是单击按钮时 GUI 冻结,则您只需要该按钮的代码。见***.com/help/mcve @BryanOakley 好的,我把它剪掉了。所以现在运行时,GUI 在 10 秒 wait() 函数期间冻结。我也注意到它在单击时不会改变按钮颜色,但是...... 你说得对,需要实现线程。我建议pyQT。为了节省您一些时间 - 您将在 Google 上搜索一个非常简单的如何使用线程的版本很长一段时间,然后才意识到您确实必须通过一些看起来很复杂的脚本才能理解它。以下是一些我发现有助于您入门的 *** 问题:1、2 wait 的目的是什么:它真的只是在等待,还是您在尝试模拟一些实际上需要时间才能完成的其他过程?这是一个重要的区别,因为等待与实际工作完全不同。 我只是写了一个等待函数,因为 time.sleep() 会停止脚本。其目的是等待而不暂停脚本。 【参考方案1】:

Tkinter GUI(以及大多数其他 GUI)处于永久等待状态。没有理由引入显式等待事件。

如果您需要运行某个函数,然后在 10 毫秒后运行某个其他函数,您可以安排其他代码使用 after 运行。例如:

    GPIO.output(16, GPIO.HIGH)
    win.after(10, GPIO.output, 16, GPIO.LOW)

当然,如果您想做不止一件事,您可以使用lambda 或编写另一个函数。

【讨论】:

他这里不是为了简化代码的目的而使用等待功能吗?换句话说,wait 函数只是代替了在硬件上需要几秒钟才能完成的进程?他提到他正在尝试构建他的程序以包含线程 - 这将是一个更强大的解决方案,即使实施起来更具挑战性。 @chase:这是个好问题。由于在调用wait 之后有代码,我认为这只是为了等待一段时间。 @BryanOakley 好的,我不知道永久等待状态。使用 win.after 是有道理的。我将实施这一点。但是,我使用 win.after(250, READ) 来刷新 GUI 上的 temp 和 humd 变量。我注意到在这些过程中仍然存在一些滞后/冻结。我想我的下一步是深入研究线程并进行实验? 如果您有阻塞的函数(即:您从传感器读取并等待数据可用),或者如果您调用的函数在返回前花费了超过几百毫秒,那么线程或多处理是一种解决方案。归根结底,tkinter 程序在单个线程中运行,因此一次只能做一件事。如果这些“事物”中的任何一个需要很长时间,GUI 将在这些“事物”完成之前无响应。但是,如果检查一个传感器只需要几毫秒,那么您可以轻松地检查几十个没有线程的传感器。

以上是关于Tkinter GUI 冻结 - 解除阻塞/线程的提示?的主要内容,如果未能解决你的问题,请参考以下文章

Python & Tkinter -> 关于调用冻结程序的长时间运行函数

树莓派、tkinter 和线程

多线程 Python Tkinter 串行监视器中的按钮问题

在 Tkinter 中执行函数期间程序冻结

如何在不冻结 GUI 的情况下在单个插槽中实现阻塞进程?

如何解除阻塞已删除命名管道上的线程阻塞?