简体   繁体   中英

Display realtime output of a subprocess in a tkinter widget

My question is almost the same as this one: Widget to Display subprocess stdout? but a step further.

I have the following code (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 !!

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?

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. 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. Or, if you use pygtk you can use stuff like

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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