Best way to display logs in pyqt?
Adapted from Todd Vanyo's example for PyQt5:
import sysfrom PyQt5 import QtWidgetsimport logging# Uncomment below for terminal log messages# logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(name)s - %(levelname)s - %(message)s')class QTextEditLogger(logging.Handler): def __init__(self, parent): super().__init__() self.widget = QtWidgets.QPlainTextEdit(parent) self.widget.setReadOnly(True) def emit(self, record): msg = self.format(record) self.widget.appendPlainText(msg)class MyDialog(QtWidgets.QDialog, QtWidgets.QPlainTextEdit): def __init__(self, parent=None): super().__init__(parent) logTextBox = QTextEditLogger(self) # You can format what is printed to text box logTextBox.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')) logging.getLogger().addHandler(logTextBox) # You can control the logging level logging.getLogger().setLevel(logging.DEBUG) self._button = QtWidgets.QPushButton(self) self._button.setText('Test Me') layout = QtWidgets.QVBoxLayout() # Add the new logging box widget to the layout layout.addWidget(logTextBox.widget) layout.addWidget(self._button) self.setLayout(layout) # Connect signal to slot self._button.clicked.connect(self.test) def test(self): logging.debug('damn, a bug') logging.info('something to remember') logging.warning('that\'s not right') logging.error('foobar')app = QtWidgets.QApplication(sys.argv)dlg = MyDialog()dlg.show()dlg.raise_()sys.exit(app.exec_())
If you are using the Python logging
module to can easily create a custom logging handler that passes the log messages through to a QPlainTextEdit
instance (as described by Christopher).
To do this you first subclass logging.Handler
. In this __init__
we create the QPlainTextEdit
that will contain the logs. The key bit here is that the handle will be receiving messages via the emit()
function. So we overload this function and pass the message text into the QPlainTextEdit
.
import loggingclass QPlainTextEditLogger(logging.Handler): def __init__(self, parent): super(QPlainTextEditLogger, self).__init__() self.widget = QPlainTextEdit(parent) self.widget.setReadOnly(True) def emit(self, record): msg = self.format(record) self.widget.appendPlainText(msg) def write(self, m): pass
Create an object from this class, passing it the parent for the QPlainTextEdit
(e.g. the main window, or a layout). You can then add this handler for the current logger.
# Set up logging to use your widget as a handlerlog_handler = QPlainTextEditLogger(<parent widget>)logging.getLogger().addHandler(log_handler)
Here's a complete working example based on mfitzp's answer:
import sysfrom PyQt4 import QtCore, QtGuiimport logging# Uncomment below for terminal log messages# logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(name)s - %(levelname)s - %(message)s') class QPlainTextEditLogger(logging.Handler): def __init__(self, parent): super().__init__() self.widget = QtGui.QPlainTextEdit(parent) self.widget.setReadOnly(True) def emit(self, record): msg = self.format(record) self.widget.appendPlainText(msg) class MyDialog(QtGui.QDialog, QPlainTextEditLogger): def __init__(self, parent=None): super().__init__(parent) logTextBox = QPlainTextEditLogger(self) # You can format what is printed to text box logTextBox.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')) logging.getLogger().addHandler(logTextBox) # You can control the logging level logging.getLogger().setLevel(logging.DEBUG) self._button = QtGui.QPushButton(self) self._button.setText('Test Me') layout = QtGui.QVBoxLayout() # Add the new logging box widget to the layout layout.addWidget(logTextBox.widget) layout.addWidget(self._button) self.setLayout(layout) # Connect signal to slot self._button.clicked.connect(self.test) def test(self): logging.debug('damn, a bug') logging.info('something to remember') logging.warning('that\'s not right') logging.error('foobar')if (__name__ == '__main__'): app = None if (not QtGui.QApplication.instance()): app = QtGui.QApplication([]) dlg = MyDialog() dlg.show() dlg.raise_() if (app): app.exec_()