FocusOutEvent get stuck in a loop
-
wrote on 22 Apr 2025, 12:20 last edited by
I'm working on a PYQT app and have two lineEdit boxes (line_edit1 and line_edit2) with input masks. Since editingFinished only triggers if the input mask is complete it looks like I need to create a custom focusOutEvent to be able to check if the user leaves the box before they complete the full input mask and set the focus back in that lineEdit. The problem is it seems the focusOutEvent is getting stuck in a loop if I have both custom lineEdits right after each other. If I put a normal lineEdit (line_edit3) between 1 and 2 then everything works as expected.
In watching the debug with line_edit1 and 2 together when I enter the first character in line_edit1 it goes to my on_text_changed1 function and evaluates if it has the correct number of characters and prints out the status. It then immediately goes to the custom focusOutEvent for line_edit1 and checks. (Not sure why since this should only be checking for the text changed and not a focus out) and then goes to line_edit2 and checks it's focusOutEvent and back to line_edit1 again and into a loop until it crashes.
If I have line_edit3 in between 1 and 2 then it does not bounce back and forth between the focusOutEvent of 1 and 2.
Maybe I have not done the custom focusOutEvent correctly?
Any help would be appreciated.from PyQt6.QtWidgets import QApplication, QLineEdit, QWidget, QVBoxLayout from PyQt6.QtGui import QFocusEvent class CustomLineEdit1(QLineEdit): def __init__(self, parent=None): super().__init__(parent) def focusOutEvent(self, event: QFocusEvent): print("Focus lost from the AA QLineEdit") print("QFocusEvent: ", QFocusEvent.reason(event)) if self.hasAcceptableInput(): print("Input mask is valid") super().focusOutEvent(event) # Added to make the cursor stop blinking after focus out else: print("Input mask is invalid") super().setFocus() # Set focus back to the line edit if input is invalid class CustomLineEdit2(QLineEdit): def __init__(self, parent=None): super().__init__(parent) def focusOutEvent(self, event: QFocusEvent): print("Focus lost from the BB QLineEdit") print("QFocusEvent: ", QFocusEvent.reason(event)) if self.hasAcceptableInput(): print("Input mask is valid") super().focusOutEvent(event) # Added to make the cursor stop blinking after focus out else: print("Input mask is invalid") super().setFocus() # Set focus back to the line edit if input is invalid def on_text_changed1(self): print("Text changed:", self.line_edit1.text()) if len(self.line_edit1.text()) == 9: print("Validate:", self.line_edit1.hasAcceptableInput()) print("Input mask completed for AA Box.") self.line_edit1.isEnabled = True self.line_edit1.setFocus() else: print("Validate:", self.line_edit1.hasAcceptableInput()) print("Input mask not completed for AA Box.") def on_text_changed2(self): print("Text changed:", self.line_edit2.text()) if len(self.line_edit2.text()) == 8: print("Validate:", self.line_edit2.hasAcceptableInput()) print("Input mask completed for BB Box.") else: print("Validate:", self.line_edit2.hasAcceptableInput()) print("Input mask not completed for BB Box.") class MyWindow(QWidget): def __init__(self): super().__init__() self.line_edit1 = CustomLineEdit1() self.line_edit1.setInputMask('>AA999-99A') #AA Box self.line_edit1.textChanged.connect(lambda: on_text_changed1(self)) self.line_edit1.editingFinished self.line_edit2 = CustomLineEdit2() self.line_edit2.setInputMask('>AAAA-99A') # BB Box self.line_edit2.textChanged.connect(lambda: on_text_changed2(self)) self.line_edit2.isEnabled = False self.line_edit3 = QLineEdit() layout = QVBoxLayout() layout.addWidget(self.line_edit1) # layout.addWidget(self.line_edit3) # Put line_edit3 here and everything works fine layout.addWidget(self.line_edit2) layout.addWidget(self.line_edit3) # Put line_edit3 here and it breaks the code self.setLayout(layout) if __name__ == '__main__': app = QApplication([]) window = MyWindow() window.show() app.exec()
-
I'm working on a PYQT app and have two lineEdit boxes (line_edit1 and line_edit2) with input masks. Since editingFinished only triggers if the input mask is complete it looks like I need to create a custom focusOutEvent to be able to check if the user leaves the box before they complete the full input mask and set the focus back in that lineEdit. The problem is it seems the focusOutEvent is getting stuck in a loop if I have both custom lineEdits right after each other. If I put a normal lineEdit (line_edit3) between 1 and 2 then everything works as expected.
In watching the debug with line_edit1 and 2 together when I enter the first character in line_edit1 it goes to my on_text_changed1 function and evaluates if it has the correct number of characters and prints out the status. It then immediately goes to the custom focusOutEvent for line_edit1 and checks. (Not sure why since this should only be checking for the text changed and not a focus out) and then goes to line_edit2 and checks it's focusOutEvent and back to line_edit1 again and into a loop until it crashes.
If I have line_edit3 in between 1 and 2 then it does not bounce back and forth between the focusOutEvent of 1 and 2.
Maybe I have not done the custom focusOutEvent correctly?
Any help would be appreciated.from PyQt6.QtWidgets import QApplication, QLineEdit, QWidget, QVBoxLayout from PyQt6.QtGui import QFocusEvent class CustomLineEdit1(QLineEdit): def __init__(self, parent=None): super().__init__(parent) def focusOutEvent(self, event: QFocusEvent): print("Focus lost from the AA QLineEdit") print("QFocusEvent: ", QFocusEvent.reason(event)) if self.hasAcceptableInput(): print("Input mask is valid") super().focusOutEvent(event) # Added to make the cursor stop blinking after focus out else: print("Input mask is invalid") super().setFocus() # Set focus back to the line edit if input is invalid class CustomLineEdit2(QLineEdit): def __init__(self, parent=None): super().__init__(parent) def focusOutEvent(self, event: QFocusEvent): print("Focus lost from the BB QLineEdit") print("QFocusEvent: ", QFocusEvent.reason(event)) if self.hasAcceptableInput(): print("Input mask is valid") super().focusOutEvent(event) # Added to make the cursor stop blinking after focus out else: print("Input mask is invalid") super().setFocus() # Set focus back to the line edit if input is invalid def on_text_changed1(self): print("Text changed:", self.line_edit1.text()) if len(self.line_edit1.text()) == 9: print("Validate:", self.line_edit1.hasAcceptableInput()) print("Input mask completed for AA Box.") self.line_edit1.isEnabled = True self.line_edit1.setFocus() else: print("Validate:", self.line_edit1.hasAcceptableInput()) print("Input mask not completed for AA Box.") def on_text_changed2(self): print("Text changed:", self.line_edit2.text()) if len(self.line_edit2.text()) == 8: print("Validate:", self.line_edit2.hasAcceptableInput()) print("Input mask completed for BB Box.") else: print("Validate:", self.line_edit2.hasAcceptableInput()) print("Input mask not completed for BB Box.") class MyWindow(QWidget): def __init__(self): super().__init__() self.line_edit1 = CustomLineEdit1() self.line_edit1.setInputMask('>AA999-99A') #AA Box self.line_edit1.textChanged.connect(lambda: on_text_changed1(self)) self.line_edit1.editingFinished self.line_edit2 = CustomLineEdit2() self.line_edit2.setInputMask('>AAAA-99A') # BB Box self.line_edit2.textChanged.connect(lambda: on_text_changed2(self)) self.line_edit2.isEnabled = False self.line_edit3 = QLineEdit() layout = QVBoxLayout() layout.addWidget(self.line_edit1) # layout.addWidget(self.line_edit3) # Put line_edit3 here and everything works fine layout.addWidget(self.line_edit2) layout.addWidget(self.line_edit3) # Put line_edit3 here and it breaks the code self.setLayout(layout) if __name__ == '__main__': app = QApplication([]) window = MyWindow() window.show() app.exec()
@BretO Maybe you should https://doc.qt.io/qt-6/qevent.html#accept the event in the "else" part?
-
@BretO Maybe you should https://doc.qt.io/qt-6/qevent.html#accept the event in the "else" part?
-
@jsulm Which else are you referring to?
I looked at that link but I'm not sure how I would put that in.I'm still pretty new to QT so trying to understand the documentation is not always easy.
@BretO said in FocusOutEvent get stuck in a loop:
Which else are you referring to?
def focusOutEvent(self, event: QFocusEvent): print("Focus lost from the AA QLineEdit") print("QFocusEvent: ", QFocusEvent.reason(event)) if self.hasAcceptableInput(): print("Input mask is valid") super().focusOutEvent(event) # Added to make the cursor stop blinking after focus out else: # HERE print("Input mask is invalid") super().setFocus() event.accept()
-
@jsulm Which else are you referring to?
I looked at that link but I'm not sure how I would put that in.I'm still pretty new to QT so trying to understand the documentation is not always easy.
wrote on 22 Apr 2025, 13:05 last edited by -
wrote on 22 Apr 2025, 13:09 last edited by
OK. I just tried putting it before and after the super().setFocus() on both focusoutevents with the same results of it getting stuck in the loop.
-
wrote on 22 Apr 2025, 18:00 last edited by
I've been digging through this some more and what appears to be happening is when I press tab to leave line_edit1 it goes to my focusOutEvent of CustomLineEdit1 and sets the focus back to line_edit1 but it is like the focus has also transferred over to line_edit2 so when the super().setfocus of line_edit1 happens then line_edit2 sees that and fires off it's focusOutEvent and this keeps going back and forth since they each see the focus leaving their respective line edits.
If I remove the super().setfocus from the else of line_edit2 things work because there is nothing telling it to take the focus back away from line_edit1. The problem with this is that after I do go on to line_edit2 and tab out of it to go to line_edit3, with out the super().setfocus in the else the focus does not return to line_edit2 like I need it to.
The event.accept() have had no effect.
Unless there is something else going on here it seems like the focus should never make it to line_edit2 if I'm intercepting the focusOutEvent of line_edit1 and changing it.
7/7