The problem here is that process.stdout.readline()
will block until a full line is available. This means the condition line == ''
will never be met until the process exits. You have two options around this.
First you can set stdout to non-blocking and manage a buffer yourself. It would look something like this. EDIT: As Terry Jan Reedy pointed out this is a Unix only solution. The second alternative should be preferred.
import fcntl
...
def startProcess(self):
self.process = subprocess.Popen(['./subtest.sh'],
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE,
bufsize=0) # prevent any unnecessary buffering
# set stdout to 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 much as we 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() is None:
self.after(100, self.updateLines)
The second alternative is to have a separate thread read the lines into a queue. Then have updatelines pop from the queue. It would look something like this
from threading import Thread
from queue import Queue, Empty
def readlines(process, queue):
while process.poll() is 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 for 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() is None:
self.after(100, self.updateLines)
The threading route is probably safer. I'm not positive that setting stdout to non-blocking will work on all platforms.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…