Roman Kiselev
Roman Kiselev

Reputation: 1174

PyQt4 QMessageBox with a countdown timer

I write a python application for measurements in a laboratory, which saves data into a remote database over an unstable network connection. When connection is broken, a question pops up (I use QMessageBox.question), asking user to whether repeat the last transaction or cancel it.

Recently application was modified to do automatic measurement autonomously during the night. There is no operator anymore to click on the default option "Retry"! It should be selected automatically after some timeout, still giving the user opportunity to make other choice.

enter image description here

This means, I need a version of QMessageBox that clicks a default button after a timeout is elapsed, if user did not make any other choice.

There is similar question, but it is C++-specific.

Upvotes: 2

Views: 1422

Answers (1)

Roman Kiselev
Roman Kiselev

Reputation: 1174

This answer is a Python adaptation of C++ code posted by Jeremy Friesner in a similar question:

from PyQt4.QtGui import QMessageBox as MBox, QApplication
from PyQt4.QtCore import QTimer


class TimedMBox(MBox):
    """
    Variant of QMessageBox that automatically clicks the default button
    after the timeout is elapsed
    """
    def __init__(self, timeout=5, buttons=None, **kwargs):
        if not buttons:
            buttons = [MBox.Retry, MBox.Abort, MBox.Cancel]

        self.timer = QTimer()
        self.timeout = timeout
        self.timer.timeout.connect(self.tick)
        self.timer.setInterval(1000)
        super(TimedMBox, self).__init__(parent=None)

        if "text" in kwargs:
            self.setText(kwargs["text"])
        if "title" in kwargs:
            self.setWindowTitle(kwargs["title"])
        self.t_btn = self.addButton(buttons.pop(0))
        self.t_btn_text = self.t_btn.text()
        self.setDefaultButton(self.t_btn)
        for button in buttons:
            self.addButton(button)

    def showEvent(self, e):
        super(TimedMBox, self).showEvent(e)
        self.tick()
        self.timer.start()

    def tick(self):
        self.timeout -= 1
        if self.timeout >= 0:
            self.t_btn.setText(self.t_btn_text + " (%i)" % self.timeout)
        else:
            self.timer.stop()
            self.defaultButton().animateClick()

    @staticmethod
    def question(**kwargs):
        """
        Ask user a question, which has a default answer. The default answer is
        automatically selected after a timeout.

        Parameters
        ----------

        title : string
            Title of the question window

        text : string
            Textual message to the user

        timeout : float
            Number of seconds before the default button is pressed

        buttons : {MBox.DefaultButton, array}
            Array of buttons for the dialog, default button first

        Returns
        -------
        button : MBox.DefaultButton
            The button that has been pressed
        """
        w = TimedMBox(**kwargs)
        w.setIcon(MBox.Question)
        return w.exec_()



if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)
    w = TimedMBox.question(text="Please select something",
                           title="Timed Message Box",
                           timeout=8)
    if w == MBox.Retry:
        print "Retry"
    if w == MBox.Cancel:
        print "Cancel"
    if w == MBox.Abort:
        print "Abort"

In this implementation the default button is the first one in the argument buttons.

Upvotes: 6

Related Questions