如何实时判断python os.stdout中是否有输出流?

我在用tkinter做一个进度窗口
用重定向将项目子模块程序内的打印输出到进度窗口的text控件内
目前存在的问题是:
进度窗口有进度条,但进度条卡顿,必须等到text控件内有输出内容后,窗口才能刷新,进度条才能动一下。

这个问题是因为Tkinter的GUI更新是单线程的,也就是说在执行打印输出时,GUI会被阻塞,直到输出完成才会进行刷新。因此,您需要将进度更新与打印输出分离,以避免GUI被阻塞。以下是一个可能的解决方案:

  1. 在进度窗口中创建一个Text控件和一个Scrollbar控件,用于显示和滚动文本。
  2. 将进度窗口作为一个独立的线程,在该线程中实时更新进度条。
  3. 在主线程中执行子模块程序,并将其输出重定向到一个队列中。
  4. 在进度窗口线程中循环读取该队列,并将其内容追加到Text控件中。

下面是一个简单的示例代码:

import threading
import queue
import time
import tkinter as tk


class ProgressWindow(threading.Thread):
    def __init__(self):
        super().__init__()
        self.queue = queue.Queue()
        self.progress = 0
        self.root = tk.Tk()
        self.text = tk.Text(self.root)
        self.text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        self.scrollbar = tk.Scrollbar(self.root)
        self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.text.config(yscrollcommand=self.scrollbar.set)
        self.scrollbar.config(command=self.text.yview)
        self.progressbar = tk.Canvas(self.root, width=200, height=20, bg='white')
        self.progressbar.pack(side=tk.BOTTOM, padx=5, pady=5)
        self.start()

    def run(self):
        while self.progress < 100:
            self.progressbar.delete("all")
            self.progressbar.create_rectangle(0, 0, self.progress * 2, 20, fill="green")
            self.root.update()
            time.sleep(0.1)

    def append_text(self, text):
        self.queue.put(text)

    def update_progress(self, progress):
        self.progress = progress

    def show(self):
        self.root.mainloop()


class RedirectText:
    def __init__(self, text_ctrl):
        self.output = text_ctrl

    def write(self, string):
        self.output.append_text(string)


def run_submodule():
    for i in range(10):
        print("Output from submodule: {}".format(i))
        time.sleep(1)


if __name__ == "__main__":
    window = ProgressWindow()
    sys.stdout = RedirectText(window.text)
    window.show()
    t = threading.Thread(target=run_submodule)
    t.start()
    for i in range(10):
        window.update_progress((i + 1) * 10)
        time.sleep(1)
    t.join()
    window.update_progress(100)

在这个示例代码中,我们创建了一个ProgressWindow类来表示进度窗口。该类继承自threading.Thread类,并在构造函数中创建了一个Text控件和一个Scrollbar控件,以及一个进度条的Canvas控件。在run()方法中,我们循环更新进度条的进度,并使用self.root.update()方法实时刷新GUI。