简体   繁体   English

显示tkinter小部件中子进程的实时输出

[英]Display realtime output of a subprocess in a tkinter widget

My question is almost the same as this one: Widget to Display subprocess stdout? 我的问题与此问题几乎相同: 显示子进程stdout的小部件? but a step further. 但更进一步。

I have the following code (python2.7): 我有以下代码(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)

The output from the "longrunning" bash script is captured in realtime (appear in the console) but the Tkinter window appear only after the end of the subprocess !! “longrunning”bash脚本的输出是实时捕获的(出现在控制台中),但Tkinter窗口仅在子进程结束后出现!!

How can I have the window appearing before the subprocess start and update its content in realtime ? 如何在子进程启动之前显示窗口并实时更新其内容?

Finally I found the solution. 最后我找到了解决方案。 After the window construction, you must add : 窗口构建后,您必须添加:

frame.pack()
# force drawing of the window
win.update_idletasks()

And then after every line insertion in the widget, you must also force a refresh with the same method only on the widget. 然后在窗口小部件中的每一行插入之后,您还必须仅在窗口小部件上使用相同的方法强制刷新。

# 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()

This is an interesting solution. 这是一个有趣的解决方案。 Would that be possible to have the whole working code? 是否有可能拥有整个工作代码?

I am asking because I was wonder how the while True does not block the usability of the whole GUI... does it not? 我在问,因为我很奇怪while True如何不会阻止整个GUI的可用性......不是吗?

As suspected, I tried and this example is not really work. 怀疑,我试过,这个例子并没有真正起作用。 If you use something like " ls -Rf / " as command, you will see that the while loop will make the txt output flowing pretty well. 如果你使用类似“ ls -Rf / ”的命令作为命令,你会看到while循环将使txt输出流畅。 However both windows (main and secondary) will block big time. 然而,两个窗口(主要和次要)将阻止大的时间。

I suspect you need to send the print txt part in a separated thread or process. 我怀疑你需要在一个单独的线程或进程中发送print txt部分。 Or, if you use pygtk you can use stuff like 或者,如果你使用pygtk,你可以使用像

gobject.io_add_watch(self.ep1.stdout,       # file descriptor
                     gobject.IO_IN,         # condition
                     self.write_to_buffer ) # callback

which was actually invented for this. 这实际上是为此而发明的。

Just in case someone else is looking for it... 以防其他人正在寻找它......

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)

From here 这里开始

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM