1 | import os |
---|
2 | import sys |
---|
3 | import logging |
---|
4 | |
---|
5 | from PyQt5.QtCore import * |
---|
6 | |
---|
7 | |
---|
8 | class XStream(QObject): |
---|
9 | """ |
---|
10 | Imitates, loosely, an `io.TextIOWrapper` class in order to intercept |
---|
11 | messages going to stdout and stderr. |
---|
12 | |
---|
13 | To intercept messages to stdout, use: |
---|
14 | |
---|
15 | .. code-block:: python |
---|
16 | XStream.stdout().messageWritten.mySignalHandler() |
---|
17 | |
---|
18 | All messages to stdout will now also be emitted to your handler. Use |
---|
19 | `XStream.stderr()` in the same way to intercept stderr messages. |
---|
20 | |
---|
21 | An additional function, `XStream.write_log()`, emits a message only to the |
---|
22 | messageWritten signal, and not the stdout or stderr streams. It is intended |
---|
23 | for separation of log handling between console and Qt, as reflected in the |
---|
24 | name. |
---|
25 | """ |
---|
26 | _stdout = None |
---|
27 | _stderr = None |
---|
28 | messageWritten = pyqtSignal(str) |
---|
29 | |
---|
30 | _real_stream = None |
---|
31 | |
---|
32 | def flush(self): |
---|
33 | pass |
---|
34 | |
---|
35 | def fileno(self): |
---|
36 | return -1 |
---|
37 | |
---|
38 | def write(self, msg): |
---|
39 | """ |
---|
40 | 'msg' is emitted in the messageWritten signal, and is also written to |
---|
41 | the original stream. This means stdout/stderr messages have been |
---|
42 | redirected to a custom location (e.g. in-widget), but also reach |
---|
43 | console output. |
---|
44 | |
---|
45 | :param msg: message to write |
---|
46 | :return: None |
---|
47 | """ |
---|
48 | if(not self.signalsBlocked()): |
---|
49 | self.messageWritten.emit(str(msg)) |
---|
50 | self._real_stream.write(msg) |
---|
51 | |
---|
52 | def write_log(self, msg): |
---|
53 | """ |
---|
54 | 'msg' is only emitted in the messageWritten signal, not in the original |
---|
55 | stdout/stderr stream, for the purposes of logging. Use in, e.g. a |
---|
56 | custom logging handler's emit() function. |
---|
57 | |
---|
58 | :param msg: message to write |
---|
59 | :return: None |
---|
60 | """ |
---|
61 | if(not self.signalsBlocked()): |
---|
62 | self.messageWritten.emit(str(msg)) |
---|
63 | |
---|
64 | @staticmethod |
---|
65 | def stdout(): |
---|
66 | """ |
---|
67 | Creates imitation of sys.stdout. |
---|
68 | :return: An XStream instance that interfaces exactly like sys.stdout, |
---|
69 | but, has redirection capabilities through the |
---|
70 | messageWritten(str) signal. |
---|
71 | """ |
---|
72 | if(not XStream._stdout): |
---|
73 | XStream._stdout = XStream() |
---|
74 | XStream._stdout._real_stream = sys.stdout |
---|
75 | sys.stdout = XStream._stdout |
---|
76 | return XStream._stdout |
---|
77 | |
---|
78 | @staticmethod |
---|
79 | def stderr(): |
---|
80 | """ |
---|
81 | Creates imitation of sys.stderr. |
---|
82 | :return: An XStream instance that interfaces exactly like sys.stderr, |
---|
83 | but, has redirection capabilities through the |
---|
84 | messageWritten(str) signal. |
---|
85 | """ |
---|
86 | if(not XStream._stderr): |
---|
87 | XStream._stderr = XStream() |
---|
88 | XStream._stderr._real_stream = sys.stderr |
---|
89 | sys.stderr = XStream._stderr |
---|
90 | return XStream._stderr |
---|
91 | |
---|
92 | |
---|
93 | class QtHandler(logging.Handler): |
---|
94 | """ |
---|
95 | Version of logging handler "emitting" the message to custom stdout() |
---|
96 | """ |
---|
97 | def __init__(self): |
---|
98 | logging.Handler.__init__(self) |
---|
99 | |
---|
100 | def emit(self, record): |
---|
101 | record = self.format(record) |
---|
102 | if record: |
---|
103 | XStream.stdout().write_log('%s\n'%record) |
---|
104 | |
---|
105 | |
---|
106 | # Define the default logger |
---|
107 | logger = logging.getLogger() |
---|
108 | |
---|
109 | # Add the qt-signal logger |
---|
110 | handler = QtHandler() |
---|
111 | handler.setFormatter(logging.Formatter( |
---|
112 | fmt="%(asctime)s - %(levelname)s: %(message)s", |
---|
113 | datefmt="%H:%M:%S" |
---|
114 | )) |
---|
115 | logger.addHandler(handler) |
---|