5

I am trying to write a (currently very) simple PyQt application, and wanted to allow users to navigate using the arrow keys, rather than clicking buttons.

I have the basics implemented, and in my main QWidget, I override keyPressEvent, and right now, all I ask is that it raise an alert (QMessageBox.information(self, "Hey", "pressed:{}".format(event), QMessageBox.Ok)).

This works perfectly well for standard ASCII keys (such as letters and the Enter key), but it is completely ignoring when I press the arrow keys, and I can't for the life of me figure out why.

Part of me wonders if there's some other thing going on with the keyboard since I am on a laptop that does not have a number pad (it's an HP with a standard regular ASCII keyboard, the four arrow keys wedged underneath the right Shift, and no number pad).

Any suggestions or help? I can post code if needed, but since the keyPressEvent works for every other key, I'm not sure what it would add.

dwanderson
  • 2,775
  • 2
  • 25
  • 40
  • Can you post a short working piece of code that reproduces the problem? Offhand I don't know why this would be but I'm not a Qt expert. – Ajean Aug 22 '14 at 05:46
  • I tried to slim down the code to a MAFE (minimum appropriate-failing example) and the problem went away... so I must've missed something, perhaps adding another Widget, as @Kitsune Meyoko suggests (though setting the FocusPolicy didn't work on the first try) - I'll experiment a little and get back to you. Thanks! – dwanderson Aug 24 '14 at 20:14
  • All readers of this old question, also check this thread: http://stackoverflow.com/q/39807858/2039431 – Michael Westwort Oct 02 '16 at 19:17

1 Answers1

6

If your want to don't focus child widget anything. If your want only main QWidget, set Children No Focus Policy;

Example, hope is helps;

import sys
from PyQt4 import QtGui, QtCore

class QCustomWidget (QtGui.QWidget):
    def __init__ (self, parent = None):
        super(QCustomWidget, self).__init__(parent)
        myQLayout = QtGui.QVBoxLayout()
        self.my1QPushButton = QtGui.QPushButton('Test 1', self)
        self.my2QPushButton = QtGui.QPushButton('Test 2', self)
        self.setChildrenFocusPolicy(QtCore.Qt.NoFocus)
        myQLayout.addWidget(self.my1QPushButton)
        myQLayout.addWidget(self.my2QPushButton)
        self.setLayout(myQLayout)

    def setChildrenFocusPolicy (self, policy):
        def recursiveSetChildFocusPolicy (parentQWidget):
            for childQWidget in parentQWidget.findChildren(QtGui.QWidget):
                childQWidget.setFocusPolicy(policy)
                recursiveSetChildFocusPolicy(childQWidget)
        recursiveSetChildFocusPolicy(self)

    def keyPressEvent (self, eventQKeyEvent):
        messageQMessageBox = QtGui.QMessageBox(QtGui.QMessageBox.Question, 'Question', 'Hello Main', QtGui.QMessageBox.Yes)
        messageQMessageBox.exec_()
        QtGui.QWidget.keyPressEvent(self, eventQKeyEvent)

appQApplication = QtGui.QApplication(sys.argv)
windowQCustomWidget = QCustomWidget()
windowQCustomWidget.setFixedSize(640, 480)
windowQCustomWidget.show()
sys.exit(appQApplication.exec_())

QWidget.setFocusPolicy (self, Qt.FocusPolicy policy) Reference

FocusPolicy ENUM Reference


The current cursor in widget, It have effect with key pressed.

Case 1 : Pure QWidget: This case it can track all key because focus self QWidget.

import sys
from PyQt4 import QtGui, QtCore

class QCustomWidget (QtGui.QWidget):
    def __init__ (self, parent = None):
        super(QCustomWidget, self).__init__(parent)
        myQLayout = QtGui.QVBoxLayout()
        self.setLayout(myQLayout)

    def keyPressEvent (self, eventQKeyEvent):
        messageQMessageBox = QtGui.QMessageBox(QtGui.QMessageBox.Question, 'Question', 'Hello', QtGui.QMessageBox.Yes)
        messageQMessageBox.exec_()
        QtGui.QWidget.keyPressEvent(self, eventQKeyEvent)

appQApplication = QtGui.QApplication(sys.argv)
windowQCustomWidget = QCustomWidget()
windowQCustomWidget.setFixedSize(640, 480)
windowQCustomWidget.show()
sys.exit(appQApplication.exec_())

