简体   繁体   中英

Correct handling of KeyEvent in Pyqt5, problem with catching KeyPressEvent

I have KeyEvent(self, event) function and transfer event.key() to function in module MoveKeyboard but only catch KeyReleaseEvent . I need to handle the release and continuous holding of the button and execute appropriate action . I tested KeyEvent(self, event) and in console output is only: 1: Release .

Joy, rviz, ..., move_keyboard have PyQt5.QtWidgets.QWidget class.

Main window code:

class MainWindow(PyQt5.QtWidgets.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__(None)
        self.title = 'Robot teleoperation'
        self.left = 10
        self.top = 10
        self.width = 1920
        self.height = 1080
        rospy.init_node("gui_node")

        #self.joy = Joystick(maxDistance=50,MinimumSize=100,EclipseX=-20,EclipseY=40)
        #self.rviz = Rviz()
        #self.arm_position = BaseArmPosition()
        #self.laser_position = LaserPosition()
        #self.move_slider = MoveSlider()
        #self.arm_slider = ArmSlider()
        self.move_keyboard = MoveKeyboard()

        self.initUI()


    def initUI(self):
        self.central_widget = PyQt5.QtWidgets.QWidget()
        self.setCentralWidget(self.central_widget)

        grid = PyQt5.QtWidgets.QGridLayout(self.centralWidget())

        #grid.addWidget(self.rviz, 0, 0)
        #grid.addWidget(self.joy, 0, 1)
        #grid.addWidget(self.arm_slider,0,2)

        #grid.addWidget(self.arm_position, 1, 0)
        #grid.addWidget(self.move_slider, 1,1)
        grid.addWidget(self.move_keyboard,0,0)

        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
        self.show() 

    def keyPressEvent(self, event):
        print("1: Press")
        self.move_keyboard.PressEvent(event)
        if event.key() == PyQt5.QtCore.Qt.Key_Control:
            print("2: Press")
            self.move_keyboard.PressEvent(event)
        return super(MainWindow, self).keyPressEvent(event)

    def keyReleaseEvent(self, event):
        print("1: Release")
        self.move_keyboard.ReleaseEvent(event)
        if event.key() == PyQt5.QtCore.Qt.Key_Control:
            print("2: Release")
            self.move_keyboard.ReleaseEvent(event)
        return super(MainWindow, self).keyReleaseEvent(event)

Class MoveKeyoard:

import PyQt5
import rospy
from geometry_msgs.msg import Twist


class MoveKeyboard(PyQt5.QtWidgets.QWidget):
    def __init__(self, *args, **kwargs):
        super(MoveKeyboard, self).__init__(*args, **kwargs)            
        self.Up = PyQt5.QtWidgets.QLabel("Up(W)")
        self.Left = PyQt5.QtWidgets.QLabel("Left(A)")
        self.Right = PyQt5.QtWidgets.QLabel("Right(D)")
        self.Down = PyQt5.QtWidgets.QLabel("Down(S)")

        self.LinearValue = PyQt5.QtWidgets.QDoubleSpinBox(self)
        self.AngularValue = PyQt5.QtWidgets.QDoubleSpinBox(self)

        self.label_linear = PyQt5.QtWidgets.QLabel("Linear speed")
        self.label_angular = PyQt5.QtWidgets.QLabel("Angular speed")

        self.initUI()


    def initUI(self):
        self.LinearValue.setMaximum(1.00)
        self.LinearValue.setMinimum(0.00)
        self.LinearValue.setSingleStep(0.01)

        self.AngularValue.setMaximum(1.00)
        self.AngularValue.setMinimum(0.00)
        self.AngularValue.setSingleStep(0.01)

        layout = PyQt5.QtWidgets.QGridLayout(self)

        layout.addWidget(self.label_linear,0,0)
        layout.addWidget(self.LinearValue,0,1)
        layout.addWidget(self.label_angular,1,0)
        layout.addWidget(self.AngularValue,1,1)

        layout.addWidget(self.Up,2,1)

        layout.addWidget(self.Left,3,0)
        layout.addWidget(self.Down,3,1)
        layout.addWidget(self.Right,3,2)


        self.pub = rospy.Publisher('cmd_vel', Twist, queue_size = 1)

    def PressEvent(self, e):
        if e.key() == PyQt5.QtCore.Qt.Key_W:
            self.Up.setStyleSheet('color: red')
            self.do("W")
        if e.key() == PyQt5.QtCore.Qt.Key_S:
            self.Down.setStyleSheet('color: red')
            self.do("S")
        if e.key() == PyQt5.QtCore.Qt.Key_A:
            self.Left.setStyleSheet('color: red')
            self.do("A")
        if e.key() == PyQt5.QtCore.Qt.Key_D:
            self.Right.setStyleSheet('color: red')
            self.do("D")

    def ReleaseEvent(self, e):
        if e.key() == PyQt5.QtCore.Qt.Key_W:
            self.Up.setStyleSheet('color: black')
            self.do("---W")
        if e.key() == PyQt5.QtCore.Qt.Key_S:
            self.Down.setStyleSheet('color: black')
            self.do("---S")
        if e.key() == PyQt5.QtCore.Qt.Key_A:
            self.Left.setStyleSheet('color: black')
            self.do("---A")
        if e.key() == PyQt5.QtCore.Qt.Key_D:
            self.Right.setStyleSheet('color: black')
            self.do("---D")

    def do(self,str_):
        print(str_)

If a widget consumes the QKeyEvent event ( event.accept() ) then that event will not be propagated, in this case QDoubleSpinBox consumes the events they use as a Qt.ControlModifier modifier so that other widgets will not receive the press, only the release.

So the solution in this case is to prevent the QDoubleSpinBox from consuming the combination Ctrl + W , Ctrl + S , Ctrl + A and Ctrl + D .

On the other hand I do not need the window to handle these events, I think it is better for the widget itself to handle them.

from PyQt5 import QtCore, QtWidgets


class DoubleSpinBox(QtWidgets.QDoubleSpinBox):
    def keyPressEvent(self, event):
        if event.modifiers() == QtCore.Qt.ControlModifier and event.key() in (
            QtCore.Qt.Key_W,
            QtCore.Qt.Key_S,
            QtCore.Qt.Key_A,
            QtCore.Qt.Key_D,
        ):
            event.ignore()
        else:
            super(DoubleSpinBox, self).keyPressEvent(event)


class MoveKeyboard(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(MoveKeyboard, self).__init__(parent)
        self.Up = QtWidgets.QLabel("Up(W)")
        self.Left = QtWidgets.QLabel("Left(A)")
        self.Right = QtWidgets.QLabel("Right(D)")
        self.Down = QtWidgets.QLabel("Down(S)")

        self.LinearValue = DoubleSpinBox()
        self.AngularValue = DoubleSpinBox()

        self.label_linear = QtWidgets.QLabel("Linear speed")
        self.label_angular = QtWidgets.QLabel("Angular speed")

        self.initUI()

    def initUI(self):
        self.LinearValue.setMaximum(1.00)
        self.LinearValue.setMinimum(0.00)
        self.LinearValue.setSingleStep(0.01)

        self.AngularValue.setMaximum(1.00)
        self.AngularValue.setMinimum(0.00)
        self.AngularValue.setSingleStep(0.01)

        layout = QtWidgets.QGridLayout(self)

        layout.addWidget(self.label_linear, 0, 0)
        layout.addWidget(self.LinearValue, 0, 1)
        layout.addWidget(self.label_angular, 1, 0)
        layout.addWidget(self.AngularValue, 1, 1)

        layout.addWidget(self.Up, 2, 1)

        layout.addWidget(self.Left, 3, 0)
        layout.addWidget(self.Down, 3, 1)
        layout.addWidget(self.Right, 3, 2)

    def get_widget_by_key(self, key):
        d = {
            QtCore.Qt.Key_W: ("W", self.Up),
            QtCore.Qt.Key_S: ("S", self.Down),
            QtCore.Qt.Key_A: ("A", self.Left),
            QtCore.Qt.Key_D: ("D", self.Right),
        }
        return d.get(key, ("", None))

    def keyPressEvent(self, event):
        if event.modifiers() == QtCore.Qt.ControlModifier and not event.isAutoRepeat():
            letter, widget = self.get_widget_by_key(event.key())
            if widget is not None:
                widget.setStyleSheet("color: red")
                print(letter)
        super(MoveKeyboard, self).keyPressEvent(event)

    def keyReleaseEvent(self, event):
        if event.modifiers() == QtCore.Qt.ControlModifier and not event.isAutoRepeat():
            letter, widget = self.get_widget_by_key(event.key())
            if widget is not None:
                widget.setStyleSheet("color: black")
                print("--{}".format(letter))
        super(MoveKeyboard, self).keyReleaseEvent(event)


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__(None)
        self.title = "Robot teleoperation"
        self.move_keyboard = MoveKeyboard()
        self.initUI()

    def initUI(self):
        self.central_widget = QtWidgets.QWidget()
        self.setCentralWidget(self.central_widget)
        grid = QtWidgets.QGridLayout(self.centralWidget())
        grid.addWidget(self.move_keyboard, 0, 0)
        self.setWindowTitle(self.title)
        self.setGeometry(10, 10, 1920, 1080)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM