Solved QMessageBox::question returning NoButton?
-
I am running pytest and PyQt-5 with
QT_QPA_PLATFORM=offscreen
andQMessageBox::question
is returningNoButton
, but only when many other unit tests are running first. The test runs fine alone. I have debugged the qt code fromQMessageBox::question
on down and for some reasonQMessageBoxPrivate::_q_buttonClicked
is never called.I can't include an example here because this is a complicated integration test. But it is clear that the
QEventLoop
inQDialog::exec
is not executing anything.Under what conditions would this occur? The documentation doesn't help. Thanks!
-
@patrickkidd Turns out my code was calling
QApplication::quit
at some point in one of the prior unit tests. So the answer is thatQMessageBox::question
(and the other static methods, and probably also QDialog) quietly returns the default -NoButton
- when the application has quit. -
@patrickkidd Turns out my code was calling
QApplication::quit
at some point in one of the prior unit tests. So the answer is thatQMessageBox::question
(and the other static methods, and probably also QDialog) quietly returns the default -NoButton
- when the application has quit. -
Hi,
Out of curiosity, are you monkeypatching your QMessageBox for the tests ?
Otherwise how are you making the that part work while testing ?
-
@SGaist said in QMessageBox::question returning NoButton?:
Hi,
Out of curiosity, are you monkeypatching your QMessageBox for the tests ?
Otherwise how are you making the that part work while testing ?
qtbot.clickYesAfter(lambda: model.removeTag(1))
from pytestqt.qtbot import QtBot class PKQtBot(QtBot): def qWaitForMessageBox(self, action, contains=None, handleClick=None): from PyQt5.QtWidgets import QAbstractButton msgBoxAccepted = util.Condition() def acceptMessageBox(): box = QApplication.activeModalWidget() if box: if contains: assert contains in box.text() if handleClick: handleClick() msgBoxAccepted() msgBoxAccepted.timer.stop() else: okButton = box.button(QMessageBox.Ok) box.buttonClicked[QAbstractButton].connect(msgBoxAccepted) msgBoxAccepted() self.mouseClick(okButton, Qt.LeftButton) msgBoxAccepted.timer.stop() msgBoxAccepted.timer = QTimer(QApplication.instance()) msgBoxAccepted.timer.timeout.connect(acceptMessageBox) msgBoxAccepted.timer.start(100) action() msgBoxAccepted.wait() def clickYesAfter(self, action): def doClickYes(): self.mouseClick(QApplication.activeModalWidget().button(QMessageBox.Yes), Qt.LeftButton) self.qWaitForMessageBox(action, handleClick=doClickYes) def clickNoAfter(self, action): def doClickNo(): self.mouseClick(QApplication.activeModalWidget().button(QMessageBox.No), Qt.LeftButton) self.qWaitForMessageBox(action, handleClick=doClickNo) @pytest.yield_fixture def qtbot(qApp, request): """ Overridden to use our qApp, because the old one was calling abort(). """ result = PKQtBot(request) util.qtbot = result yield result
I also have another one that uses my custom
Condition
class, which can be used to wait for a lambda or keep track of signal calls:def waitUntil(self, condition, timeout=2000): util.Condition(condition=condition).wait(maxMS=timeout)
class Condition(Debug): """ Allows you to wait for a signal to be called. """ def __init__(self, only=None, condition=None): self.callCount = 0 self.callArgs = [] self.senders = [] self.lastCallArgs = None self.only = only self.condition = condition def reset(self): self.callCount = 0 self.callArgs = [] self.senders = [] self.lastCallArgs = None def test(self): """ Return true if the condition is true. """ if self.condition: return self.condition() else: self.callCount > 0 def set(self, *args): """ Set the condition to true. Alias for condition(). """ self.callCount += 1 self.senders.append(QObject().sender()) self.lastCallArgs = args self.callArgs.append(args) def __call__(self, *args): """ Called by whatever signal that triggers the condition. """ if self.only: only = self.only if not only(*args): return self.set(*args) def wait(self, maxMS=2000, onError=None): """ Wait for the condition to be true. onError is a callback. """ startTime = time.time() success = True app = QApplication.instance() while app and not self.test(): try: app.processEvents(QEventLoop.WaitForMoreEvents, 100) except KeyboardInterrupt as e: if onError: onError() break elapsed = ((time.time() - startTime) * 1000) if elapsed >= maxMS: break