Hallteon
Hallteon

Reputation: 105

How to stop waiting for a keypress in a separate thread module in keyboard + PyQt5?

I'm writing a PyQt5 GUI program to control drones and I need to track keystrokes. I keep track of them in a separate thread created with QThread. When I press the button for the first time, the keystroke tracking should start, but the second time I press the button, the flow should stop and the keystroke tracking too. Here is my code:

class Keybord_Recognition(QtCore.QObject):

    def __init__(self):
        super(Keybord_Recognition, self).__init__()

    def key_recog(self, k):
        if k.event_type == 'down':
            if k.name == 'w':
                print('forward')

            elif k.name == 's':
                print('back')

            elif k.name == 'a':
                print('left')

            elif k.name == 'd':
                print('right')

            elif k.name == 'z':
                print('up')

            elif k.name == 'x':
                print('down')

    def run(self):
        keyboard.hook(self.key_recog)
        keyboard.wait('p')

class Ui_MainWindow(object):

    def setupUi(self, MainWindow):
        self.control_btn = QtWidgets.QPushButton(self.centralwidget)
        self.control_btn.setGeometry(QtCore.QRect(480, 60, 160, 150))
        self.control_btn.setObjectName("control_btn")
        self.control_btn.setCheckable(True)
        self.control_btn.clicked.connect(self.kboard_recognition)
        self.control_btn.setStyleSheet("QPushButton{background-color: #aae053;\n"
                                   "border-radius: 60%;\nbackground-image: url('images/pult.png');\n"
                                   "background-repeat: no-repeat;\nbackground-position: center;}\n"
                                   "QPushButton:hover{background-color: #81eb3b;}")

    def kboard_recognition(self):
        if self.control_btn.isChecked():
            self.control_btn.setStyleSheet("QPushButton{background-color: red;\n"
                                       "border-radius: 60%;\nbackground-image: url('images/pause.png');\n"
                                       "background-repeat: no-repeat;\nbackground-position: center;}\n")
            self.thread_kboard = QtCore.QThread()
            self.k_recog = Keybord_Recognition()

            self.k_recog.moveToThread(self.thread_kboard)
            self.thread_kboard.started.connect(self.k_recog.run)
            self.thread_kboard.start()

        else:
            self.control_btn.setStyleSheet("QPushButton{background-color: #aae053;\n"
                                       "border-radius: 60%;\nbackground-image: url('images/pult.png');\n"
                                       "background-repeat: no-repeat;\nbackground-position: center;}\n"
                                       "QPushButton:hover{background-color: #81eb3b;}")

            keyboard.send('q')
            self.thread_kboard.terminate()

But when I press the button a second time, the keyboard doesn't stop tracking. How to fix it?

Upvotes: 0

Views: 208

Answers (1)

eyllanesc
eyllanesc

Reputation: 244282

It is not necessary to use threads to use keyboard together with Qt, on the other hand you have to remove the callback so that the events are no longer parsed.

import keyboard

from PyQt5 import QtCore, QtWidgets


class QKeyBoard:
    def _hook_callback(self, k):
        if k.event_type == "down":
            if k.name == "w":
                print("forward")

            elif k.name == "s":
                print("back")

            elif k.name == "a":
                print("left")

            elif k.name == "d":
                print("right")

            elif k.name == "z":
                print("up")

            elif k.name == "x":
                print("down")

    def start(self):
        keyboard.hook(self._hook_callback)

    def stop(self):
        keyboard.unhook(self._hook_callback)


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        self.centralwidget = QtWidgets.QWidget()
        self.setCentralWidget(self.centralwidget)
        self.control_btn = QtWidgets.QPushButton(self.centralwidget)
        self.control_btn.setGeometry(QtCore.QRect(480, 60, 160, 150))
        self.control_btn.setObjectName("control_btn")
        self.control_btn.setCheckable(True)
        self.control_btn.setStyleSheet(
            "QPushButton{\n"
            "background-color: #aae053;\n"
            "border-radius: 60%;\n"
            "background-image: url('images/pult.png');\n"
            "background-repeat: no-repeat;\n"
            "background-position: center;}\n"
            "QPushButton:hover{\n"
            "background-color: #81eb3b;\n"
            "}"
            "QPushButton:checked{\n"
            "background-color: red;\n"
            "border-radius: 60%;\n"
            "background-image: url('images/pause.png');\n"
            "background-repeat: no-repeat;\n"
            "background-position: center;\n"
            "}"
        )


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)

        self.qkeyboard = QKeyBoard()
        self.control_btn.toggled.connect(self.handle_toggled)

    def handle_toggled(self, state):
        if state:
            self.qkeyboard.start()
        else:
            self.qkeyboard.stop()


def main():
    import sys

    app = QtWidgets.QApplication(sys.argv)

    w = MainWindow()
    w.resize(640, 480)
    w.show()

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

Upvotes: 1

Related Questions