如何实时将子进程的输出写入 tkinter 中的文本小部件?
How can I write output from a Subprocess to a text widget in tkinter in realtime?
嘿,我想在 tk.Text 小部件中将子进程的输出显示到我的 GUI 中。我有一个代码,它的工作量很小,但不是实时的。如何实现终端的输出实时写入文本小部件???
import tkinter as tk
import subprocess
import threading
#### classes ####
class Redirect():
def __init__(self, widget, autoscroll=True):
self.widget = widget
self.autoscroll = autoscroll
def write(self, textbox):
self.widget.insert('end', textbox)
if self.autoscroll:
self.widget.see('end') # autoscroll
def flush(self):
pass
def run():
threading.Thread(target=test).start()
def test():
p = subprocess.Popen("python myprogram.py".split(), stdout=subprocess.PIPE, bufsize=1, text=True)
while p.poll() is None:
msg = p.stdout.readline().strip() # read a line from the process output
if msg:
print(msg)
##### Window Setting ####
fenster = tk.Tk()
fenster.title("My Program")
textbox = tk.Text(fenster)
textbox.grid()
scrollbar = tk.Scrollbar(fenster, orient=tk.VERTICAL)
scrollbar.grid()
textbox.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=textbox.yview)
start_button= tk.Button(fenster, text="Start", command=run),
start_button.grid()
old_stdout = sys.stdout
sys.stdout = Redirect(textbox)
fenster.mainloop()
sys.stdout = old_stdout
欢迎来到堆栈溢出。我稍微修改了你的代码(你在定义 start_button
之后有一个 ,
并且没有导入 sys
,我也把你的代码放在 ##### Window Setting ####
下面的 boilerplate-code 中) .
您的主要问题是,您没有让 Text
小部件在 run
中可用,而且在 test
函数(作为线程执行)中可用。所以我将你的小部件作为两个函数的参数移交了(但是可能不是最pythonic的方式)。为了执行绑定到按钮的命令,我使用了 from functools import partial
并通过 command=partial(run, textbox)
绑定了包含参数的命令。然后,我只是将 run
中的参数移交给了在您创建和启动线程的行中使用 args=[textbox]
的线程。最后,我在 test
函数中用 textbox.insert(tk.END, msg + "\n")
更新了文本框,同时删除了 print()
。插入将末尾的任何文本附加到文本框,"\n"
开始新行。
这是(稍微重组的)完整代码(app.py):
import tkinter as tk
import subprocess
import threading
import sys
from functools import partial
# ### classes ####
class Redirect:
def __init__(self, widget, autoscroll=True):
self.widget = widget
self.autoscroll = autoscroll
def write(self, textbox):
self.widget.insert('end', textbox)
if self.autoscroll:
self.widget.see('end') # autoscroll
def flush(self):
pass
def run(textbox=None):
threading.Thread(target=test, args=[textbox]).start()
def test(textbox=None):
p = subprocess.Popen("python myprogram.py".split(), stdout=subprocess.PIPE, bufsize=1, text=True)
while p.poll() is None:
msg = p.stdout.readline().strip() # read a line from the process output
if msg:
textbox.insert(tk.END, msg + "\n")
if __name__ == "__main__":
fenster = tk.Tk()
fenster.title("My Program")
textbox = tk.Text(fenster)
textbox.grid()
scrollbar = tk.Scrollbar(fenster, orient=tk.VERTICAL)
scrollbar.grid()
textbox.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=textbox.yview)
start_button = tk.Button(fenster, text="Start", command=partial(run, textbox))
start_button.grid()
old_stdout = sys.stdout
sys.stdout = Redirect(textbox)
fenster.mainloop()
sys.stdout = old_stdout
这是我创建的 test-file myprogram.py 的代码:
import time
for i in range(10):
print(f"TEST{i}")
time.sleep(1)
嘿,我想在 tk.Text 小部件中将子进程的输出显示到我的 GUI 中。我有一个代码,它的工作量很小,但不是实时的。如何实现终端的输出实时写入文本小部件???
import tkinter as tk
import subprocess
import threading
#### classes ####
class Redirect():
def __init__(self, widget, autoscroll=True):
self.widget = widget
self.autoscroll = autoscroll
def write(self, textbox):
self.widget.insert('end', textbox)
if self.autoscroll:
self.widget.see('end') # autoscroll
def flush(self):
pass
def run():
threading.Thread(target=test).start()
def test():
p = subprocess.Popen("python myprogram.py".split(), stdout=subprocess.PIPE, bufsize=1, text=True)
while p.poll() is None:
msg = p.stdout.readline().strip() # read a line from the process output
if msg:
print(msg)
##### Window Setting ####
fenster = tk.Tk()
fenster.title("My Program")
textbox = tk.Text(fenster)
textbox.grid()
scrollbar = tk.Scrollbar(fenster, orient=tk.VERTICAL)
scrollbar.grid()
textbox.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=textbox.yview)
start_button= tk.Button(fenster, text="Start", command=run),
start_button.grid()
old_stdout = sys.stdout
sys.stdout = Redirect(textbox)
fenster.mainloop()
sys.stdout = old_stdout
欢迎来到堆栈溢出。我稍微修改了你的代码(你在定义 start_button
之后有一个 ,
并且没有导入 sys
,我也把你的代码放在 ##### Window Setting ####
下面的 boilerplate-code 中) .
您的主要问题是,您没有让 Text
小部件在 run
中可用,而且在 test
函数(作为线程执行)中可用。所以我将你的小部件作为两个函数的参数移交了(但是可能不是最pythonic的方式)。为了执行绑定到按钮的命令,我使用了 from functools import partial
并通过 command=partial(run, textbox)
绑定了包含参数的命令。然后,我只是将 run
中的参数移交给了在您创建和启动线程的行中使用 args=[textbox]
的线程。最后,我在 test
函数中用 textbox.insert(tk.END, msg + "\n")
更新了文本框,同时删除了 print()
。插入将末尾的任何文本附加到文本框,"\n"
开始新行。
这是(稍微重组的)完整代码(app.py):
import tkinter as tk
import subprocess
import threading
import sys
from functools import partial
# ### classes ####
class Redirect:
def __init__(self, widget, autoscroll=True):
self.widget = widget
self.autoscroll = autoscroll
def write(self, textbox):
self.widget.insert('end', textbox)
if self.autoscroll:
self.widget.see('end') # autoscroll
def flush(self):
pass
def run(textbox=None):
threading.Thread(target=test, args=[textbox]).start()
def test(textbox=None):
p = subprocess.Popen("python myprogram.py".split(), stdout=subprocess.PIPE, bufsize=1, text=True)
while p.poll() is None:
msg = p.stdout.readline().strip() # read a line from the process output
if msg:
textbox.insert(tk.END, msg + "\n")
if __name__ == "__main__":
fenster = tk.Tk()
fenster.title("My Program")
textbox = tk.Text(fenster)
textbox.grid()
scrollbar = tk.Scrollbar(fenster, orient=tk.VERTICAL)
scrollbar.grid()
textbox.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=textbox.yview)
start_button = tk.Button(fenster, text="Start", command=partial(run, textbox))
start_button.grid()
old_stdout = sys.stdout
sys.stdout = Redirect(textbox)
fenster.mainloop()
sys.stdout = old_stdout
这是我创建的 test-file myprogram.py 的代码:
import time
for i in range(10):
print(f"TEST{i}")
time.sleep(1)