[英]Tkinter read subprocess output and put it into the gui
I'm pretty new to python and tkinter so I'm struggling with creating a script that reads the terminal output into a label or Gui in tkinter. I'm pretty new to python and tkinter so I'm struggling with creating a script that reads the terminal output into a label or Gui in tkinter. I've searched around and can't find any tutorials on how to do it, a lot of forums have specific or old code which makes it really difficult to adapt especially for a beginner.
我四处搜索,找不到任何关于如何做到这一点的教程,很多论坛都有特定或旧代码,这使得它很难适应,尤其是对于初学者。 The code that I found that looks like it will work best for what I'm trying to accomplish was made by jfs, the only problem is that I keep getting errors which for the life of me I cant figure out.
我发现它看起来最适合我想要完成的代码是由 jfs 编写的,唯一的问题是我不断收到错误,这对于我的生活我无法弄清楚。
Here is the code:这是代码:
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')
Due to my lack of experience, i don't know how to fix the errors that keep being thrown.由于我缺乏经验,我不知道如何解决不断抛出的错误。 Here is a terminal output of what keeps occurring.
这是不断发生的终端 output。
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'
Thanks to everyone that took the time to read this I really appreciate it.感谢所有花时间阅读本文的人,我真的很感激。
Also sorry if i didn't put this in the right forum I'm still trying to understand this website and am dedicated to improve.也很抱歉,如果我没有把它放在正确的论坛上,我仍在努力了解这个网站并致力于改进。
Thank You - Connor谢谢你-康纳
Try this:尝试这个:
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")
This code starts a new thread that reads from self.proc.stdout
and writes the data into self.stdout_data
in a while True
loop.此代码启动一个新线程,该线程从
self.proc.stdout
读取数据并在while True
循环中将数据写入self.stdout_data
。 There is also a tkinter loop that takes the data out of the self.stdout_data
and puts it in the Label
widget.还有一个 tkinter 循环将数据从
self.stdout_data
中取出并放入Label
小部件中。
I didn't directly set the Label
's text from the thread because sometimes tkinter can crash if you call it from a different thread.我没有直接从线程中设置
Label
的文本,因为如果您从不同的线程调用它,有时 tkinter 可能会崩溃。
Another thing: I removed the StringVar
because I can just use: <tkinter.Label>.config(text=<new text>)
instead.另一件事:我删除了
StringVar
因为我可以使用: <tkinter.Label>.config(text=<new text>)
代替。
I have the problem too, this was my solution.我也有这个问题,这是我的解决方案。 I post it on [https://github.com/ianfun/notes/blob/main/editor/main.py].
我将其发布在 [https://github.com/ianfun/notes/blob/main/editor/main.py] 上。
data.py was generate by build.py. data.py 由 build.py 生成。
I cannot found grate terminal like vscode in python.我在 python 中找不到像 vscode 这样的格栅终端。
note:use key right to select注意:使用 select 的密钥权限
you can read idlelib run.py你可以阅读 idlelib run.py
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.