Here is one approach, it uses threading and queue to not block the .mainloop()
and to not call tkinter
methods from another thread (which may potentially break it):
import tkinter as tk
from subprocess import Popen, PIPE
from threading import Thread
from queue import Queue
def start_new_proc():
# text.config(cursor="clock")
root.config(cursor="clock")
text.config(state="normal")
queue = Queue()
Thread(target=lambda: run_proc(queue)).start()
update_text(queue)
def after_proc():
# text.config(cursor="")
root.config(cursor="")
text.config(state="disable")
def update_text(queue):
data = queue.get()
if data == 'break':
after_proc()
return
text.insert('end', data)
root.after(100, update_text, queue)
def run_proc(queue):
command = "ping 8.8.8.8"
proc = Popen(command, shell=True, stdout=PIPE)
for line in iter(proc.stdout.readline, ''):
line = line.decode('utf-8')
if line == '':
queue.put('break')
break
queue.put(line)
# proc.wait()
root = tk.Tk()
text = tk.Text(root, state="disabled")
text.pack()
button = tk.Button(root, text="Run", command=start_new_proc)
button.pack()
root.mainloop()
Quick explanation:
When you click the button, it calls start_new_proc()
:
Now first all the configurations are run so now the cursor and text are configured (note that the clock cursor is visible only when the cursor is not on Text widget, since the config for text widget is commented out)
Then a queue object is created, this will allow to safely communicate between threads (and it will get garbage collected once the function "stops")
Then a thread is started where the cmd
command is run (oh and I changed it a bit (removed "-c 1") because I couldn't test otherwise). So every iteration the data is put into the queue for the main thread to safely receive it, other stuff is the same, also notice that there is '"break"' put into queue once the loop has to stop, so that the other end knows to stop too (probably better to place some object since "break"
can be placed there by line
variable)
Then the update_text()
function is called which receives data from the queue and then inserts it to the text widget (there is also the "break"
check) and then the .after()
method for making the function loop without blocking the .mainloop()
(and also when "break"
apppears then configuration function gets called to config the state and cursor)