I have a QtPy6 application that creates QMessageBox instances anonymously, only from the main thread:
msg = QMessageBox.critical(None, "Invalid name!", "Your output name can only be alphanumeric!")
I also have an automation test suite that must close opened QMessageBoxes when start_gui_repack()
is called, which is done by scheduling a call to close_active_modals()
as follows (see this question for an explanation):
app = QApplication(sys.argv)
class clientGUILib():
def __init__(self):
self.gui = client.MyMainWindow()
self.gui.show()
self.repackPanel = self.gui.repackFilePanel
def close_active_modals(self):
while isinstance(QApplication.activeWindow(), QMessageBox):
win: QMessageBox = QApplication.activeWindow()
closeBtn = win.defaultButton()
QTest.mouseClick(closeBtn, Qt.MouseButton.LeftButton)
QApplication.processEvents()
time.sleep(0.5)
def start_gui_repack(self, delay_time=1):
threading.Timer(delay_time, self.close_active_modals).start()
QTest.mouseClick(self.repackPanel.repackButton, Qt.MouseButton.LeftButton)
QApplication.processEvents()
Any test that uses these methods prints the following warnings to console:
QObject::setParent: Cannot set parent, new parent is in a different thread
I'm not sure how to trace what object this warning is coming from. Rarely, either of these warnings will also appear; again, I'm not sure what in my code could be causing them or if they are relevant at all.
QWidget::repaint: Recursive repaint detected
QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
In my test suite, each test individually runs, triggering a repack operation which creates up to 3 message boxes. These message boxes all get closed by the close_active_modals()
method, but leave behind more copies of the warning each time. The application eventually crashes, which seems to be related to how many of these warnings are printed; the amount of tests completed is always inconsistent - for example (//comments added by me):
Repack T0 And Delete Files And Folders ..
QObject::setParent: Cannot set parent, new parent is in a different thread
QObject::setParent: Cannot set parent, new parent is in a different thread
Repack T0 And Delete Files And Folders | PASS |
//1 message box expected
------------------------------------------------------------------------------
Repack T12 Without Deletion ...
QObject::setParent: Cannot set parent, new parent is in a different thread
QObject::setParent: Cannot set parent, new parent is in a different thread
Repack T12 Without Deletion | PASS |
//1 message box expected
------------------------------------------------------------------------------
Repack T12 And Delete Files ...
QObject::setParent: Cannot set parent, new parent is in a different thread
QObject::setParent: Cannot set parent, new parent is in a different thread
QObject::setParent: Cannot set parent, new parent is in a different thread
QObject::setParent: Cannot set parent, new parent is in a different thread
QObject::setParent: Cannot set parent, new parent is in a different thread
QObject::setParent: Cannot set parent, new parent is in a different thread
QObject::setParent: Cannot set parent, new parent is in a different thread
QObject::setParent: Cannot set parent, new parent is in a different thread
//3 message boxes expected, crashes here
As my message boxes are created using the static functions method, I can't use the msg.close()
function without code changes. However, I did try converting some of my code to use the property-based method but got the same behaviour.
I'm not sure if this has to do with the way my test suite is set up; clientGuiLib.py
is set as a library import for a RobotFramework test file and I'm assuming the global-scoped app = QApplication(sys.argv)
is enough to run the application properly; I'm not familiar enough with RobotFramework to know exactly how this is handled.
Unrelated questions regarding the same warning all seem to suggest that interacting with the GUI from a non-GUI thread isn't a good idea. However, my method of closing QMessageBoxes seems to be the only way to do it, as the main thread is blocked while a message box is open. I'm not sure if using QTest methods is considered "interacting with the GUI from an outside thread".
I can't figure out how to avoid the application crashing when all tests are run sequentially, and have no idea why it's so inconsistent or what precisely is causing it. I wasn't able to find a way to turn on a verbose logging mode for PyQt6 that would help me trace where the warning is coming from exactly either. I'm looking for first and foremost a solution to the crash, but if I haven't provided enough information, a solution to the QObject::setParent
warning or at least a way to trace it would be super appreciated.