Search code examples
pythonpyqtraspberry-piqtimer

periodic polling of port or hardware IO point on Raspberry Pi


In developing an application using Qt5 with Python, you are generally event driven. No sweat, works like a charm. However, there are instances when you need to poll the status of some hardware GPIO (i.e. a button push), or get some information from a serial port, or something like a gpsd daemon.

What is the preferred way to handle this? Via a QTimer, say, running every 50 msec? Or is there some other method I haven't found? Is it better to set up a trigger on a GPIO pi (https://www.ics.com/blog/control-raspberry-pi-gpio-pins-python) or is there any conflict with the Qt5 Gui?

Basic documentation doesn't look horrible, and I can follow some examples, of course, but didn't know if there was a better/canonical/more Pythonic method.

https://doc.qt.io/qtforpython/PySide2/QtCore/QTimer.html

https://python-catalin.blogspot.com/2019/08/python-qt5-qtimer-class.html


Solution

  • I don't think there is a pythonic solution, not because you can't use python but because python is not relevant to the topic. And there is no canonical solution either, everything will depend on the application.

    From my experience I have found it much easier to reuse libraries that handle GPIOs like Rpi.GPIO or gpiozero. These libraries have as a strategy to create threads where the state of the pins is monitored, so you cannot use the callbacks directly to update the GUI but you must implement wrapper(see this for example).

    trivial example:

    import sys
    
    from gpiozero import Button
    
    from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl
    from PyQt5.QtWidgets import QMainWindow, QApplication
    
    
    class ButtonManager(QObject):
        pressed = pyqtSignal()
    
        def __init__(self, parent=None):
            super(ButtonManager, self).__init__(parent)
            self._button = Button(20)
            self._button.when_pressed = self._on_when_pressed
    
        def _on_when_pressed(self):
            self.pressed.emit()
    
    
    class MainWindow(QMainWindow):
        @pyqtSlot()
        def on_pressed(self):
            print("pressed")
    
    
    def main():
    
        app = QApplication(sys.argv)
        w = MainWindow()
    
        button_manager = ButtonManager()
        button_manager.pressed.connect(w.on_pressed)
    
        w.show()
    
        sys.exit(app.exec_())
    
    
    if __name__ == "__main__":
        main()
    

    If you are going to use serial ports then the best option is to use Qt Serial Port since unlike pyserial it is not necessary to use threads but the notification is through signals(See this for example)

    Using a QTimer is another option.