@@ -2760,16 +2760,16 @@ The following example shows how to log to a Qt GUI. This introduces a simple
2760
2760
``QtHandler `` class which takes a callable, which should be a slot in the main
2761
2761
thread that does GUI updates. A worker thread is also created to show how you
2762
2762
can 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 ).
2765
2765
2766
2766
The worker thread is implemented using Qt's ``QThread `` class rather than the
2767
2767
:mod: `threading ` module, as there are circumstances where one has to use
2768
2768
``QThread ``, which offers better integration with other ``Qt `` components.
2769
2769
2770
2770
The code should work with recent releases of either ``PySide2 `` or ``PyQt5 ``.
2771
2771
You 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.
2773
2773
2774
2774
.. code-block :: python3
2775
2775
@@ -2789,22 +2789,24 @@ refer to the comments in the code for more detailed information.
2789
2789
Signal = QtCore.pyqtSignal
2790
2790
Slot = QtCore.pyqtSlot
2791
2791
2792
+
2792
2793
logger = logging.getLogger(__name__)
2793
2794
2795
+
2794
2796
#
2795
2797
# Signals need to be contained in a QObject or subclass in order to be correctly
2796
2798
# initialized.
2797
2799
#
2798
2800
class Signaller(QtCore.QObject):
2799
- signal = Signal(str)
2801
+ signal = Signal(str, logging.LogRecord )
2800
2802
2801
2803
#
2802
2804
# Output to a Qt GUI is only supposed to happen on the main thread. So, this
2803
2805
# 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 .
2808
2810
#
2809
2811
# You specify the slot function to do whatever GUI updates you want. The handler
2810
2812
# doesn't know or care about specific UI elements.
@@ -2817,7 +2819,7 @@ refer to the comments in the code for more detailed information.
2817
2819
2818
2820
def emit(self, record):
2819
2821
s = self.format(record)
2820
- self.signaller.signal.emit(s)
2822
+ self.signaller.signal.emit(s, record )
2821
2823
2822
2824
#
2823
2825
# 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.
2827
2829
def ctname():
2828
2830
return QtCore.QThread.currentThread().objectName()
2829
2831
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
+
2830
2839
#
2831
2840
# This worker class represents work that is done in a thread separate to the
2832
2841
# 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.
2851
2860
while not QtCore.QThread.currentThread().isInterruptionRequested():
2852
2861
delay = 0.5 + random.random() * 2
2853
2862
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)
2855
2865
i += 1
2856
2866
2857
2867
#
@@ -2864,10 +2874,18 @@ refer to the comments in the code for more detailed information.
2864
2874
#
2865
2875
class Window(QtWidgets.QWidget):
2866
2876
2877
+ COLORS = {
2878
+ logging.DEBUG: 'black',
2879
+ logging.INFO: 'blue',
2880
+ logging.WARNING: 'orange',
2881
+ logging.ERROR: 'red',
2882
+ logging.CRITICAL: 'purple',
2883
+ }
2884
+
2867
2885
def __init__(self, app):
2868
2886
super(Window, self).__init__()
2869
2887
self.app = app
2870
- self.textedit = te = QtWidgets.QTextEdit (self)
2888
+ self.textedit = te = QtWidgets.QPlainTextEdit (self)
2871
2889
# Set whatever the default monospace font is for the platform
2872
2890
f = QtGui.QFont('nosuchfont')
2873
2891
f.setStyleHint(f.Monospace)
@@ -2880,7 +2898,7 @@ refer to the comments in the code for more detailed information.
2880
2898
self.handler = h = QtHandler(self.update_status)
2881
2899
# Remember to use qThreadName rather than threadName in the format string.
2882
2900
fs = '%(asctime)s %(qThreadName)-12s %(levelname)-8s %(message)s'
2883
- formatter = logging.Formatter(f )
2901
+ formatter = logging.Formatter(fs )
2884
2902
h.setFormatter(formatter)
2885
2903
logger.addHandler(h)
2886
2904
# Set up to terminate the QThread when we exit
@@ -2932,21 +2950,25 @@ refer to the comments in the code for more detailed information.
2932
2950
# that's where the slots are set up
2933
2951
2934
2952
@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)
2937
2957
2938
2958
@Slot()
2939
2959
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)
2943
2964
extra = {'qThreadName': ctname() }
2944
2965
logger.log(level, 'Manually logged!', extra=extra)
2945
2966
2946
2967
@Slot()
2947
2968
def clear_display(self):
2948
2969
self.textedit.clear()
2949
2970
2971
+
2950
2972
def main():
2951
2973
QtCore.QThread.currentThread().setObjectName('MainThread')
2952
2974
logging.getLogger().setLevel(logging.DEBUG)
0 commit comments