【问题标题】:UPDATE 2: How Do I Stop a Function in a while loop?更新 2:如何在 while 循环中停止函数?
【发布时间】:2016-06-10 16:23:48
【问题描述】:

更新:

所以我成功地实现了线程,以允许 GUI 在进程运行时保持畅通。现在我正试图弄清楚如何让这个 while 循环中断并仍然正常运行。

我尝试实现第二个变量,while 语句将其视为一个标志,以尝试在其中运行一次 PUMP 函数后打破 while 循环。但是,现在 PUMP 功能根本不运行。 GPIO 引脚永远不会变高。 我要做什么:

-按下按钮。 - 将标志设置为 1 - 如果浮子开关为高位,则在线程中运行 RUN() 函数/如果浮子开关为低位,则发出低水位信号 - RUN() 在运行 PUMP() 函数时检查标志和浮动开关的状态 - PUMP() 将 GPIO 引脚变为高电平,并在 5 秒后调用 OFF() 函数 - OFF () 将标志设置为 0,并将泵 GPIO 设置为低

如果在 PUMP() 期间浮动开关变低,它应该触发并调用 LOW() 函数,通过将 GPIO 引脚设置为低并显示状态来停止泵。这也将标志设置为 0。

代码:

from tkinter import *
import tkinter.font
import RPi.GPIO as GPIO
import threading

#Variables

Flag = 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("Pump Test")
win.geometry("150x150+0+0")

#Label Setup

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

#Functions
def RUN ():
    while GPIO.input(18) and Flag == 1:
        PUMP()
    if Flag == 0:
        OFF()
    elif GPIO.input(18) == False:
        LOW()

def OFF ():
    Flag = 0
    GPIO.output(16, GPIO.LOW)
    WTR.config(text="Water", bg="grey")

def LOW ():
    Flag = 0
    GPIO.output(16, GPIO.LOW)
    WTR.config(text="LOW WATER", bg="red")

def WTR ():
    Flag = 1
    if GPIO.input(18):
        threading.Thread(target=RUN).start()
    if GPIO.input(18)== False:
        threading.Thread(target=LOW).start()

def PUMP (): 
    GPIO.output(16, GPIO.HIGH)
    win.after(5000, OFF)


#Buttons


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


mainloop()

【问题讨论】:

  • 很明显,我不需要使用 wait() 函数而是实现 win.after(time) 。但我仍然不确定如何中断实际过程。也许是一个 do-while 逻辑?
  • 看来我实际上需要研究线程。一个线程处理 GUI 和 Pump,一个线程设置标志和跟踪 Float 传感器。
  • 这不会有太大帮助,但摆脱繁忙的循环,只需使用time.sleep()
  • 你可能想看看这个:stackoverflow.com/questions/459083/…
  • @mauenza: time.sleep() 在 GUI 的主线程中是个坏主意。它将阻止 GUI 响应所有事件,包括与刷新窗口相关的事件。

标签: python-3.x tkinter raspberry-pi


【解决方案1】:

为确保 UI 对用户事件(鼠标点击等)和系统事件(如曝光和重绘)保持响应,您不应在函数中进入长期循环,也不要使用 sleep。取而代之的是,Tkinter 提供了after 方法,允许您安排在某个时间间隔后完成的事情。此调用将您的调用添加到事件队列中,并由mainloop 调用的代码及时处理。对于延迟后应该发生的事情,显然使用了after(num_millis)。如果您需要轮询 pin 的状态,则使用较短的时间并在处理程序中,设置另一个 after 调用以再次调用您的轮询函数。请注意,您可以取消 after 调用,前提是您保留调用该方法时返回的 id 值。

不要使用time.sleep。睡眠期间不会处理任何 UI 事件,并且 UI 将死机。使用after

【讨论】:

  • 我明白你对 after 方法的看法。但是,尚不清楚您对解决方案的建议对我如何以我正在寻找的方式打破循环。我再次更新代码,尝试使用变量作为标志,但现在一起遇到了不同的问题。
【解决方案2】:

您的Flag 是一个模块级变量。

如果您想在函数中修改(不将其作为参数传递),则需要在函数中将其标记为全局

观察:

In [1]: flag = 0

In [2]: def foo(num):
   ...:     flag = num
   ...:     

In [3]: flag
Out[3]: 0

In [4]: foo(4)

In [5]: flag
Out[5]: 0

In [6]: foo(12)

In [7]: flag
Out[7]: 0

调用foo 会设置flag,但这是函数的局部变量!它对模块级对象没有影响。

你必须明确地说你要修改模块级变量:

In [8]: def bar(num):
   ...:     global flag
   ...:     flag = num
   ...:     

In [9]: bar(4)

In [10]: flag
Out[10]: 4

In [11]: bar(7)

In [12]: flag
Out[12]: 7

【讨论】:

    【解决方案3】:

    感谢@patthoyts 和@RolandSmith 的洞察力帮助我找到了答案。

    他们说的两件事很有帮助 - 不使用 time.sleep 并确保我使用的是全局变量。

    对 Flag 的想法进行了一些返工,而不是休眠,而是创建一个检查函数来查看已经过去了多少时间。暂时删除线程,因为它对 GUI 进程并不可怕。在幕后的帮助下向 Tom Slick 大喊大叫!

    from tkinter import *
    import tkinter.font
    import RPi.GPIO as GPIO
    import time
    
    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)
    
    time_to_run = 60 # time to run the pump in seconds
    time_running = time.time() - 1
    
    #Window Setup
    win = Tk()
    win.title("Pump Test")
    win.geometry("150x150+0+0")
    
    #Label Setup
    Label (win, text="Water System", fg="red", bg="black", font="24").grid(row=0, column=0)
    
    
    #Functions
    def off():
        WTR.config(text="LOW WATER", bg="red")
        GPIO.output(16, GPIO.LOW)
    
    
    def wtr():
        global time_running
    
        time_running = time.time() + time_to_run
        if GPIO.input(18) and not GPIO.input(16):
            pump_on()
    
        elif GPIO.input(16):
            pump_off()
    
    
    def pump_on():
        WTR.config(text="Water", bg="green")       
        GPIO.output(16, GPIO.HIGH)
    
    
    # added to turn the pump off manualy
    def pump_off():
        WTR.config(text="Water", bg="grey")       
        GPIO.output(16, GPIO.LOW)
    
    
    def check_pump():
        # if the water level goes low turn the pump off
        # and show low water level
        if not GPIO.input(18):
            off()
    
        # if a set time has passed then shut the pump off it it is running
        if time_running <= time.time() and GPIO.input(16):
            pump_off()
    
        win.after(100, check_pump) # call this function every 100 miliseconds
    
    
    #Buttons
    WTR = Button(win, text="Water", bg="grey", command = wtr, height = 2, width = 8)
    WTR.grid(row=1, column=0) #Water Pump Control
    
    check_pump() # must be called once to start the after function
    mainloop()
    

    【讨论】:

      猜你喜欢
      • 2018-08-15
      • 1970-01-01
      • 2010-09-26
      • 2013-09-11
      • 2023-01-10
      • 2015-10-04
      • 1970-01-01
      • 2016-10-07
      • 1970-01-01
      相关资源
      最近更新 更多