在 main() 函数内的 Python 中使用 IF 语句

Using an IF statement in Python inside of a main() function

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

这是我第一次使用 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)

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

回溯(最后一次调用): 文件“/home/pi/Downloads/GPIO_test_06.py”,第 21 行,在 butTwo_callback 中 check_all_depressed() 文件“/home/pi/Downloads/GPIO_test_06.py”,第 29 行,在 check_all_depressed 中 如果但是一个和但是两个和三个: NameError:名称 'butOne' 未定义

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

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

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

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

答案:

删除 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 程序遵循事件发生的顺序,即从上到下,回调发生在此流程之外。

从根本上说,GPIO 包支持事件的方式是等待通道上的上升沿和下降沿 select。这是在后台线程中完成的,而不管主线程中发生了什么。您的 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)

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

中的嵌套函数