Case 2 : Add button in QWidget : This case can't track arrow key because it focus button and arrow key has used by set new position of focus. (If your use style Windows in linux your can see grid-line of focus)

import sys
from PyQt4 import QtGui, QtCore

class QCustomWidget (QtGui.QWidget):
    def __init__ (self, parent = None):
        super(QCustomWidget, self).__init__(parent)
        myQLayout = QtGui.QVBoxLayout()
        self.my1QPushButton = QtGui.QPushButton('Test 1', self)
        self.my2QPushButton = QtGui.QPushButton('Test 2', self)
        myQLayout.addWidget(self.my1QPushButton)
        myQLayout.addWidget(self.my2QPushButton)
        self.my1QPushButton.keyPressEvent = self.button1KeyPressEvent
        self.my2QPushButton.keyPressEvent = self.button2KeyPressEvent
        self.setLayout(myQLayout)

    def keyPressEvent (self, eventQKeyEvent):
        messageQMessageBox = QtGui.QMessageBox(QtGui.QMessageBox.Question, 'Question', 'Hello Main', QtGui.QMessageBox.Yes)
        messageQMessageBox.exec_()
        QtGui.QWidget.keyPressEvent(self, eventQKeyEvent)

    def button1KeyPressEvent (self, eventQKeyEvent):
        messageQMessageBox = QtGui.QMessageBox(QtGui.QMessageBox.Question, 'Question', 'Hello Button 1', QtGui.QMessageBox.Yes)
        messageQMessageBox.exec_()
        QtGui.QPushButton.keyPressEvent(self.my1QPushButton, eventQKeyEvent)

    def button2KeyPressEvent (self, eventQKeyEvent):
        messageQMessageBox = QtGui.QMessageBox(QtGui.QMessageBox.Question, 'Question', 'Hello Button 2', QtGui.QMessageBox.Yes)
        messageQMessageBox.exec_()
        QtGui.QPushButton.keyPressEvent(self.my2QPushButton, eventQKeyEvent)

appQApplication = QtGui.QApplication(sys.argv)
windowQCustomWidget = QCustomWidget()
windowQCustomWidget.setFixedSize(640, 480)
windowQCustomWidget.show()
sys.exit(appQApplication.exec_())

I don't know what is ASCII of arrow key return by PyQt, But Your can avoid that. In pyQt have ENUM of QtCore.Qt.Key, your can read class reference;

Example:

def keyPressEvent (self, eventQKeyEvent):
    key = eventQKeyEvent.key()
    if key == QtCore.Qt.Key_F1:
        print 'Help'
    elif key == QtCore.Qt.Key_F5:
        print 'Reload'
    elif key == QtCore.Qt.Key_Left:
        print 'Left'
    elif key == QtCore.Qt.Key_Up:
        print 'Up'
    elif key == QtCore.Qt.Key_Right:
        print 'Right'
    elif key == QtCore.Qt.Key_Down:
        print 'Down'

See also: Qt.Key ENUM Reference

hichris123
  • 10,145
  • 15
  • 56
  • 70
Bandhit Suksiri
  • 3,390
  • 1
  • 18
  • 20
  • That's where I started when I was trying to get this to work, but if I put my `QMessageBox` as the first line of that function, it still doesn't get called, meaning that pressing these arrow keys isn't even registering as a KeyPressEvent at all – dwanderson Aug 22 '14 at 02:28
  • (and, as a potentially humorous side note - the reason I added `QMessageBox`s at all is because I tried using `Qt.Key_Up` and it didn't work so I figured "alright, let me print out something about this event to see what's happening") – dwanderson Aug 22 '14 at 02:29
  • What widget class your implementation. The current cursor in widget, It have effect with key pressed. Such as, list of button. Arrow key doesn't work because it use by set new cursor position. – Bandhit Suksiri Aug 22 '14 at 02:34
  • Just a plain `QWidget` – dwanderson Aug 22 '14 at 02:41
  • Ummm, It maybe focus other widget ?, please read last edited answer. – Bandhit Suksiri Aug 22 '14 at 02:56
  • 1
    @KitsuneMeyoko, this is a very similar question: http://stackoverflow.com/q/39807858/2039431. Maybe you know the answer there as well. – Michael Westwort Oct 01 '16 at 15:02