What I'm trying to do is simply have the output of some terminal commands print out to a wx.TextCtrl widget. I figured the easiest way to accomplish this is to create a custom stdout class and overload the write function to that of the widget.
stdout class:
class StdOut(sys.stdout):
def __init__(self,txtctrl):
sys.stdout.__init__(self)
self.txtctrl = txtctrl
def write(self,string):
self.txtctrl.write(string)
And then I would do something such as:
sys.stdout = StdOut(createdTxtCtrl)
subprocess.Popen('echo "Hello World!"',stdout=sys.stdout,shell=True)
What results is the following error:
Traceback (most recent call last):
File "mainwindow.py", line 开发者_开发技巧12, in <module>
from systemconsole import SystemConsole
File "systemconsole.py", line 4, in <module>
class StdOut(sys.stdout):
TypeError: Error when calling the metaclass bases
file() argument 2 must be string, not tuple
Any ideas to fix this would be appreciated.
sys.stdout
is not a class
, it's an instance (of type file
).
So, just do:
class StdOut(object):
def __init__(self,txtctrl):
self.txtctrl = txtctrl
def write(self,string):
self.txtctrl.write(string)
sys.stdout = StdOut(the_text_ctrl)
No need to inherit from file
, just make a simple file-like object like this! Duck typing is your friend...
(Note that in Python, like most other OO languages but differently from Javascript, you only ever inherit from classes AKA types, never from instances of classes/types;-).
If all you need to implement is writing, there is no need to define a new class at all. Simply use createdTxtCtrl
instead of StdOut(createdTxtCtrl)
, because the former already supports the operation you need.
If all you need to do with stdout is direct some programs' output there, not direct all kinds of stuff there, don't change sys.stdout
, just instantiate the subprocess.Popen
with your own file-like object (createdTxtCtrl
) instead of sys.stdout
.
I have deployed a package to PyPi called tiotrap, which contains TextIOTrap
helper class for managing TextIO Streams.
Installation:
python3 -m pip install tiotrap
Trap writes to a list, you could implement your own handler with this method as well, just substitute the lambda with a function/method call:
import tiotrap
aTrappedStdout = []
_stdout_bk = sys.stdout
sys.stdout = tiotrap.TextIOTrap(write_handler=lambda s: aTrappedStdout.append(s))
print("TEST1")
print("TEST2")
sys.stdout = _stdout_bk
print(f"aTrappedStdout = {aTrappedStdout}")
# output: aTrappedStdout = ['TEST1', 'TEST2']
Here is a way you can use it to store the captured data:
_stdout_bk = sys.stdout
tio_trap = tiotrap.TextIOTrap(store=True)
sys.stdout = tio_trap
print("TEST1")
print("TEST2")
sys.stdout = _stdout_bk
print(f"captured logs:\n{str(tio_trap)}\n~end~\n")
# output:
# captured logs:
# TEST1
# TEST2
# ~end~
As a cross platform DEVNULL replacement:
_stdout_bk = sys.stdout
sys.stdout = tiotrap.DEVNULL
print("THIS WILL NOT PRINT")
sys.stdout = _stdout_bk
print("THIS WILL PRINT")
Wouldn't sys.stdout = StdOut(createdTxtCtrl)
create cyclical dependency? Try not reassigning sys.stdout
to the class derived from sys.stdout
.
精彩评论