Python 如何将打印语句重定向到Tkinter文本小部件
我有一个Python程序,它执行一组操作并在STDOUT上打印响应。现在我正在编写一个GUI,它将调用已经存在的代码,我希望在GUI中打印相同的内容,而不是标准输出。为此,我将使用文本小部件。我不想修改执行此任务的现有代码(此代码也被其他一些程序使用)Python 如何将打印语句重定向到Tkinter文本小部件,python,tkinter,Python,Tkinter,我有一个Python程序,它执行一组操作并在STDOUT上打印响应。现在我正在编写一个GUI,它将调用已经存在的代码,我希望在GUI中打印相同的内容,而不是标准输出。为此,我将使用文本小部件。我不想修改执行此任务的现有代码(此代码也被其他一些程序使用) 有人能告诉我如何使用这个现有的任务定义,使用它的STDOUT结果并将其插入到文本小部件中吗?在主GUI程序中,我想调用此任务定义并将其结果打印到STDOUT。有什么方法可以使用这些信息吗?通常打印到标准输出的函数应该将文本放入文本小部件。通常打印
有人能告诉我如何使用这个现有的任务定义,使用它的STDOUT结果并将其插入到文本小部件中吗?在主GUI程序中,我想调用此任务定义并将其结果打印到STDOUT。有什么方法可以使用这些信息吗?通常打印到标准输出的函数应该将文本放入文本小部件。通常打印到标准输出的函数应该将文本放入文本小部件。您可能可以通过用自己的类似文件的对象替换
sys.stdout
来解决这个问题到文本小部件
例如:
import Tkinter as tk
import sys
class ExampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
toolbar = tk.Frame(self)
toolbar.pack(side="top", fill="x")
b1 = tk.Button(self, text="print to stdout", command=self.print_stdout)
b2 = tk.Button(self, text="print to stderr", command=self.print_stderr)
b1.pack(in_=toolbar, side="left")
b2.pack(in_=toolbar, side="left")
self.text = tk.Text(self, wrap="word")
self.text.pack(side="top", fill="both", expand=True)
self.text.tag_configure("stderr", foreground="#b22222")
sys.stdout = TextRedirector(self.text, "stdout")
sys.stderr = TextRedirector(self.text, "stderr")
def print_stdout(self):
'''Illustrate that using 'print' writes to stdout'''
print "this is stdout"
def print_stderr(self):
'''Illustrate that we can write directly to stderr'''
sys.stderr.write("this is stderr\n")
class TextRedirector(object):
def __init__(self, widget, tag="stdout"):
self.widget = widget
self.tag = tag
def write(self, str):
self.widget.configure(state="normal")
self.widget.insert("end", str, (self.tag,))
self.widget.configure(state="disabled")
app = ExampleApp()
app.mainloop()
您可能可以通过使用您自己的类似文件的对象替换
sys.stdout
来解决这个问题,该对象将写入文本小部件
例如:
import Tkinter as tk
import sys
class ExampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
toolbar = tk.Frame(self)
toolbar.pack(side="top", fill="x")
b1 = tk.Button(self, text="print to stdout", command=self.print_stdout)
b2 = tk.Button(self, text="print to stderr", command=self.print_stderr)
b1.pack(in_=toolbar, side="left")
b2.pack(in_=toolbar, side="left")
self.text = tk.Text(self, wrap="word")
self.text.pack(side="top", fill="both", expand=True)
self.text.tag_configure("stderr", foreground="#b22222")
sys.stdout = TextRedirector(self.text, "stdout")
sys.stderr = TextRedirector(self.text, "stderr")
def print_stdout(self):
'''Illustrate that using 'print' writes to stdout'''
print "this is stdout"
def print_stderr(self):
'''Illustrate that we can write directly to stderr'''
sys.stderr.write("this is stderr\n")
class TextRedirector(object):
def __init__(self, widget, tag="stdout"):
self.widget = widget
self.tag = tag
def write(self, str):
self.widget.configure(state="normal")
self.widget.insert("end", str, (self.tag,))
self.widget.configure(state="disabled")
app = ExampleApp()
app.mainloop()
您可以使用调用CLI程序,获取它生成的标准输出,并在文本小部件中显示它 类似于(未经测试的):
请注意,这将一直阻止,直到CLI程序完成执行,从而冻结GUI。要绕过阻塞,您可以将此代码放入一个脚本中,并在等待线程完成时让GUI更新。您可以使用调用CLI程序,获取它生成的标准输出,并在文本小部件中显示它 类似于(未经测试的):
请注意,这将一直阻止,直到CLI程序完成执行,从而冻结GUI。为了避免阻塞,您可以将此代码放入一个脚本中,并在等待线程完成时让GUI更新。在python中,无论何时调用print('examplestring'),您都会间接调用sys.stdout.write('examplestring') : 方法1:在GUI上打印
def redirector(inputStr):
textbox.insert(INSERT, inputStr)
sys.stdout.write = redirector #whenever sys.stdout.write is called, redirector is called.
root.mainloop()
实际上,我们正在调用print-(callsfor)->sys.stdout.write-(callsfor)->重定向器
方法2:在CLI和GUI上编写装饰程序-打印输出
def decorator(func):
def inner(inputStr):
try:
textbox.insert(INSERT, inputStr)
return func(inputStr)
except:
return func(inputStr)
return inner
sys.stdout.write=decorator(sys.stdout.write)
#print=decorator(print) #you can actually write this but not recommended
root.mainloop()
装饰程序实际上是将func sys.stdout.write分配给func internal
sys.stdout.write=inner
func inner在调用实际的sys.stdout.write之前添加一行额外的代码
这在某种程度上是更新旧的func sys.stdout.write以具有新功能。
您会注意到,我使用了try,但如果在打印到文本框时出现任何错误,我至少会保留sys.stdout.write的原始func到CLI
方法3:Bryan Oakley的例子
...
sys.stdout = TextRedirector(self.text, "stdout")
...
class TextRedirector(object):
def __init__(self, widget, tag="stdout"):
self.widget = widget
self.tag = tag
def write(self, str):
self.widget.configure(state="normal")
self.widget.insert("end", str, (self.tag,))
self.widget.configure(state="disabled")
他所做的是用一个方法将sys.stdout分配给类textdirector.write(str)
这么叫
print('string')-调用->sys.stdout.write('string')-调用->textdirector.write('string')在python中,无论何时调用print('examplestring'),都是间接调用sys.stdout.write('examplestring')) : 方法1:在GUI上打印
def redirector(inputStr):
textbox.insert(INSERT, inputStr)
sys.stdout.write = redirector #whenever sys.stdout.write is called, redirector is called.
root.mainloop()
实际上,我们正在调用print-(callsfor)->sys.stdout.write-(callsfor)->重定向器
方法2:在CLI和GUI上编写装饰程序-打印输出
def decorator(func):
def inner(inputStr):
try:
textbox.insert(INSERT, inputStr)
return func(inputStr)
except:
return func(inputStr)
return inner
sys.stdout.write=decorator(sys.stdout.write)
#print=decorator(print) #you can actually write this but not recommended
root.mainloop()
装饰程序实际上是将func sys.stdout.write分配给func internal
sys.stdout.write=inner
func inner在调用实际的sys.stdout.write之前添加一行额外的代码
这在某种程度上是更新旧的func sys.stdout.write以具有新功能。
您会注意到,我使用了try,但如果在打印到文本框时出现任何错误,我至少会保留sys.stdout.write的原始func到CLI
方法3:Bryan Oakley的例子
...
sys.stdout = TextRedirector(self.text, "stdout")
...
class TextRedirector(object):
def __init__(self, widget, tag="stdout"):
self.widget = widget
self.tag = tag
def write(self, str):
self.widget.configure(state="normal")
self.widget.insert("end", str, (self.tag,))
self.widget.configure(state="disabled")
他所做的是用一个方法将sys.stdout分配给类textdirector.write(str)
这么叫
print('string')-调用->sys.stdout.write('string')-调用->textdirector.write('string')事实上,我认为这个问题不限于
tkinter
。任何框架都可以应用,因为它实际上是重定向sys.stdout
为此,我创建了一个类(RedirectStdMsg
)
tl;博士
测试用例
这是test\u tk\u have\u stop\u btn():
事实上,我认为这个问题不仅限于
tkinter
。任何框架都可以应用,因为它实际上是重定向sys.stdout
为此,我创建了一个类(RedirectStdMsg
)
tl;博士
测试用例
这是test\u tk\u have\u stop\u btn():
问题在于原始函数的编码方式是,它执行一些操作序列并打印结果(在该函数中,打印命令用于显示状态)。此函数在其他几个程序中共享(所有程序都是基于命令行的函数)。所以我在想是否有办法在主GUI程序中解决这个问题。问题是原始函数的编码方式是它执行一些操作序列并打印结果(在该函数中,print命令用于显示状态)。此函数在其他几个程序中共享(所有程序都是基于命令行的函数)。所以我在想是否有办法在主GUI程序本身中解决这个问题。请您介绍一下“类似文件”的对象。非常感谢您的帮助。我在GUI模块中包含了这个TextRedirector类,并配置为使用标准输出。它解决了我的问题。只有一个问题:我如何停止重定向到stdout。函数调用结束后,我尝试了“sys.stdout=sys.\uu stdout\uuu”。然后,当我尝试打印语句时,控制台(或文本小部件)中看不到输出。我将stdout的原始引用保留为“old_stdout=sys.stdout”,然后将其引用修改为重定向类,然后在函数调用结束后将其还原回old_stdout,
def test_tk_without_stop_btn():
app = ExampleApp()
with RedirectStdMsg(sys.stdout)(TextRedirector(app.text, "stdout").write), \
RedirectStdMsg(sys.stderr)(TextRedirector(app.text, "stderr").write):
app.mainloop()
def test_tk_have_stop_btn():
director_out = RedirectStdMsg(sys.stdout)
director_err = RedirectStdMsg(sys.stderr)
app = ExampleApp(stdout=director_out, stderr=director_err)
app.mainloop()
def test_to_file():
# stdout test
with open('temp.stdout.log', 'w') as file_obj:
with RedirectStdMsg(sys.stdout)(file_obj.write):
print('stdout to file')
print('stdout to console')
# stderr test
with open('temp.stderr.log', 'w') as file_obj:
with RedirectStdMsg(sys.stderr)(file_obj.write):
sys.stderr.write('stderr to file')
sys.stderr.write('stderr to console')
# another way
cs_stdout = RedirectStdMsg(sys.stdout)
cs_stdout.start(open('temp.stdout.log', 'a').write)
print('stdout to file 2')
...
cs_stdout.stop()
print('stdout to console 2')
if __name__ == '__main__':
test_to_file()
test_tk_without_stop_btn()
test_tk_have_stop_btn()