python - Issues intercepting subprocess output in real time -
i've spent 6 hours on stack overflow, rewriting python code , trying work. doesn't tho. no matter do.
the goal: getting output of subprocess appear in real time in tkinter text box.
the issue: can't figure out how make popen work in real time. seems hang until process complete. (run on own, process works expected, it's thing has error)
relevant code:
import os import tkinter import tkinter.ttk tk import subprocess class application (tk.frame): process = 0 def __init__ (self, master=none): tk.frame.__init__(self, master) self.grid() self.createwidgets() def createwidgets (self): self.quitbutton = tk.button(self, text='quit', command=self.quit) self.quitbutton.grid() self.console = tkinter.text(self) self.console.config(state=tkinter.disabled) self.console.grid() def startprocess (self): dir = "c:/folder/" self.process = subprocess.popen([ "python", "-u", dir + "start.py" ], stdout=subprocess.pipe, stdin=subprocess.pipe, stderr=subprocess.pipe, cwd=dir) self.updatelines() def updatelines (self): self.console.config(state=tkinter.normal) while true: line = self.process.stdout.readline().decode().rstrip() if line == '' , self.process.poll() != none: break else: self.console.insert(tkinter.end, line + "\n") self.console.config(state=tkinter.disabled) self.after(1, self.updatelines) app = application() app.startprocess() app.mainloop()
also, feel free destroy code if it's written poorly. first python project, don't expect @ language yet.
the problem here process.stdout.readline()
block until full line available. means condition line == ''
never met until process exits. have 2 options around this.
first can set stdout non-blocking , manage buffer yourself. this. edit: terry jan reedy pointed out unix solution. second alternative should preferred.
import fcntl ... def startprocess(self): self.process = subprocess.popen(['./subtest.sh'], stdout=subprocess.pipe, stdin=subprocess.pipe, stderr=subprocess.pipe, bufsize=0) # prevent unnecessary buffering # set stdout non-blocking fd = self.process.stdout.fileno() fl = fcntl.fcntl(fd, fcntl.f_getfl) fcntl.fcntl(fd, fcntl.f_setfl, fl | os.o_nonblock) # schedule updatelines self.after(100, self.updatelines) def updatelines(self): # read stdout as can line = '' while true: buff = self.process.stdout.read(1024) if buff: buff += line.decode() else: break self.console.config(state=tkinter.normal) self.console.insert(tkinter.end, line) self.console.config(state=tkinter.disabled) # schedule callback if self.process.poll() none: self.after(100, self.updatelines)
the second alternative have separate thread read lines queue. have updatelines pop queue. this
from threading import thread queue import queue, empty def readlines(process, queue): while process.poll() none: queue.put(process.stdout.readline()) ... def startprocess(self): self.process = subprocess.popen(['./subtest.sh'], stdout=subprocess.pipe, stdin=subprocess.pipe, stderr=subprocess.pipe) self.queue = queue() self.thread = thread(target=readlines, args=(self.process, self.queue)) self.thread.start() self.after(100, self.updatelines) def updatelines(self): try: line = self.queue.get(false) # false non-blocking, raises empty if empty self.console.config(state=tkinter.normal) self.console.insert(tkinter.end, line) self.console.config(state=tkinter.disabled) except empty: pass if self.process.poll() none: self.after(100, self.updatelines)
the threading route safer. i'm not positive setting stdout non-blocking work on platforms.
Comments
Post a Comment