Best way to display logs in pyqt?

I am currently working on a graphical interface using a qt designer. I am wondering how I should print lines in a graphical interface that acts like a log window. I am using pyqt5.

+9
source share
5 answers

If you use the Python logging module, you can easily create your own logging handler that passes log messages through an instance of QPlainTextEdit (as described by Christopher).

For this, you are the first subclass of logging.Handler . In this __init__ we create a QPlainTextEdit that will contain the logs. The key bit here is that the handle will receive messages through the emit() function. Therefore, we overload this function and pass the message text to QPlainTextEdit .

 import logging class QPlainTextEditLogger(logging.Handler): def __init__(self, parent): super(Logger, self).__init__() self.widget = QPlainTextEdit(parent) self.widget.setReadOnly(True) def emit(self, record): msg = self.format(record) self.widget.textCursor().appendPlainText(msg) def write(self, m): pass 

Create an object from this class by passing it the parent element for QPlainTextEdit (for example, the main window or layout). Then you can add this handler for the current registrar.

 # Set up logging to use your widget as a handler log_handler = QPlainTextEditLogger(<parent widget>) logging.getLogger().addHandler(log_handler) 
+12
source

Here is a complete working example based on mfitzp answer :

 import sys from PyQt4 import QtCore, QtGui import 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\ 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_() 
+10
source

Adapted from Todd Vanyo example for PyQt5:

 import sys from PyQt5 import QtWidgets import 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\ not right') logging.error('foobar') app = QtWidgets.QApplication(sys.argv) dlg = MyDialog() dlg.show() dlg.raise_() sys.exit(app.exec_()) 
+9
source

It looks like you will want to use a QPlainTextEdit widget set as read-only.

Try changing the background color to gray to give the user a hint that he is not editing. It is also up to you if you want it to be scrollable or you can select text.

This answer may make you run a subclass of QPlainTextEdit to scroll through the output, save to a file, whatever.

+1
source

Alex's answer should be ok in one thread, but if you enter another thread (QThread), you may receive the following warning:

 QObject::connect: Cannot queue arguments of type 'QTextCursor' (Make sure 'QTextCursor' is registered using qRegisterMetaType().) 

This is because you are modifying the GUI ( self.widget.appendPlainText(msg) ) from a stream other than the main stream without using the Qt Signal / Slot mechanism.

Here is my solution:

 # my_logger.py import logging from PyQt5.QtCore import pyqtSignal, QObject class Handler(QObject, logging.Handler): new_record = pyqtSignal(object) def __init__(self, parent): super().__init__(parent) super(logging.Handler).__init__() formatter = Formatter('%(asctime)s|%(levelname)s|%(message)s|', '%d/%m/%Y %H:%M:%S') self.setFormatter(formatter) def emit(self, record): msg = self.format(record) self.new_record.emit(msg) # <---- emit signal here class Formatter(logging.Formatter): def formatException(self, ei): result = super(Formatter, self).formatException(ei) return result def format(self, record): s = super(Formatter, self).format(record) if record.exc_text: s = s.replace('\n', '') return s 
  # gui.py ... # GUI code ... def setup_logger(self) handler = Handler(self) log_text_box = QPlainTextEdit(self) self.main_layout.addWidget(log_text_box) logging.getLogger().addHandler(handler) logging.getLogger().setLevel(logging.INFO) handler.new_record.connect(log_text_box.appendPlainText) # <---- connect QPlainTextEdit.appendPlainText slot ... 
+1
source

All Articles