/
console.py
164 lines (127 loc) · 4.57 KB
/
console.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Part of the PsychoPy library
# Copyright (C) 2002-2018 Jonathan Peirce (C) 2019-2024 Open Science Tools Ltd.
# Distributed under the terms of the GNU General Public License (GPL).
"""Classes and functions for broadcasting standard streams from external
consoles/terminals within the PsychoPy GUI suite.
"""
# This module can be expanded to centralize management for all console related
# actions in the future.
#
import os.path
import sys
import io
class StdStreamDispatcher:
"""Class for broadcasting standard output to text boxes.
This class serves to redirect and log standard streams within the PsychoPy
GUI suite, usually from sub-processes (e.g., a running script) to display
somewhere. An instance of this class is created on-startup and referenced by
the main application instance. Only one instance of this class can be
created per-session (singleton).
Parameters
----------
app : :class:`~psychopy.app._psychopyApp.PsychoPyApp`
Reference to the application instance.
"""
# Developer note: In the future we should be able to attach other listeners
# dynamically. Right now they are hard-coded, being the runner output box
# and coder output panel. This will allow changes being made on those
# objects not requiring any changes here.
#
_instance = None
_initialized = False
_app = None # reference to parent app
_logFile = None
def __init__(self, app, logFile=None):
# only setup if previously not instanced
if not self._initialized:
self._app = app
self._logFile = logFile
self._initialized = True
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super(StdStreamDispatcher, cls).__new__(cls)
return cls._instance
@classmethod
def getInstance(cls):
"""Get the (singleton) instance of the `StdStreamDispatcher` class.
Getting a reference to the `StdOutManager` class outside of the scope of
the user's scripts should be done through this class method.
Returns
-------
StdStreamDispatcher or None
Instance of the `Mouse` class created by the user.
"""
return cls._instance
@classmethod
def initialized(cls):
"""Check if this class has been initialized.
Returns
-------
bool
`True` if the `StdStreamDispatcher` class has been already
instanced.
"""
return cls._initialized
@property
def logFile(self):
"""Log file for standard streams (`str` or `None`).
"""
return self._logFile
@logFile.setter
def logFile(self, val):
self._logFile = val
def redirect(self):
"""Redirect `stdout` and `stderr` to listeners.
"""
sys.stdout = sys.stderr = self
def write(self, text):
"""Send text standard output to all listeners (legacy). This method is
used for compatibility for older code. This makes it so an instance of
this object looks like a ...
Parameters
----------
text : str
Text to broadcast to all standard output windows.
"""
self.broadcast(text=text)
def broadcast(self, text):
"""Send text standard output to all listeners.
Parameters
----------
text : str
Text to broadcast to all standard output windows.
"""
# do nothing is the app isn't fully realized
if not self._app.appLoaded:
return
coder = self._app.coder
if coder is not None:
if hasattr(coder, 'consoleOutput'):
coder.consoleOutput.write(text)
runner = self._app.runner
if runner is not None:
runner.stdOut.write(text)
# write to log file
if self._logFile is not None:
# with open(self._logFile, 'a') as lf:
with io.open(self._logFile, 'a', encoding="utf-8") as lf:
lf.write(text)
lf.flush()
def flush(self):
pass
def clear(self):
"""Clear all output windows."""
# do nothing is the app isn't fully realized
if not self._app.appLoaded:
return
coder = self._app.coder
if coder is not None:
if hasattr(coder, 'consoleOutput'):
coder.consoleOutput.Clear()
runner = self._app.runner
if runner is not None:
runner.stdOut.Clear()
if __name__ == "__main__":
pass