1

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

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
asylumax
  • 781
  • 1
  • 8
  • 34

1 Answers1

2

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.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Don't know if there is an example for Rpi.GPIO; I will look into the gpiozero example. I'm using QtDesigner, and generate the python with pyuic5 and am unfamiliar with the qml. – asylumax Jul 14 '20 at 13:34
  • 1
    @asylumax The QML is secondary, look only at how I handle the data with gpiozero, search among my answers and you will surely find an example with Rpi.GPIO – eyllanesc Jul 14 '20 at 14:01
  • Thanks; I think the gpiozero is understandable; I checked out https://stackoverflow.com/questions/59678470/updates-can-only-be-scheduled-from-gui-thread-or-from-qquickitemupdatepaintnod/59690726#59690726, and understand some of it. I will update the question. – asylumax Jul 14 '20 at 15:32
  • @asylumax Is it missing to answer something in your question? I think my answer covers everything you indicate in your question or did I miss something? – eyllanesc Jul 14 '20 at 16:00
  • Cleared this up; I get how the '_on_when_pressed' gets called, but can't figure out how it interacts with the GUI. lblImageNote is the label in the MainWindow, but the you get an error "AttributeError: type object 'MainWindow' has no attribute 'lblImageNote'" – asylumax Jul 14 '20 at 16:10
  • Solved! Use 'window' instead of 'MainWindow'! Doh! Don't need the 'emit' either. Staring at the screen too long... – asylumax Jul 14 '20 at 16:12
  • 1
    @asylumax the error you have is trivial, it seems that you have to review your notes about OOP – eyllanesc Jul 14 '20 at 16:14
  • 1
    @asylumax see my trivial example – eyllanesc Jul 14 '20 at 16:21
  • Code fails for me; TypeError: super() takes at least 1 argument (0 given); I think I see where this is going, though. Reviewing signals and slots... – asylumax Jul 14 '20 at 16:38
  • @asylumax Oopss, try again – eyllanesc Jul 14 '20 at 16:43
  • Still failing for me. – asylumax Jul 14 '20 at 16:58
  • @asylumax What version of python do you use? Could you point out the full error message – eyllanesc Jul 14 '20 at 17:06
  • @asylumax You should always point out the version of python that you use, if you do not do it, it is assumed that you use the latest version, so in that version my solution is valid. It seems that you use python2, try again – eyllanesc Jul 14 '20 at 17:09
  • I think that was the issue; on some of my machines, python defaults to python3; on this rPi, it defaults to python 2.7! Thanks for hanging with me. All works, and I understand this slot/signal bit. Very clear. – asylumax Jul 14 '20 at 17:11