@@ -2760,16 +2760,16 @@ The following example shows how to log to a Qt GUI. This introduces a simple
27602760``QtHandler `` class which takes a callable, which should be a slot in the main
27612761thread that does GUI updates. A worker thread is also created to show how you
27622762can log to the GUI from both the UI itself (via a button for manual logging)
2763- as well as a worker thread doing work in the background (here, just random
2764- short delays).
2763+ as well as a worker thread doing work in the background (here, just logging
2764+ messages at random levels with random short delays in between ).
27652765
27662766The worker thread is implemented using Qt's ``QThread `` class rather than the
27672767:mod: `threading ` module, as there are circumstances where one has to use
27682768``QThread ``, which offers better integration with other ``Qt `` components.
27692769
27702770The code should work with recent releases of either ``PySide2 `` or ``PyQt5 ``.
27712771You should be able to adapt the approach to earlier versions of Qt. Please
2772- refer to the comments in the code for more detailed information.
2772+ refer to the comments in the code snippet for more detailed information.
27732773
27742774.. code-block :: python3
27752775
@@ -2789,22 +2789,24 @@ refer to the comments in the code for more detailed information.
27892789 Signal = QtCore.pyqtSignal
27902790 Slot = QtCore.pyqtSlot
27912791
2792+
27922793 logger = logging.getLogger(__name__)
27932794
2795+
27942796 #
27952797 # Signals need to be contained in a QObject or subclass in order to be correctly
27962798 # initialized.
27972799 #
27982800 class Signaller(QtCore.QObject):
2799- signal = Signal(str)
2801+ signal = Signal(str, logging.LogRecord )
28002802
28012803 #
28022804 # Output to a Qt GUI is only supposed to happen on the main thread. So, this
28032805 # handler is designed to take a slot function which is set up to run in the main
2804- # thread. In this example, the function takes a single argument which is a
2805- # formatted log message. You can attach a formatter instance which formats a
2806- # LogRecord however you like, or change the slot function to take some other
2807- # value derived from the LogRecord .
2806+ # thread. In this example, the function takes a string argument which is a
2807+ # formatted log message, and the log record which generated it. The formatted
2808+ # string is just a convenience - you could format a string for output any way
2809+ # you like in the slot function itself .
28082810 #
28092811 # You specify the slot function to do whatever GUI updates you want. The handler
28102812 # doesn't know or care about specific UI elements.
@@ -2817,7 +2819,7 @@ refer to the comments in the code for more detailed information.
28172819
28182820 def emit(self, record):
28192821 s = self.format(record)
2820- self.signaller.signal.emit(s)
2822+ self.signaller.signal.emit(s, record )
28212823
28222824 #
28232825 # This example uses QThreads, which means that the threads at the Python level
@@ -2827,6 +2829,13 @@ refer to the comments in the code for more detailed information.
28272829 def ctname():
28282830 return QtCore.QThread.currentThread().objectName()
28292831
2832+
2833+ #
2834+ # Used to generate random levels for logging.
2835+ #
2836+ LEVELS = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR,
2837+ logging.CRITICAL)
2838+
28302839 #
28312840 # This worker class represents work that is done in a thread separate to the
28322841 # main thread. The way the thread is kicked off to do work is via a button press
@@ -2851,7 +2860,8 @@ refer to the comments in the code for more detailed information.
28512860 while not QtCore.QThread.currentThread().isInterruptionRequested():
28522861 delay = 0.5 + random.random() * 2
28532862 time.sleep(delay)
2854- logger.debug('Message after delay of %3.1f: %d', delay, i, extra=extra)
2863+ level = random.choice(LEVELS)
2864+ logger.log(level, 'Message after delay of %3.1f: %d', delay, i, extra=extra)
28552865 i += 1
28562866
28572867 #
@@ -2864,10 +2874,18 @@ refer to the comments in the code for more detailed information.
28642874 #
28652875 class Window(QtWidgets.QWidget):
28662876
2877+ COLORS = {
2878+ logging.DEBUG: 'black',
2879+ logging.INFO: 'blue',
2880+ logging.WARNING: 'orange',
2881+ logging.ERROR: 'red',
2882+ logging.CRITICAL: 'purple',
2883+ }
2884+
28672885 def __init__(self, app):
28682886 super(Window, self).__init__()
28692887 self.app = app
2870- self.textedit = te = QtWidgets.QTextEdit (self)
2888+ self.textedit = te = QtWidgets.QPlainTextEdit (self)
28712889 # Set whatever the default monospace font is for the platform
28722890 f = QtGui.QFont('nosuchfont')
28732891 f.setStyleHint(f.Monospace)
@@ -2880,7 +2898,7 @@ refer to the comments in the code for more detailed information.
28802898 self.handler = h = QtHandler(self.update_status)
28812899 # Remember to use qThreadName rather than threadName in the format string.
28822900 fs = '%(asctime)s %(qThreadName)-12s %(levelname)-8s %(message)s'
2883- formatter = logging.Formatter(f )
2901+ formatter = logging.Formatter(fs )
28842902 h.setFormatter(formatter)
28852903 logger.addHandler(h)
28862904 # Set up to terminate the QThread when we exit
@@ -2932,21 +2950,25 @@ refer to the comments in the code for more detailed information.
29322950 # that's where the slots are set up
29332951
29342952 @Slot(str)
2935- def update_status(self, status):
2936- self.textedit.append(status)
2953+ def update_status(self, status, record):
2954+ color = self.COLORS.get(record.levelno, 'black')
2955+ s = '<pre><font color="%s">%s</font></pre>' % (color, status)
2956+ self.textedit.appendHtml(s)
29372957
29382958 @Slot()
29392959 def manual_update(self):
2940- levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR,
2941- logging.CRITICAL)
2942- level = random.choice(levels)
2960+ # This function uses the formatted message passed in, but also uses
2961+ # information from the record to format the message in an appropriate
2962+ # color according to its severity (level).
2963+ level = random.choice(LEVELS)
29432964 extra = {'qThreadName': ctname() }
29442965 logger.log(level, 'Manually logged!', extra=extra)
29452966
29462967 @Slot()
29472968 def clear_display(self):
29482969 self.textedit.clear()
29492970
2971+
29502972 def main():
29512973 QtCore.QThread.currentThread().setObjectName('MainThread')
29522974 logging.getLogger().setLevel(logging.DEBUG)
0 commit comments