拦截Python的`print`语句并在GUI中显示

4
我有一个在Python中比较复杂的命令行函数(我们称之为myFunction()),我正在努力将其集成到图形界面中(使用PySide/Qt)。
GUI用于帮助选择输入和显示输出。然而,myFunction被设计为独立的命令行函数,并且偶尔会打印出进度。
我的问题是:如何拦截这些print调用并在GUI中显示它们?我知道修改myFunction()以发送processEvents()到GUI是可能的,但那样我就失去了在终端中执行myFunction()的能力。
理想情况下,我想要类似于Ubuntu的图形软件更新程序的东西,它具有小型嵌入式终端外观的小部件,显示在终端中执行apt-get时会显示的内容。
4个回答

8

你可以将标准输出重定向,并在操作完成后进行还原。例如:

import StringIO
import sys

# somewhere to store output
out = StringIO.StringIO()

# set stdout to our StringIO instance
sys.stdout = out

# print something (nothing will print)
print 'herp derp'

# restore stdout so we can really print (__stdout__ stores the original stdout)
sys.stdout = sys.__stdout__

# print the stored value from previous print
print out.getvalue()

从初步测试来看,这似乎是我要找的。我可以拦截print的输出并将其存储在变量中。现在我只需要找到一种以不冻结GUI的方式显示它的方法,但这是另一个问题! - PhilMacKay

2

以下是使用contextmanager的Python 3模式,它既封装了猴子补丁技术,也确保在出现异常时恢复sys.stdout

from io import StringIO
import sys
from contextlib import contextmanager


@contextmanager
def capture_stdout():
    """
    context manager encapsulating a pattern for capturing stdout writes
    and restoring sys.stdout even upon exceptions

    Examples:
    >>> with capture_stdout() as get_value:
    >>>     print("here is a print")
    >>>     captured = get_value()
    >>> print('Gotcha: ' + captured)

    >>> with capture_stdout() as get_value:
    >>>     print("here is a print")
    >>>     raise Exception('oh no!')
    >>> print('Does printing still work?')
    """
    # Redirect sys.stdout
    out = StringIO()
    sys.stdout = out
    # Yield a method clients can use to obtain the value
    try:
        yield out.getvalue
    finally:
        # Restore the normal stdout
        sys.stdout = sys.__stdout__

2

将其包装在一个函数中,劫持标准输出:

def stdin2file(func, file):
  def innerfunc(*args, **kwargs):
    old = sys.stdout
    sys.stdout = file
    try:
      return func(*args, **kwargs)
    finally:
      sys.stdout = old
  return innerfunc

然后只需提供一个支持write()的文件对象:

class GUIWriter:
  def write(self, stuff):
    #send stuff to GUI

MyFunction = stdin2file(MyFunction, GUIWriter())

包装器也可以转变为装饰器:
def redirect_stdin(file):
  def stdin2file(func, file):
    def innerfunc(*args, **kwargs):
      old = sys.stdout
      sys.stdout = file
      try:
        return func(*args, **kwargs)
      finally:
        sys.stdout = old
    return innerfunc
  return stdin2file

在声明MyFunction()时使用它:
@redirect_stdin(GUIWriter())
def MyFunction(a, b, c, d):
  # any calls to print will call the 'write' method of the GUIWriter
  # do stuff

非常感谢,这篇文章非常有帮助。只是 def stdin2file(func, file) 这一行有微妙的错误:当尝试使用 redirect_stdin 作为装饰器时,file 参数会导致错误,所以应该改为 def stdin2file(func) - Yuri Pozniak

1
所有的打印都是通过sys.stdout完成的,它是一个普通的类文件对象:如果我没记错的话,它需要一个write(str)方法。只要你的替换有这个方法,很容易就可以插入你的钩子:
import sys

class CaptureOutput:
    def write(self, message):
        log_message_to_textbox(message)

sys.stdout = CaptureOutput()

log_message_to_textbox 的实际内容由您决定。


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