Issue
I have a small GUI app made using pyqt5. I found a strange problem while using eventFilter...
def eventFilter(self, source, event):
if event.type() == QtCore.QEvent.KeyPress:
# RETURN
if event.key() == QtCore.Qt.Key_Return:
if source is self.userLineEdit:
self.pswLineEdit.setFocus()
elif source is self.pswLineEdit:
self.LoginButton.click()
# TAB
elif event.key() == QtCore.Qt.Key_Tab:
if source is self.userLineEdit:
self.pswLineEdit.setFocus()
return super().eventFilter(source, event)
While pressing enter key just behave normally, tab key does not. I don't know where the problem could be. I'm going to link a video to show the exact problem as I'm not able to describe how this is not working
Link to video I know it's pixelated (sorry) but the important thing is the behavior of the cursor
SMALL EXAMPLE
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLineEdit
from PyQt5 import QtCore
class App(QWidget):
def __init__(self):
super().__init__()
self.title = 'Hello, world!'
self.left = 10
self.top = 10
self.width = 640
self.height = 480
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.userEdit = QLineEdit(self)
self.pswEdit = QLineEdit(self)
self.userEdit.setPlaceholderText("Username")
self.pswEdit.setPlaceholderText("Password")
self.userEdit.installEventFilter(self)
self.pswEdit.installEventFilter(self)
mainLayout = QVBoxLayout()
mainLayout.addWidget(self.userEdit)
mainLayout.addWidget(self.pswEdit)
self.setLayout(mainLayout)
self.show()
def eventFilter(self, source, event):
if event.type() == QtCore.QEvent.KeyPress:
# RETURN
if event.key() == QtCore.Qt.Key_Return:
if source is self.userEdit:
self.pswEdit.setFocus()
# TAB
elif event.key() == QtCore.Qt.Key_Tab:
if source is self.userEdit:
self.pswEdit.setFocus()
return super().eventFilter(source, event)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
Solution
If you don't filter events, they are processed by the object and eventually propagated back to the parent.
By default, QWidget will try to focus the next (or previous, for Shift+Tab) child widget in the focus chain, by calling its focusNextPrevChild()
. If it can do it, it will actually set the focus on that widget, otherwise the event is ignored and propagated to the parent.
Since most widgets (including QLineEdit) don't handle the tab keys for focus changes on their own, as they don't have children, their parent will receive it, which will call focusNextPrevChild()
looking for another child widget, and so on, up to the object tree, until a widget finally can handle the key, eventually changing the focus.
In your case, this is what's happening:
- you check events and find that the tab key event is received by the first line edit;
- you set the focus on the other line edit, the password field;
- you let the event be handled anyway by the widget, since you're not ignoring or filtering it out;
- the first line edit calls
focusNextPrevChild()
but is not able to do anything with it; - the event is propagated to the parent, which then calls its own
focusNextPrevChild()
; - the function checks the current focused child widget, which is the password field you just focused, and finds the next, which coincidentally is the first line edit, which gets focused again;
The simple solution is to just add return True
after changing the focus, so that the event doesn't get propagated to the parent causing a further focus change:
if event.key() == QtCore.Qt.Key_Tab:
if source is self.userEdit:
self.pswEdit.setFocus()
return True
Note that overriding the focus behavior is quite complex, and you have to be very careful about how focus and events are handled, especially for specific widgets that might deal with events in particular ways (studying the Qt sources is quite useful for this), otherwise you'll get unexpected behavior or even fatal recursion.
For instance, there's normally no need for an event filter for the return key, as QLineEdit already provides the returnPressed
signal:
self.userEdit.returnPressed.connect(self.pswEdit.setFocus)
Qt already has a quite thorough focus management system, if you just want more control over the way the tab chain works use existing functions like setTabOrder()
on the parent or top level window, and if you want to have more control over how (or if) they get it, use setFocusPolicy()
.
Answered By - musicamante
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.