如何将“stdout”重定向到标签小部件?
Posted
技术标签:
【中文标题】如何将“stdout”重定向到标签小部件?【英文标题】:How to redirecting "stdout" to a Label widget? 【发布时间】:2012-06-06 10:53:12 【问题描述】:我正在尝试将标准输出重定向到标签小部件。目标是将脚本中的所有 Python 打印“打印”到 Label 中。
但是当我点击BUTTON1
时,什么都没有发生...
这是我的代码:
from Tkinter import *
import sys
import tkMessageBox
class App:
def __init__(self, master):
self.frame = Frame(master, borderwidth=5, relief=RIDGE)
self.frame.grid()
class IORedirector(object):
def __init__(self,TEXT_INFO):
self.TEXT_INFO = TEXT_INFO
class StdoutRedirector(IORedirector):
def write(self,str):
self.TEXT_INFO.config(text=str)
self.TEXT_HEADER = self.text_intro = Label(self.frame, bg="lightblue",text="MY SUPER PROGRAMM") ## HEADER TEXT
self.TEXT_HEADER.grid(row=0, column=0, columnspan=2, sticky=W+E+N+S)
self.MENU = Frame(self.frame, borderwidth=5, relief=RIDGE, height=12)
self.MENU.grid(row=1, column=0, sticky=N)
self.button = Button(self.MENU, text="QUIT", fg="red", bg="red", command=self.frame.quit)
self.button.grid(row=4, column=0)
self.BUTTON1 = Button(self.MENU, text="BUTTON1", command=self.BUTTON1_CMD)
self.BUTTON1.grid(row=0, column=0,sticky=W+E)
self.TEXT_INFO = Label(self.frame, height=12, width=40, text="I WANT TO SEE THE STDOUT OUTPUT HERE", bg="grey",borderwidth=5, relief=RIDGE)
self.TEXT_INFO.grid(row=1, column=1)
sys.stdout = StdoutRedirector(self.TEXT_INFO)
def BUTTON1_CMD(self):
print "TEST NUMBER ONE"
print "TEST NUMBER TWO"
root = Tk()
app = App(root)
root.mainloop()
【问题讨论】:
【参考方案1】:您看不到文本集的原因是它在瞬间正确设置,然后立即设置为空白。这是因为 print 在 print 语句之后向 stdout 发送换行符。这是一个修改后的版本,它附加到标签上,而不是为每个打印语句覆盖它。
class StdoutRedirector(IORedirector):
def write(self,str):
self.TEXT_INFO.config(text=self.TEXT_INFO.cget('text') + str)
【讨论】:
你太棒了!这是完美的工作!感谢您的帮助,我不知道 print 在 print 语句之后总是向标准输出发送换行符,很高兴知道 ;-) (我不能投票,因为我的声誉不超过 15,但是我一有它,我就会投票给你;))【参考方案2】:我创建了一个类,它将标准输出写入调用复制到 tkinter 小部件,无论是标签还是文本。在 Python3.3.1/WindowsXp 上为我工作:
import sys
class StdoutToWidget:
'''
Retrieves sys.stdout and show write calls also in a tkinter
widget. It accepts widgets which have a "text" config and defines
their width and height in characters. It also accepts Text widgets.
Use stop() to stop retrieving.
You can manage output height by using the keyword argument. By default
the class tries to get widget\'s height configuration and use that. If
that fails it sets self.height to None which you can also do manually.
In this case the output will not be trimmed. However if you do not
manage your widget, it can grow vertically hard by getting more and
more inputs.
'''
# Inspired by Jesse Harris and Stathis
# http://***.com/a/10846997/2334951
# http://***.com/q/14710529/2334951
# TODO: horizontal wrapping
# make it a widget decorator (if possible)
# height management for Text widget mode
def __init__(self, widget, height='default', width='default'):
self._content = []
self.defstdout = sys.stdout
self.widget = widget
if height == 'default':
try:
self.height = widget.cget('height')
except:
self.height = None
else:
self.height = height
if width == 'default':
try:
self.width = widget.cget('width')
except:
self.width = None
else:
self.width = width
def flush(self):
'''
Frame sys.stdout's flush method.
'''
self.defstdout.flush()
def write(self, string, end=None):
'''
Frame sys.stdout's write method. This method puts the input
strings to the widget.
'''
if string is not None:
self.defstdout.write(string)
try:
last_line_last_char = self._content[-1][-1]
except IndexError:
last_line_last_char = '\n'
else:
if last_line_last_char == '\n':
self._content[-1] = self._content[-1][:-1]
if last_line_last_char != '\n' and string.startswith('\r'):
self._content[-1] = string[1:]
elif last_line_last_char != '\n':
self._content[-1] += string
elif last_line_last_char == '\n' and string.startswith('\r'):
self._content.append(string[1:])
else:
self._content.append(string)
if hasattr(self.widget, 'insert') and hasattr(self.widget, 'see'):
self._write_to_textwidget()
else:
self._write_to_regularwidget(end)
def _write_to_regularwidget(self, end):
if self.height is None:
self.widget.config(text='\n'.join(self.content))
else:
if not end:
content = '\n'.join(self.content[-self.height:])
else:
content = '\n'.join(self.content[-self.height+end:end])
self.widget.config(text=content)
def _write_to_textwidget(self):
self.widget.insert('end', '\n'.join(self.content))
self.widget.see('end')
def start(self):
'''
Starts retrieving.
'''
sys.stdout = self
def stop(self):
'''
Stops retrieving.
'''
sys.stdout = self.defstdout
@property
def content(self):
c = []
for li in self._content:
c.extend(li.split('\n'))
if not self.width:
return c
else:
result = []
for li in c:
while len(li) > self.width:
result.append(li[:self.width])
li = li[self.width:]
result.append(li)
return result
@content.setter
def content(self, string):
self._content = string.split('\n')
@property
def errors(self):
return self.defstdout.errors
@property
def encoding(self):
return self.defstdout.encoding
EDIT1:我收到了反对票,所以这是更新的。我在 Label 小部件中使用它,并且 print() 函数在我的小部件中顺利显示。此外,如果我将 None 传递给 write 调用并假设 -1 作为结束参数,作为一个额外的功能,那么它不会显示最后一行(小心索引)。我使用它是因为我在小部件上附加了一个滑块。我很快就会发布一个演示。
【讨论】:
以上是关于如何将“stdout”重定向到标签小部件?的主要内容,如果未能解决你的问题,请参考以下文章