【发布时间】:2021-03-28 17:47:51
【问题描述】:
我正在尝试实现从子进程管道 stdout 和 stderr 读取并同时更新 GUI。但是我的代码正在等待所有管道读取过程,然后全部更新。
import tkinter as tk
from tkinter.filedialog import askopenfilename, asksaveasfilename
import subprocess
import threading
import time
from queue import *
class Application:
# load main window of application
def __init__(self, master):
self.current_script_file_name = None
self.q = Queue()
self.window = master
self.window.title("TK")
self.window.rowconfigure(1, minsize = 600, weight = 2)
self.window.columnconfigure(1, minsize = 600, weight= 2)
# create frame for buttons and create buttons
self.frame_buttons = tk.Frame(master = self.window, relief = tk.RAISED, bd = 2)
self.button_save = tk.Button(master = self.frame_buttons, text = "Save", command = self.save_script_to_file)
self.button_run = tk.Button(master = self.frame_buttons, text = "Run", command = self.run_script_from_file)
# create frame for tk.Text editor and output
self.frame_text = tk.Frame(master = self.window, relief = tk.RAISED, bd = 2)
self.text_editor = tk.Text(self.frame_text)
self.text_output = tk.Text(self.frame_text, background = "Black", foreground = "White")
#adjust buttons
self.button_save.grid(row = 0, column = 0, sticky = "ew", padx = 5, pady = 5)
self.button_run.grid(row = 1, column = 0, sticky = "ew", padx = 5)
self.frame_buttons.grid(row = 0, column = 0, sticky = "ns")
#adjust text editor and text output
self.text_editor.grid(row = 0, column = 0, sticky = "ew", padx = 10, pady = 10)
self.text_output.grid(row = 1, column = 0, sticky = "ew", padx = 5, pady = 5)
self.frame_text.grid(row = 0, column = 1, sticky = "ns")
self.text_output.insert(tk.END, 'Script Result:\n')
def run(self):
self.window.mainloop()
def save_script_to_file(self):
file_path = asksaveasfilename(
filetypes=[("Python Scripts", "*.py"), ("Kotlin Scripts", "*.kts*")]
)
if not file_path:
return
with open(file_path, "w") as output_file:
text = self.text_editor.get(1.0, tk.END)
output_file.write("#!/usr/bin/env python3\n")
output_file.write(text)
self.window.title(f"Text Editor Application - {file_path}")
def run_script_from_file(self):
# start thread so main window not going to freeze
threading.Thread(target=self.run_script).start()
self.update()
def run_script(self):
sub_proc = subprocess.Popen(['python','script.py'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
threading.Thread(target=self.pipe_reader, args=[sub_proc.stdout]).start()
threading.Thread(target=self.pipe_reader, args=[sub_proc.stderr]).start()
def update(self):
while not self.q.empty():
for source, line in iter(self.q.get, None):
self.text_output.insert(tk.END,line)
self.window.after(1000,self.update)
def pipe_reader(self, pipe):
try:
with pipe:
for line in iter(pipe.readline, b''):
self.q.put((pipe, line))
finally:
self.q.put(None)
if __name__ == '__main__':
root = tk.Tk()
app = Application(root)
app.run()
run_script_from_file 是一个按钮命令。而 self.q 是一个队列,属于这个函数的同一类。
为什么我的 text_output tk.Text 字段没有尽快更新
脚本.py:
#!/usr/bin/env python3
import time
counter = 1
while counter < 6:
print ("The current counter value: %d" % counter)
counter = counter + 1
time.sleep(1)
print(asd)
编辑:我把我所有的代码 编辑:添加 script.py
【问题讨论】:
-
这还不足以让我们给出确定的答案,但我猜这是因为您在另一个脚本中遗漏了
flush=True,这导致输出被缓冲。你为什么首先通过子进程而不是import script来做这个? -
这几乎是我实际编写的所有代码。我有点尝试实现类似 CMake 的东西。在 GUI 中,它创建一个子进程并运行脚本并在另一个字段上显示输出。
-
您需要显示minimal reproducible example,我们才能提供具体帮助。如果这也是您编写的内容,它应该包括其他程序。我不明白你的 cmake 评论。如果您尝试并行运行另一个 python 程序,您不需要子进程,您可以简单地导入另一个文件并在另一个线程或进程中运行导入,或者作为 tkinter 主循环的一部分(取决于它的作用)。
-
那个 CMake 例子很糟糕。我想从另一种语言运行脚本并在脚本执行时同时查看输出。
标签: python multithreading user-interface tkinter