Skip to content Skip to sidebar Skip to footer

Display Stderr In A Pyqt Qmessagebox

I want to capture stderr output from my PyQt script and display it in a QMessageBox. I found these posts and tried them, but none really worked for me: Display python console mess

Solution 1:

I worked out something which seems to do the job and is as simple as I can make it. If anyone has improvements or can explain the problems I've worked around here please edit the code.

This is an example which starts a simple main window with a button to create a stderr. The error message is captured and displayed in a message box which clears correctly on 'OK' or closing. The technique can be used with stdout too.

from PyQt4 import QtGui

classStdErrHandler():
    def__init__(self):
        # To instantiate only one message box
        self.err_box = Nonedefwrite(self, std_msg):
        # All that stderr or stdout require is a class with a 'write' method.if self.err_box isNone:
            self.err_box = QtGui.QMessageBox()
            # Both OK and window delete fire the 'finished' signal
            self.err_box.finished.connect(self.clear)
        # A single error is sent as a string of separate stderr .write() messages, # so concatenate them.
        self.err_box.setText(self.err_box.text() + std_msg)
        # .show() is used here because .exec() or .exec_() create multiple# MessageBoxes.
        self.err_box.show()

    defclear(self):
        # QMessageBox doesn't seem to be actually destroyed when closed, just hidden.# This is true even if destroy() is called or if the Qt.WA_DeleteOnClose # attribute is set.  Clear text for next time.
        self.err_box.setText('')

classAppMainWindow(QtGui.QMainWindow):
    """
    Main window with button to create an error
    """def__init__(self, parent=None):
        # initialization of the superclasssuper(AppMainWindow, self).__init__(parent)

        self.create_err = QtGui.QPushButton(self)
        self.create_err.setText("Create Error")
        self.create_err.clicked.connect(self.err_btn_clicked)

    deferr_btn_clicked(self):
        # Deliberately create a stderr output
        oopsie

if __name__ == "__main__":
    import sys

    app = QtGui.QApplication(sys.argv)
    amw = AppMainWindow()
    amw.show()

    # Instantiate stderr handler class
    std_err_handler = StdErrHandler()
    # Connect stderr to the handler
    sys.stderr = std_err_handler
    sys.exit(app.exec_())

EDIT: As three_pineapples pointed out, this isn't thread-safe. I found when I installed this in another app the message box would hang if a stderr occurred while running the app under the pydev debugger, which is probably due to this.

I rewrote it using a thread and queue, but this also hung.

Then I learned that PyQt signals & slots are thread-safe, so I rewrote it using only that. The version below is thread-safe (I think) and runs correctly as a stand-alone script or under the debugger:

from PyQt4 import QtCore, QtGui

classStdErrHandler(QtCore.QObject):
    '''
    This class provides an alternate write() method for stderr messages.
    Messages are sent by pyqtSignal to the pyqtSlot in the main window.
    '''
    err_msg = QtCore.pyqtSignal(str)

    def__init__(self, parent=None):
        QtCore.QObject.__init__(self)

    defwrite(self, msg):
        # stderr messages are sent to this method.
        self.err_msg.emit(msg)

classAppMainWindow(QtGui.QMainWindow):
    '''
    Main window with button to create an error
    '''def__init__(self, parent=None):
        # initialization of the superclasssuper(AppMainWindow, self).__init__(parent)

        # To avoid creating multiple error boxes
        self.err_box = None# Single button, connect to button handler
        self.create_err = QtGui.QPushButton(self)
        self.create_err.setText("Create Error")
        self.create_err.clicked.connect(self.err_btn_clicked)

    deferr_btn_clicked(self):
        # Deliberately create a stderr output
        oopsie

    defstd_err_post(self, msg):
        '''
        This method receives stderr text strings as a pyqtSlot.
        '''if self.err_box isNone:
            self.err_box = QtGui.QMessageBox()
            # Both OK and window delete fire the 'finished' signal
            self.err_box.finished.connect(self.clear)
        # A single error is sent as a string of separate stderr .write() messages,# so concatenate them.
        self.err_box.setText(self.err_box.text() + msg)
        # .show() is used here because .exec() or .exec_() create multiple# MessageBoxes.
        self.err_box.show()

    defclear(self):
        # QMessageBox doesn't seem to be actually destroyed when closed, just hidden.# This is true even if destroy() is called or if the Qt.WA_DeleteOnClose# attribute is set.  Clear text for next time.
        self.err_box.setText('')

if __name__ == '__main__':
    import sys

    app = QtGui.QApplication(sys.argv)
    amw = AppMainWindow()
    amw.show()

    # Create the stderr handler and point stderr to it
    std_err_handler = StdErrHandler()
    sys.stderr = std_err_handler

    # Connect err_msg signal to message box method in main window
    std_err_handler.err_msg.connect(amw.std_err_post)

    sys.exit(app.exec_())

Post a Comment for "Display Stderr In A Pyqt Qmessagebox"