Search code examples
pythonpython-3.xpyqtpyqt5qthread

trying to use 4x4 keypad with pyqt5 gui application


i am trying to write code which take input from 4x4 keypad(Mechanical) using pad4pi module with pyqt5 based GUI application.

when i try to click button it work properly but when i try to genrate some event i get error message:

QObject::startTimer: Timers can only be used with threads started with QThread
class DigitalClock(QWidget,QThread):
    def __init__(self):
        super().__init__()
        SetupKeyboard.keypad.registerKeyPressHandler(self.printKey)
        self.setWindowTitle("OM SAI RAM")
        self.showFullScreen() 
        #self.setCursor(Qt.BlankCursor)
        button = QPushButton("Click", self) 
        button.clicked.connect(self.change)
        button.move(10,10)    
        button.show()

    def change(self):
        self.newpage = Authentication_page()
        self.close()

    def printKey(self, key):
        if key == 'A':
            self.newpage = Authentication_page()
            self.close()

class Authentication_page(QWidget):
    """
    Class to validate authentication.
    """
    def __init__(self):
        super().__init__()
        self.showFullScreen() 
        self.maindesign()

    def maindesign(self):
        """Method to design main page"""
        ####Label###
        self.admin_header = QLabel("Admin Panel", self)
        self.admin_header.setStyleSheet("font-size:40px")
        self.admin_header.move(130, 10)
        self.admin_header.show() 

when i click button code work fine but when i push mechanical button, code freeze with error message.


Solution

  • The handler assigned by registerKeyPressHandler is executed in the thread where the keys are monitored, in your case printKey is executed in a secondary thread where you try to create a widget but that is forbidden by Qt.

    The solution is to create a QObject and emit a signal (since the signals are thread-safe) by sending the key pressed, then connect to a slot where you receive the information:

    from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject
    from PyQt5.QtWidgets import QLabel, QPushButton, QWidget
    
    from pad4pi import rpi_gpio
    
    
    class KeypadManager(QObject):
        keyPressed = pyqtSignal(object)
    
        def __init__(self, parent=None):
            super().__init__(parent)
            factory = rpi_gpio.KeypadFactory()
            self._keypad = factory.create_4_by_4_keypad()
            self._keypad.registerKeyPressHandler(self._key_press_handler)
    
        def _key_press_handler(self, key):
            self.keyPressed.emit(key)
    
    
    class DigitalClock(QWidget):
        def __init__(self, parent=None):
            super().__init__(parent)
            self.setWindowTitle("OM SAI RAM")
            self._keypad_manager = KeypadManager()
            self._keypad_manager.keyPressed.connect(self.printKey)
    
            # self.setCursor(Qt.BlankCursor)
            button = QPushButton("Click", self)
            button.clicked.connect(self.show_authentication_page)
            button.move(10, 10)
    
            self.showFullScreen()
    
        def show_authentication_page(self):
            self.newpage = Authentication_page()
            self.close()
    
        @pyqtSlot(object)
        def printKey(self, key):
            if key == "A":
                self.show_authentication_page()
    
    
    class Authentication_page(QWidget):
        # ...