检测机械按钮按下Python3和QT5

时间:2018-01-29 23:38:02

标签: python pyqt pyqt5 raspberry-pi3 gpio

我有一个在Python 5.3和PyQt 7.1(我认为)中开发的应用程序。到目前为止,我在GUI中的一切工作都很好。我现在需要能够通过GPIO.add_event_detect检测到两个外部机械按钮的按下。我添加了用于检测信号下降沿的GPIO命令,当我尝试执行代码时,我得到了已经为此GPIO通道启用的冲突边缘检测。" 我尝试将代码移动到不同的类(它不是在while循环中),只是为了看看我是否会得到不同的错误。 我正在寻找任何文档或说明,解释如何将add_event_detect用于基于事件的应用程序,如QT。
一如既往,非常感谢! 迈克

编辑: 我尝试了下面的建议,我遇到执行问题。下面的代码只有三个类 - 我没有添加可执行代码,因为我不确定是否有人想花时间为GPIO引脚添加2个N.C.按钮。我希望你能够通过阅读代码来看到问题。

ExecuteSession类工作正常。我试图增加读取GPIO引脚16和16上两个信号下降沿的能力。 18个PStop和EStop课程。当ExecuteSession启动时,我可以看到打印语句"在PStop"和#34;在EStop"。当我按下按钮时,我没有看到其他打印声明。我是Python和Threads的新手,我很感激任何建议。

我确实创建了一个小型测试程序,以确保Pi看到状态转换,并且确实如此。

由于

class PStop(QThread):

    PT_event_detected = pyqtSignal(int)
    def __init__(self):
        QThread.__init__(self)
        self.queue=Queue()
        GPIO.add_event_detect(16, GPIO.FALLING, callback=self.queue.put)
        print("In PStop")

        def run(self):
            while True:
                print("In PStop Run")
                self.PT_event_detected.emit(self.queue.get())
                print("PT EVENT " +str(self.queue.get()))

class EStop(QThread):

    ER_event_detected = pyqtSignal(int)
    def __init__(self):
        QThread.__init__(self)
        self.queue=Queue()
        GPIO.add_event_detect(18, GPIO.FALLING, callback=self.queue.put)
        print("In EStop")

        def run(self):
            while True:
                print("In EStop Run")
                self.ER_event_detected.emit(self.queue.get())                
                print("ER EVENT " +str(self.queue.get()))

class ExecuteSession(QThread):

    PBValueSig = pyqtSignal(int)
    PBValueDone = pyqtSignal(int)
    PBValuePause = pyqtSignal(int)
    PBTimeActual = pyqtSignal(int)

    durComplete = 0

    def __init__(self, dur='', pause='', stopExe =''):
        QThread.__init__(self)
        self.dur = dur
        self.pause = pause
        self.stopExe = stopExe

        self.E_Stop = EStop()
        self.E_Stop.ER_event_detected.connect(self.Ext_Stop_Detect)
        self.E_Stop.start()

        self.P_Stop = PStop()
        self.P_Stop.PT_event_detected.connect(self.Ext_Stop_Detect)
        self.P_Stop.start()

    def stop(self):
        self.stop()

    def __del__(self):
        self.wait()

    def run(self):
        i = 1
        while i <= dur: #self.dur:
            iA = i
            if self.stopExe == True:
                durComplete = i
                i = self.dur + 1 #Complete

            elif self.pause == False: #12/15 added el
                self.PBValueSig.emit(i)
                i = i + 1
                durComplete = i
                time.sleep(1)
        self.PBTimeActual.emit(iA)
        time.sleep(1)
        self.PBValueDone.emit(durComplete)

def Ext_Stop_Detect(self, channel):
    print("CHANNEL " +str(channel))

1 个答案:

答案 0 :(得分:2)

您看到的错误可能是由于this问题造成的。虽然你可能实际上并没有像那个问题那样在while循环中使用它,但是你很可能 多次调用它,因为你已经在Qt回调或类似中得到它。无论如何......如何将它与Qt整合......

GPIO.add_event_detect方法在GPIO端口的状态更改时执行回调。监视GPIO的代码在单独的线程中运行,这意味着回调在单独的线程中运行。这给与Qt集成带来了一些问题,因为你需要确保以线程安全的方式执行它(如果你做错了,你可能最终会得到像this这样的错误。)

一个简单的解决方案是假设从Python线程调用时,Qt信号是线程安全的(这是G​​PIO库将使用的)。就我所知,这是一个悬而未决的问题,因为从技术上讲,它们被记录为从Python线程使用时不是线程安全的,但通常我发现它没问题。如果你想偶尔冒一个程序崩溃的风险,那么你就是“简单地”这样做的。

我假设你的程序中有一个QMainWindow的子类,但这可以添加到任何Qt类中:

class MainWindow(QMainWindow):
    event_detected = pyqtSignal(int)
    def__init__(self, *args, **kwargs):
        QMainWindow.__init__(self, *args, **kwargs)

        self.event_detected.connect(self.on_gpio_event)
        GPIO.add_event_detect(channel, GPIO.BOTH, callback=self.event_detected.emit)

    def on_gpio_event(self, channel):
        # your code here!
        print("An event occurred on channel {}".format(channel))

或者,这样做“绝对安全”的方式涉及更多的工作。在这里,您告诉GPIO库将任何事件放入线程安全的Python队列中,该队列由QThread读取线程,该队列发出Qt信号(Qt信号在从QThreads使用时绝对是线程安全的)

from six.moves.queue import Queue

class RelayThread(QThread):
    event_detected = pyqtSignal(int)
    def __init__(self, *args, **kwargs):
        QThread.__init__(self, *args, **kwargs)
        self.queue = Queue()
        GPIO.add_event_detect(channel, GPIO.BOTH, callback=self.queue.put)

    def run(self):
        while True:
            self.event_detected.emit(self.queue.get())

class MainWindow(QMainWindow):
    def__init__(self, *args, **kwargs):
        QMainWindow.__init__(self, *args, **kwargs)

        self.relay_thread = RelayThread()
        self.relay_thread.event_detected.connect(self.on_gpio_event)
        self.relay_thread.start()

    @pyqtSlot(int)
    def on_gpio_event(self, channel):
        # your code here!
        print("An event occurred on channel {}".format(channel))

无论如何,希望有所帮助。如果没有特定的代码,你就会有点难以做到,但总的来说,这就是你如何将覆盆子pi上的GPIO端口与Qt事件循环接口。