在 tkinter 小部件中显示子进程的实时输出
Posted
技术标签:
【中文标题】在 tkinter 小部件中显示子进程的实时输出【英文标题】:Display realtime output of a subprocess in a tkinter widget 【发布时间】:2013-02-28 00:48:25 【问题描述】:我的问题和这个差不多: Widget to Display subprocess stdout? 但更进一步。
我有以下代码(python2.7):
def btnGoClick(p1):
params = w.line.get()
if len(params) == 0:
return
# create child window
win = tk.Toplevel()
win.title('Bash')
win.resizable(0, 0)
# create a frame to be able to attach the scrollbar
frame = ttk.Frame(win)
# the Text widget - the size of the widget define the size of the window
t = tk.Text(frame, width=80, bg="black", fg="green")
t.pack(side="left", fill="both")
s = ttk.Scrollbar(frame)
s.pack(side="right", fill="y")
# link the text and scrollbar widgets
s.config(command=t.yview)
t.config(yscrollcommand=s.set)
frame.pack()
process = subprocess.Popen(["<bashscript>", params], shell=False,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while True:
out = process.stdout.readline()
if out == '' and process.poll() is not None:
break
print out
t.insert(tk.END, out)
“longrunning”bash 脚本的输出是实时捕获的(出现在控制台中),但 Tkinter 窗口仅在子进程结束后出现!!
如何让窗口在子流程启动之前出现并实时更新其内容?
【问题讨论】:
为了避免阻塞GUI,你可以把readline()
放到单独的线程example
如果您使用的是 POSIXy 系统,那么您也可以使用use createfilehandler()
to get output from the subprocess in "real-time" 而不是显式线程。
相关:Redirect command line results to a tkinter GUI
【参考方案1】:
我终于找到了解决方案。 窗口构建后,必须添加:
frame.pack()
# force drawing of the window
win.update_idletasks()
然后在小部件中插入每一行之后,您还必须仅在小部件上使用相同的方法强制刷新。
# insert the line in the Text widget
t.insert(tk.END, out)
# force widget to display the end of the text (follow the input)
t.see(tk.END)
# force refresh of the widget to be sure that thing are displayed
t.update_idletasks()
【讨论】:
正如我所怀疑的那样,我试过了,这个例子并没有真正起作用。如果作为命令使用“ls -Rf /”之类的命令,您将看到 while 循环将使窗口 txt 输出流畅地运行。但是,两个窗口(主要和次要)都会阻塞大量时间。 是的,没错。在输出过程中,一切都被阻塞了,它只是一个输出。你不能与之交互。但这正是我所需要的。 伙计,你不可能想要这种行为。 GUI 的可用性受到这种行为的影响。调整大小、关闭按钮、最小化按钮全部冻结。 好吧,这没什么大不了的,因为接口的目的是启动这个子进程,无论如何,这并不需要太多时间。我将尝试实施 J.F. Sebastian 提出的解决方案,看看是否可以改进【参考方案2】:这是一个有趣的解决方案。那有可能拥有整个工作代码吗?
我之所以问,是因为我想知道 while True
是如何不阻止整个 GUI 的可用性的……不是吗?
正如怀疑的那样,我试过了,这个例子并没有真正起作用。如果您使用“ls -Rf /
”之类的命令作为命令,您将看到 while 循环将使 txt 输出流畅地运行。然而,两个窗口(主要和次要)都会阻塞大量时间。
我怀疑您需要在单独的线程或进程中发送打印 txt 部分。或者,如果你使用 pygtk,你可以使用
gobject.io_add_watch(self.ep1.stdout, # file descriptor
gobject.IO_IN, # condition
self.write_to_buffer ) # callback
实际上是为此而发明的。
【讨论】:
这里有两个例子:one usesio_add_watch()
, another -- threads【参考方案3】:
以防万一其他人正在寻找它......
log_box_1 = tk.Text(root, borderwidth=3, relief="sunken")
with subprocess.Popen("ls -la", shell=True, stdout=subprocess.PIPE, bufsize=1, universal_newlines=True) as p:
for line in p.stdout:
log_box_1.insert(tk.END, line)
来自here
【讨论】:
以上是关于在 tkinter 小部件中显示子进程的实时输出的主要内容,如果未能解决你的问题,请参考以下文章