Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 ?

like image 724
ericc Avatar asked Mar 12 '13 13:03

ericc


3 Answers

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()
like image 103
ericc Avatar answered Nov 07 '22 00:11

ericc


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.

like image 21
Fabrizio Avatar answered Nov 07 '22 00:11

Fabrizio


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

like image 1
Lucas Avatar answered Nov 06 '22 23:11

Lucas