创建一个自定义的sys.stdout类?

7
我将尝试的内容是将一些终端命令的输出打印到 wx.TextCtrl 小部件中。我认为最简单的方法是创建一个自定义的 stdout 类,并重载该小部件的 write 函数。
stdout 类:
class StdOut(sys.stdout):
    def __init__(self,txtctrl):
        sys.stdout.__init__(self)
        self.txtctrl = txtctrl

    def write(self,string):
        self.txtctrl.write(string)

然后我会执行类似以下的操作:

sys.stdout = StdOut(createdTxtCtrl)    
subprocess.Popen('echo "Hello World!"',stdout=sys.stdout,shell=True)

以下是错误信息:
“发生了以下错误:”
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

如果有解决这个问题的想法,将不胜感激。

4个回答

16

sys.stdout 不是一个 class,而是一个类型为 file 的实例。

因此,只需执行以下操作:

class StdOut(object):
    def __init__(self,txtctrl):
        self.txtctrl = txtctrl
    def write(self,string):
        self.txtctrl.write(string)

sys.stdout = StdOut(the_text_ctrl)

不需要继承自file,只需像这样创建一个简单的类似文件的对象! 鸭子类型是你的好朋友...

(请注意,在Python中,与大多数其他面向对象的语言但与JavaScript不同,您只会从类别或类型继承,而永远不会从类/类型的实例继承;-)。)


5
我尝试了这个操作,但结果是:Traceback (most recent call last): File "blade_gui.py", line 179, in OnNew subprocess.Popen('echo "Hello World!"',stdout=sys.stdout,shell=True) File "C:\Python26\lib\subprocess.py", line 614, in init errread, errwrite) = self._get_handles(stdin, stdout, stderr) File "C:\Python26\lib\subprocess.py", line 734, in _get_handles c2pwrite = msvcrt.get_osfhandle(stdout.fileno()) AttributeError: 'StdOut' 对象没有 'fileno' 属性。 - Duck
2
subprocess 可以执行任何语言编写的程序(不仅限于 Python!),因此需要一个“真正的操作系统级文件”(即带有 FD,也称为 fileno)-- 除了一个真正的操作系统级文件(包括幸运的是 subprocess.PIPE,它允许父进程接收子进程的输出),没有办法将任何东西作为 subprocess.Popen 的 stdout= 传递(然而,子进程中的缓冲通常意味着这不是您想要的,因此我总是建议改用 pexpect 或 wexpect)。 - Alex Martelli
4
你不仅需要“写”,还需要“刷新”,也许还需要其他功能,这取决于它的使用方式。 - SimplyKnownAsG
@Duck - 我写了一个例子,应该可以处理更多的情况。请看我的回答。如果还有其他边缘情况,请告诉我,我很乐意修改。 - Timothy C. Quinn

0

如果您只需要实现写入操作,根本不需要定义新的类。只需使用createdTxtCtrl而不是StdOut(createdTxtCtrl),因为前者已经支持您需要的操作。

如果您只需要将某些程序的输出重定向到标准输出,而不是将所有类型的内容都重定向到标准输出,请勿更改sys.stdout,只需使用自己的文件对象(createdTxtCtrl)实例化subprocess.Popen即可,而不是使用sys.stdout


0
我已经将一个名为tiotrap的包部署到PyPi,其中包含用于管理TextIO流的TextIOTrap辅助类。
安装方法:
python3 -m pip install tiotrap

陷阱写入列表,您也可以使用此方法实现自己的处理程序,只需用函数/方法调用替换lambda即可:

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']

以下是一种您可以使用的方法来存储捕获的数据:

_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~

作为一个跨平台的 DEVNULL 替代方案:
_stdout_bk = sys.stdout
sys.stdout = tiotrap.DEVNULL
print("THIS WILL NOT PRINT")
sys.stdout = _stdout_bk
print("THIS WILL PRINT")

-1

sys.stdout = StdOut(createdTxtCtrl) 这样不会造成循环依赖吗?尝试不要将 sys.stdout 重新分配给从 sys.stdout 派生的类。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接