gpt4 book ai didi

python - 实时拦截子进程输出的问题

转载 作者:太空宇宙 更新时间:2023-11-04 01:11:43 24 4
gpt4 key购买 nike

我花了大约 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 设置为非阻塞将适用于所有平台。

关于python - 实时拦截子进程输出的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27327886/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com