我在堆栈溢出上花费了大约6个小时,重写了我的python代码,并试图使其正常工作。只是没有。不管我做什么。
目标:使子流程的输出实时显示在tkinter文本框中。
问题是:我不知道如何使Popen实时工作。它似乎挂起,直到该过程完成。(独立运行,该过程完全按预期工作,所以只有这个东西有错误)
相关代码:
import os
import tkinter
import tkinter.ttk as 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 == '' and 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()
此外,如果代码编写不当,请随时销毁我的代码。这是我的第一个python项目,我预计该语言不会再有什么优势。
这里的问题是process.stdout.readline()
将阻塞直到有完整的行可用为止。这意味着line == ''
直到流程退出,该条件才被满足。您有两种选择。
首先,您可以将stdout设置为非阻塞并自己管理缓冲区。它看起来像这样。编辑:正如Terry Jan Reedy指出的那样,这是仅Unix的解决方案。第二种选择应该是首选。
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)
第二种选择是让一个单独的线程将行读入队列。然后从队列中弹出更新行。看起来像这样
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)
穿线路线可能更安全。我不太肯定将stdout设置为non-blocking可以在所有平台上使用。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句