python - Tkinter 读取子进程输出并将其放入 gui
问题描述
我对 python 和 tkinter 还很陌生,所以我正在努力创建一个脚本,将终端输出读入 tkinter 中的标签或 Gui。我四处搜索,找不到任何关于如何做到这一点的教程,很多论坛都有特定或旧代码,这使得它很难适应,尤其是对于初学者。我发现它看起来最适合我想要完成的代码是由 jfs 编写的,唯一的问题是我不断收到错误,这对于我的生活我无法弄清楚。
这是代码:
import logging
import os
import sys
from subprocess import Popen, PIPE, STDOUT
try:
import tkinter as tk
except ImportError: # Python 3
import tkinter as tk
info = logging.getLogger(__name__).info
# define dummy subprocess to generate some output
cmd = [sys.executable or "python", "-u", "-c", """
import itertools, time
for i in itertools.count():
print(i)
time.sleep(0.5)
"""]
class ShowProcessOutputDemo:
def __init__(self, root):
"""Start subprocess, make GUI widgets."""
self.root = root
# start subprocess
self.proc = Popen(cmd, stdout=PIPE, stderr=STDOUT)
# show subprocess' stdout in GUI
self.root.createfilehandler(
self.proc.stdout, tk.READABLE, self.read_output)
self._var = tk.StringVar() # put subprocess output here
tk.Label(root, textvariable=self._var).pack()
# stop subprocess using a button
tk.Button(root, text="Stop subprocess", command=self.stop).pack()
def read_output(self, pipe, mask):
"""Read subprocess' output, pass it to the GUI."""
data = os.read(pipe.fileno(), 1 << 20)
if not data: # clean up
info("eof")
self.root.deletefilehandler(self.proc.stdout)
self.root.after(5000, self.stop) # stop in 5 seconds
return
info("got: %r", data)
self._var.set(data.strip(b'\n').decode())
def stop(self, stopping=[]):
"""Stop subprocess and quit GUI."""
if stopping:
return # avoid killing subprocess more than once
stopping.append(True)
info('stopping')
self.proc.terminate() # tell the subprocess to exit
# kill subprocess if it hasn't exited after a countdown
def kill_after(countdown):
if self.proc.poll() is None: # subprocess hasn't exited yet
countdown -= 1
if countdown < 0: # do kill
info('killing')
self.proc.kill() # more likely to kill on *nix
else:
self.root.after(1000, kill_after, countdown)
return # continue countdown in a second
self.proc.stdout.close() # close fd
self.proc.wait() # wait for the subprocess' exit
self.root.destroy() # exit GUI
kill_after(countdown=5)
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s')
root = tk.Tk()
app = ShowProcessOutputDemo(root)
root.protocol("WM_DELETE_WINDOW", app.stop) # exit subprocess if GUI is closed
root.mainloop()
info('exited')
由于我缺乏经验,我不知道如何解决不断抛出的错误。这是不断发生的事情的终端输出。
Traceback (most recent call last):
File "d:\coding\OtherProjects\testserver\tkinter-read-async-subprocess-output.py", line 83, in <module>
app = ShowProcessOutputDemo(root)
File "d:\coding\OtherProjects\testserver\tkinter-read-async-subprocess-output.py", line 37, in __init__
self.root.createfilehandler(
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.1520.0_x64__qbz5n2kfra8p0\lib\tkinter\__init__.py", line 2354, in __getattr__
return getattr(self.tk, attr)
AttributeError: '_tkinter.tkapp' object has no attribute 'createfilehandler'
感谢所有花时间阅读本文的人,我真的很感激。
也很抱歉,如果我没有把它放在正确的论坛上,我仍在努力了解这个网站并致力于改进。
谢谢你-康纳
解决方案
尝试这个:
import logging
import os
import sys
from subprocess import Popen, PIPE, STDOUT
from threading import Thread
try:
import tkinter as tk
except ImportError: # Python 3
import tkinter as tk
info = logging.getLogger(__name__).info
# define dummy subprocess to generate some output
cmd = [sys.executable or "python", "-u", "-c", """
import itertools, time
for i in itertools.count():
print(i)
time.sleep(0.5)
"""]
class ShowProcessOutputDemo:
def __init__(self, root):
"""Start subprocess, make GUI widgets."""
self.root = root
# start subprocess
self.proc = Popen(cmd, stdout=PIPE, stderr=STDOUT)
# stop subprocess using a button
tk.Button(root, text="Stop subprocess", command=self.stop).pack()
self.label = tk.Label(root) # put subprocess output here
self.label.pack()
# Create a buffer for the stdout
self.stdout_data = ""
# Create a new thread that will read stdout and write the data to
# `self.stdout_buffer`
thread = Thread(target=self.read_output, args=(self.proc.stdout, ))
thread.start()
# A tkinter loop that will show `self.stdout_data` on the screen
self.show_stdout()
def read_output(self, pipe):
"""Read subprocess' output and store it in `self.stdout_data`."""
while True:
data = os.read(pipe.fileno(), 1 << 20)
# Windows uses: "\r\n" instead of "\n" for new lines.
data = data.replace(b"\r\n", b"\n")
if data:
info("got: %r", data)
self.stdout_data += data.decode()
else: # clean up
info("eof")
self.root.after(5000, self.stop) # stop in 5 seconds
return None
def show_stdout(self):
"""Read `self.stdout_data` and put the data in the GUI."""
self.label.config(text=self.stdout_data.strip("\n"))
self.root.after(100, self.show_stdout)
def stop(self, stopping=[]):
"""Stop subprocess and quit GUI."""
if stopping:
return # avoid killing subprocess more than once
stopping.append(True)
info("stopping")
self.proc.terminate() # tell the subprocess to exit
# kill subprocess if it hasn't exited after a countdown
def kill_after(countdown):
if self.proc.poll() is None: # subprocess hasn't exited yet
countdown -= 1
if countdown < 0: # do kill
info("killing")
self.proc.kill() # more likely to kill on *nix
else:
self.root.after(1000, kill_after, countdown)
return # continue countdown in a second
self.proc.stdout.close() # close fd
self.proc.wait() # wait for the subprocess' exit
self.root.destroy() # exit GUI
kill_after(countdown=5)
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s")
root = tk.Tk()
app = ShowProcessOutputDemo(root)
root.protocol("WM_DELETE_WINDOW", app.stop) # exit subprocess if GUI is closed
root.mainloop()
info("exited")
此代码启动一个新线程,该线程在循环中读取self.proc.stdout
和写入数据。还有一个 tkinter 循环将数据从 中取出并放入小部件中。self.stdout_data
while True
self.stdout_data
Label
我没有直接Label
从线程设置 's 文本,因为如果您从不同的线程调用它,有时 tkinter 可能会崩溃。
另一件事:我删除了,StringVar
因为我可以使用:<tkinter.Label>.config(text=<new text>)
代替。
推荐阅读
- testing - Flutter:测试,如何推断部分 UI
- angular - 使用 Google Maps 地理编码服务时,Angular 中使用输入绑定的子组件速度很慢
- javascript - Desandro Masonry v4.2.1 重叠图像无间隙
- kdb - KDB如何从另一个命名空间分配根命名空间中的值?
- java - 双击操作不起作用,而单击对 selenium 中的元素起作用
- bash - AWK 打印和颜色 3 变量
- github - Github文件夹组织效率
- r - 在R中给定“边缘列表中的某些顶点名称未在顶点数据框中列出”时,查找哪些顶点未列出?
- mysql - Mysql:无法弄清楚这个 LEFT JOIN 查询有什么问题
- swift - 嵌入导航控制器时 ARSCNView 不显示对象