【问题标题】:Using an IF statement in Python inside of a main() function在 main() 函数内部使用 Python 中的 IF 语句
【发布时间】:2019-02-05 02:41:49
【问题描述】:

我正在尝试创建一个程序来检测三个不同按钮的状态,这些按钮连接到 Raspberry Pi 上的 GPIO 引脚,一旦所有三个按钮都为高电平,就会采取行动。现在我的所有按钮都通过回调函数单独工作,但“main”函数中的if 语句似乎没有运行。

这是我第一次使用 Python,如果您在我的代码结构中发现任何其他逻辑错误,请告诉我。仍在尝试掌握它,尤其是 GPIO 库函数。在此先感谢,我已经在下面发布了我的代码。

import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)

butOne = False
butTwo = False
butThree = False

# Setup button inputs
GPIO.setup(19, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(20, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(21, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

GPIO.add_event_detect(19, GPIO.RISING)
GPIO.add_event_detect(20, GPIO.RISING)
GPIO.add_event_detect(21, GPIO.RISING)

def butOne_callback(channel1):
    print("Button 1 /n")
    butOne = True

def butTwo_callback(channel2):
    print("Button 2 /n")
    butTwo = True

def butThree_callback(channel3):
    print("Button 3 /n")
    butThree = True

def main():
    GPIO.add_event_callback(19, butOne_callback)
    GPIO.add_event_callback(20, butTwo_callback)
    GPIO.add_event_callback(21, butThree_callback)

    if (butOne == True) and (butTwo == True) and (butThree == True):
        print("All Depressed")
main()

更新的代码,根据 Aditya Shankar 的建议:

import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)

GPIO.setup(19, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(20, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(21, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

GPIO.add_event_detect(19, GPIO.RISING)
GPIO.add_event_detect(20, GPIO.RISING)
GPIO.add_event_detect(21, GPIO.RISING)

def butOne_callback(channel1):
    print("Button 1 /n")
    butOne = True
    check_all_depressed()

def butTwo_callback(channel2):
    print("Button 2 /n")
    butTwo = True
    check_all_depressed()

def butThree_callback(channel3):
    print("Button 3 /n")
    butThree = True
    check_all_depressed()

def check_all_depressed():
    if butOne and butTwo and butThree:
        print("All Depressed")

GPIO.add_event_callback(19, butOne_callback)
GPIO.add_event_callback(20, butTwo_callback)
GPIO.add_event_callback(21, butThree_callback)

运行代码并按下按钮时收到错误:

Traceback(最近一次调用最后一次): 文件“/home/pi/Downloads/GPIO_test_06.py”,第 21 行,在 butTwo_callback check_all_depressed() 文件“/home/pi/Downloads/GPIO_test_06.py”,第 29 行,在 check_all_depressed 如果 butOne 和 butTwo 和 butThree: NameError: name 'butOne' 未定义

【问题讨论】:

  • 尝试将 if 语句包装在 while True 循环中,看看会发生什么(将 while True 放在 if 语句上方,然后在其下方缩进 2 行)。
  • 问题还出在其他函数上,不仅仅是main()。见stackoverflow.com/questions/14421733/…
  • 每个写入全局变量的函数都需要一个全局语句,列出它写入的全局变量的名称。

标签: python raspberry-pi gpio


【解决方案1】:

答案:

删除 if 条件

添加一个函数 check_all_depressed()

将函数添加到所有三个按钮回调的末尾,像这样

def butOne_callback(channel1):
    global butOne
    print("Button 1 /n")
    butOne = True
    check_all_depressed()

check_all_depressed 看起来像这样 -

def check_all_depressed():
    if butOne and butTwo and butThree:
        print("All Depressed")

说明: 所以,有回调,还有一般的程序流程。 基本上python程序遵循事件发生的顺序,即从上到下,回调发生在这个流程之外。

【讨论】:

  • 感谢您的回复。我实现了您的代码并收到未定义的变量错误。我认为的问题是变量不是全局的,因此是在单个按钮回调中定义的,而不是在“check_all”函数中定义的。我考虑过传入变量,但我不确定这将如何工作,因为每个回调都有一个它会传入的单独变量。如何在回调之外跟踪 butOne、butTwo 和 butThree 变量?感谢您的任何建议,我会将更新后的代码添加到 OP 中。
  • @BraytonLarson 修复了这个问题,global 需要更改全局变量,对不起!
【解决方案2】:

您的if 语句运行,但只运行一次 - 即在脚本首次启动时立即运行。到那时,按钮还没有被按下,因此它似乎不起作用。

解决此问题的一种方法是将语句放入一个具有小延迟的循环中,并测试该循环中的条件。比如:

import time

while not condition:
    time.sleep(1)

另一个问题是风格。你可以写下你的条件:

(butOne == True) and (butTwo == True) and (butThree == True)

简单来说:

butOne and butTwo and butThree

因为它们都是布尔值。在 Python 中,您甚至可以编写:

all([butOne, butTwo, butThree])

这不是更短,但如果你有更多的条件,它会避免一次又一次地重复and

最后,您选择创建一个运行主程序的主函数。将函数定义上方的所有代码也包含在其中可能是个好主意。毕竟,它都是你的主程序的一部分,而且都意味着只运行一次。这样,您还可以避免在函数内部意外使用全局变量,这可能会导致意外(但技术上正确)的行为。

【讨论】:

    【解决方案3】:

    从根本上说,GPIO 包支持事件的方式是等待您选择的通道上的上升沿和下降沿。这是在后台线程中完成的,无论主线程中发生什么。您的if 语句在配置按钮后立即运行一次,然后主线程结束。

    您可以实施两种类型的解决方案。一种是强制主线程等待状态变化。另一种是在回调中响应状态变化。

    强制main等待:

    import RPi.GPIO as GPIO
    
    channels = [19, 20, 21]
    
    def main():
         GPIO.setmode(GPIO.BCM)
         for chan in channels:
             GPIO.setup(chan, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
         for chan in channels:
             GPIO.wait_for_edge(chan, GPIO.RISING)
    
         # Now all your buttons have been pressed
    

    这可能是更有效的方法。最少的设置,没有显式的多线程。

    另一种方法是在单独的线程中监听输入。您可以将回调配置为响应上升沿,就像对 add_event_callback 所做的那样,但请记住,该函数主要用于设置多个回调。更简洁的方法是将对add_event_detect 的调用移动到main 并将它们与add_event_callback 结合起来:

    GPIO.add_event_detect(chan, GPIO.RISING, callback=...)
    

    从编程的角度来看,我会使用这样一个事实,即所有通道的处理方式几乎相同,并且只定义一个回调。事实上,设置中最重要的是频道号和频道名称:

    import RPi.GPIO as GPIO
    
    channels = {19: 1, 20: 2, 21: 3}
    
    class callback:
        def __init__(self):
             self.pressed = dict.fromkeys(channels, False)
        def __call__(self, channel):
             print(f'Button {channels[channel]} pressed')
             self.pressed[channel] = True
             if sum(self.pressed.values()) == len(self.pressed):
                 # All buttons have been pressed
    
    def main():
         GPIO.setmode(GPIO.BCM)
         cb = callback()
         for chan in channels:
             GPIO.setup(chan, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
             GPIO.add_event_detect(chan, GPIO.RISING, callback=cb)
    

    请注意,在这两个示例中,我都避免了通道配置之外的全局状态。第二种方法将回调设置为可调用类的实例以执行此操作。另一种方法是将回调定义为main 中的嵌套函数。

    【讨论】:

    • 感谢您抽出宝贵时间回复并撰写详细说明。不过,我对您的第二个解决方案有几个问题: 1. 首先,当我运行代码时,显然它对我不起作用,没有产生任何输出。 2. 你写的代码是独立运行还是我需要在你写的函数之外做额外的设置。 3. “chan”和“cb”如果只是设置为等于“channel”和“callback”,它们的目的是什么。显然,我需要学习很多关于 Python 语法的知识,感谢您花时间提供的任何解释。
    • cb 是回调类的一个实例。 chan 是一个循环变量。依次取channels的每个元素的值。
    • 您可能需要获取对 gpio 后台线程的引用并从 main 加入它,因为它可能是一个恶魔线程。
    猜你喜欢
    • 1970-01-01
    • 2019-04-19
    • 1970-01-01
    • 2020-06-09
    • 1970-01-01
    • 1970-01-01
    • 2016-03-26
    • 2021-01-13
    • 1970-01-01
    相关资源
    最近更新 更多