使用 Python3 和 QT5 检测机械按钮按下
Posted
技术标签:
【中文标题】使用 Python3 和 QT5 检测机械按钮按下【英文标题】:Detecting Mechanical Button Press with Python3 and QT5 【发布时间】:2018-01-29 23:38:02 【问题描述】:我有一个使用 Python 5.3 和 PyQt 7.1 开发的应用程序(我认为)。到目前为止,我在 GUI 中的一切工作都很好。我现在需要能够通过 GPIO.add_event_detect 检测两个外部机械按钮的按下。我添加了用于检测信号下降沿的 GPIO 命令,当我尝试执行代码时,我得到“已为此 GPIO 通道启用冲突沿检测”。 我尝试将代码移动到不同的类(它不在while循环中)只是为了看看我是否会得到不同的错误。 我正在寻找任何说明如何将 add_event_detect 用于 QT 等基于事件的应用程序的文档或说明。 一如既往,非常感谢! 迈克
编辑: 我尝试了下面的建议,但我遇到了执行问题。下面的代码只有三个类——我没有添加可执行代码,因为我不确定是否有人愿意花时间将 2 个 N.C. 按钮添加到 GPIO 引脚。我希望您只需阅读代码就能看到问题。
ExecuteSession 类工作正常。我正在尝试添加读取 PStop 和 EStop 类中 GPIO 引脚 16 和 18 上两个信号的下降沿的能力。当 ExecuteSession 启动时,我可以看到打印语句“In PStop”和“In 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))
【问题讨论】:
PyQt 7.1 ??????Python 5.3 ?????什么??你在一个非常遥远的未来 显示你的代码。 呃……我老了! Python 3.5 和 QT 5.? 你的问题不清楚,编辑你的问题并添加你尝试过的代码,除了解释你的问题是什么。 如果你用raspberry-pi标签标记它也会有所帮助 【参考方案1】:您看到的错误可能是由this 问题引起的。虽然您实际上可能没有像那个问题那样在 while 循环中拥有它,但您可能正在多次调用它,因为您已经在 Qt 回调或类似的回调中获得了它。无论如何...关于如何将其与 Qt 集成...
GPIO.add_event_detect
方法需要回调以在 GPIO 端口的状态更改时执行。监控 GPIO 的代码在单独的线程中运行,这意味着回调在单独的线程中运行。这给与 Qt 的集成带来了一些问题,因为您需要确保以线程安全的方式执行此操作(如果您做错了,您最终可能会遇到类似 this 的错误)。
一个简单的解决方案是假设 Qt 信号在从 Python 线程调用时是线程安全的(这是 GPIO 库将使用的)。据我所知,这是一个悬而未决的问题,因为从技术上讲,它们被记录为在从 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 队列中,该队列由发出 Qt 信号的 QThread 线程读出(从 QThreads 使用时,Qt 信号绝对是线程安全的)
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))
无论如何,希望对您有所帮助。如果没有您提供的特定代码,再做任何事情有点困难,但总的来说,这就是您如何将树莓派上的 GPIO 端口与 Qt 事件循环接口。
【讨论】:
三个菠萝,你的指示。我将审查并尝试,但如果不成功,我将创建一个示例代码并发布。再次感谢。 还有一个问题……线程可以产生线程吗?我已经在使用 QThread/Qt 信号,按钮检测会影响执行线程。那么我可以让一个线程产生另一个线程(嵌套?)还是应该分开? 是的,让线程产生其他线程很好。可能如果它是一个QThread
产生一个QThread
,那么你需要在构造它时正确设置父级(我认为这只是意味着做second_thread = QThread(self)
或类似的)。
three_pineapples,我根据您的建议添加了代码并解释了正在发生的事情。你能看看,看看你有什么建议吗?这是不可执行的,因为它需要两个连接到 GPIO 的 NC 开关。只是寻找核心审查来寻找代码中的明显错误。谢谢
@Mike 我认为您的问题是缩进错误。 a),PStop
和 EStop
类中的 run
方法缩进太远(它们在__init__
方法内部,因此不要正确覆盖QThread.run
)。 b) 您的 run
方法每次迭代从队列中获取两次。这将获得单独的事件。您应该每次迭代只从队列中读取一次。如果您需要使用该值两次,请从队列中读取一个变量,然后发出/打印该变量。 c) 我认为您在 ExecuteSession.run
中有一些缩进错误,但由于这不是这个问题的一部分,我不能 100% 确定。以上是关于使用 Python3 和 QT5 检测机械按钮按下的主要内容,如果未能解决你的问题,请参考以下文章