IDE 和终端运行之间 Popen.readline() 的不同行为

问题描述 投票:0回答:1

我有一个 Tkinter GUI 程序,我想用被调用进程的输出来更新文本框架。我设法使用线程和子进程使其工作得很好,将“运行”按钮绑定到一个方法来创建线程并在其中调用 Popen。

def template_thread(self):
    threading.Thread(target=self.run_template, daemon=True).start()

run_template方法调用main.py,并将配置文件的路径作为参数:

p = subprocess.Popen([EXE, FIRST_RUN, os.path.join(self.output.get(), 'runtime.ini')],
                     stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1, universal_newlines=True)

while p.poll() is None:
    print('here')
    #p.stdout.flush()
    #p.stderr.flush()
    msg = p.stdout.readline()
    err = p.stderr.readline()
    if msg:
        self.output_window.insert(tk.END, msg + "\n")
    if err:
        with open(os.path.join(self.output.get(), 'LOG.txt'), 'a') as log:
            log.write(err)

每当我在 Pycharm 中测试它时,它都可以完美地工作,但是一旦我尝试从其他任何地方运行它,行为就会改变为阻塞 readline() 直到进程完成,然后将整个标准输出刷新到文本框架立刻。我认为从其他类似但非常古老的问题来看,它与每个程序如何设置缓冲区有关,并且 IDE 终端以某种方式将缓冲区设置为刷新和换行符,但找不到解决方案。使用 '-u' 作为参数运行似乎没有效果,并且在第一个打印语句中添加 'flush=True' 也没有效果。该程序按需要工作,否则主循环不会中断,因为我可以看到我的小部件的焦点发生变化,所以我有点困惑。如果有什么改变的话,在 Windows 上运行。如有任何帮助,我们将不胜感激!

python python-3.x tkinter subprocess python-multithreading
1个回答
0
投票

在查看@TimRoberts 评论后,我设法使用 after() 和另外两个线程来读取和写入队列,使其工作。相关的类方法有:

    def update_window(self):
        try:
            msg = self.queue.get_nowait()
        except queue.Empty:
            msg = None

        if msg:
            self.output_window.insert(tk.END, msg + "\n")

        if msg != '__EXIT__':
            self.root.after(1000, self.update_window)

    def run_template(self, queue, control_queue):

        p = subprocess.Popen([EXE, FIRST_RUN, os.path.join(self.output.get(), 'runtime.ini')], stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1, universal_newlines=True)

        listner = threading.Thread(target=update_queue, args=[p.stdout, queue], daemon=True)
        listner.start()

        p.wait()

        if p.returncode != 0:
            messagebox.showerror("Runtime Error", "Error has occurred during runtime")
            with open(os.path.join(self.output.get(), 'LOG.txt'), 'a') as log:
                log.write(p.stderr.read())
            control_queue.put('Unlock')
        else:
            ret = messagebox.askyesno("Process Completed", "Run Again?")
            if ret:
                control_queue.put('Unlock')
            else:
                control_queue.put('Exit')

        queue.put('__EXIT__')


    def template_thread(self):
        self.lock_widgets(self.root)
        proc = self.root.after(1000, self.update_window)
        proc2 = self.root.after(1000, self.control_listener)
        thread = threading.Thread(target=self.run_template, args=[self.queue, self.control_queue], daemon=True).start()

和一个静态函数:

def update_queue(pipe, queue):
    while True:
        line = pipe.readline()
        if not line:
            break
        queue.put(line)

这似乎工作得很好,但我不确定这是否是最好的方法,所以我将保留这个问题。

© www.soinside.com 2019 - 2024. All rights reserved.