Python 运行进程并将实时输出到Tkinter GUI

Python 运行进程并将实时输出到Tkinter GUI,python,python-2.7,python-3.x,tkinter,Python,Python 2.7,Python 3.x,Tkinter,我试图用Tkinter python创建GUI。我想在Tkinter界面上显示工具的输出。该工具在命令行中运行良好,但它是一个连续扫描工具。有点像连续ping(我指的是Linux中没有选项的ping命令) 现在的问题是,由于ping的输出永远不会完成,因此我无法在Tkinter中打印输出。它还使我的应用程序冻结。我也无法在几秒钟后停止命令以显示输出。 我发现上面的链接对php很有帮助,但如何在python中转换此代码: 下面是我想在tkinter框架上显示的一些示例代码 #!/usr....

我试图用Tkinter python创建GUI。我想在Tkinter界面上显示工具的输出。该工具在命令行中运行良好,但它是一个连续扫描工具。有点像连续ping(我指的是Linux中没有选项的ping命令)

现在的问题是,由于ping的输出永远不会完成,因此我无法在Tkinter中打印输出。它还使我的应用程序冻结。我也无法在几秒钟后停止命令以显示输出。 我发现上面的链接对php很有帮助,但如何在python中转换此代码:

下面是我想在tkinter框架上显示的一些示例代码

#!/usr....

import subprocess
x = subprocess.call(["ping", "127.0.0.1"])
print x

这在命令行上非常有效,但我没有在tkinter界面上获得输出。

首先,我必须承认我对该模块不太熟悉,但我尝试创建一个简单的控制台,它接受您编写命令,其输出将显示在小部件中

基本思想是创建一个新的运行并行线程,当您单击按钮
Execute
时,该线程将处理命令。我们不断迭代
stdout
的行,并将它们插入到文本小部件中

这似乎对任何命令都有效,但我非常确定存在一些问题和错误。如果你们更熟悉我上面提到的模块,看到我的代码有任何严重的问题,或者有任何改进的建议,为了改进这个例子,我一定会听你们的

下面是代码:

import tkinter as tk
from tkinter.scrolledtext import ScrolledText
import threading
from subprocess import Popen, PIPE


class Console(tk.Frame):

    """Simple console that can execute bash commands"""

    def __init__(self, master, *args, **kwargs):
        tk.Frame.__init__(self, master, *args, **kwargs)

        self.text_options = {"state": "disabled",
                             "bg": "black",
                             "fg": "#08c614",
                             "insertbackground": "#08c614",
                             "selectbackground": "#f01c1c"}

        self.text = ScrolledText(self, **self.text_options)

        # It seems not to work when Text is disabled...
        # self.text.bind("<<Modified>>", lambda: self.text.frame.see(tk.END))

        self.text.pack(expand=True, fill="both")

        # bash command, for example 'ping localhost' or 'pwd'
        # that will be executed when "Execute" is pressed
        self.command = ""  
        self.popen = None     # will hold a reference to a Popen object
        self.running = False  # True if the process is running

        self.bottom = tk.Frame(self)

        self.prompt = tk.Label(self.bottom, text="Enter the command: ")
        self.prompt.pack(side="left", fill="x")
        self.entry = tk.Entry(self.bottom)
        self.entry.bind("<Return>", self.start_thread)
        self.entry.bind("<Command-a>", lambda e: self.entry.select_range(0, "end"))
        self.entry.bind("<Command-c>", self.clear)
        self.entry.focus()
        self.entry.pack(side="left", fill="x", expand=True)

        self.executer = tk.Button(self.bottom, text="Execute", command=self.start_thread)
        self.executer.pack(side="left", padx=5, pady=2)
        self.clearer = tk.Button(self.bottom, text="Clear", command=self.clear)
        self.clearer.pack(side="left", padx=5, pady=2)
        self.stopper = tk.Button(self.bottom, text="Stop", command=self.stop)
        self.stopper.pack(side="left", padx=5, pady=2)

        self.bottom.pack(side="bottom", fill="both")

    def clear_text(self):
        """Clears the Text widget"""
        self.text.config(state="normal")
        self.text.delete(1.0, "end-1c")
        self.text.config(state="disabled")

    def clear_entry(self):
        """Clears the Entry command widget"""
        self.entry.delete(0, "end")

    def clear(self, event=None):
        """Does not stop an eventual running process,
        but just clears the Text and Entry widgets."""
        self.clear_entry()
        self.clear_text()

    def show(self, message):
        """Inserts message into the Text wiget"""
        self.text.config(state="normal")
        self.text.insert("end", message)
        self.text.see("end")
        self.text.config(state="disabled")

    def start_thread(self, event=None):
        """Starts a new thread and calls process"""
        self.stop()
        self.running = True
        self.command = self.entry.get()
        # self.process is called by the Thread's run method
        threading.Thread(target=self.process).start()

    def process(self):
        """Runs in an infinite loop until self.running is False""" 
        while self.running:
            self.execute()

    def stop(self):
        """Stops an eventual running process"""
        if self.popen:
            try:
                self.popen.kill()
            except ProcessLookupError:
                pass 
        self.running = False

    def execute(self):
        """Keeps inserting line by line into self.text
        the output of the execution of self.command"""
        try:
            # self.popen is a Popen object
            self.popen = Popen(self.command.split(), stdout=PIPE, bufsize=1)
            lines_iterator = iter(self.popen.stdout.readline, b"")

            # poll() return None if the process has not terminated
            # otherwise poll() returns the process's exit code
            while self.popen.poll() is None:
                for line in lines_iterator:
                    self.show(line.decode("utf-8"))
            self.show("Process " + self.command  + " terminated.\n\n")

        except FileNotFoundError:
            self.show("Unknown command: " + self.command + "\n\n")                               
        except IndexError:
            self.show("No command entered\n\n")

        self.stop()


if __name__ == "__main__":
    root = tk.Tk()
    root.title("Console")
    Console(root).pack(expand=True, fill="both")
    root.mainloop()
将tkinter作为tk导入
从tkinter.scrolledtext导入scrolledtext
导入线程
从子流程导入Popen、PIPE
类控制台(tk.Frame):
“”“可以执行bash命令的简单控制台”“”
定义初始值(self、master、*args、**kwargs):
tk.Frame.\uuuuu init\uuuuuu(self,master,*args,**kwargs)
self.text_options={“state”:“disabled”,
“背景”:“黑色”,
“前景”:“08c614”,
“insertbackground”:“08c614”,
“选择背景”:“#f01c1c”}
self.text=滚动文本(self,**self.text\u选项)
#当文本被禁用时,它似乎不起作用。。。
#self.text.bind(“,lambda:self.text.frame.see(tk.END))
self.text.pack(expand=True,fill=“两者”)
#bash命令,例如“ping localhost”或“pwd”
#按下“Execute”(执行)时将执行的
self.command=“”
self.popen=None#将保存对popen对象的引用
self.running=False#如果进程正在运行,则为True
self.bottom=tk.Frame(self)
self.prompt=tk.Label(self.bottom,text=“输入命令:”)
自我提示包装(side=“left”,fill=“x”)
self.entry=tk.entry(self.bottom)
self.entry.bind(“,self.start\u线程)
self.entry.bind(“,lambda e:self.entry.select_range(0,“end”))
self.entry.bind(“,self.clear)
self.entry.focus()
self.entry.pack(side=“left”,fill=“x”,expand=True)
self.executer=tk.Button(self.bottom,text=“Execute”,command=self.start\u线程)
self.executer.pack(side=“left”,padx=5,pady=2)
self.Clear=tk.Button(self.bottom,text=“Clear”,command=self.Clear)
self.clear.pack(side=“left”,padx=5,pady=2)
self.stopper=tk.按钮(self.bottom,text=“Stop”,command=self.Stop)
自止动块包装(侧=“左”,padx=5,pady=2)
self.bottom.pack(side=“bottom”,fill=“bottom”)
def清除文本(自身):
“”“清除文本小部件”“”
self.text.config(state=“normal”)
self.text.delete(1.0,“end-1c”)
self.text.config(state=“disabled”)
def清除_条目(自身):
“”“清除输入命令小部件”“”
self.entry.delete(0,“结束”)
def清除(自身,事件=无):
“”不会停止最终运行的进程,
但只需清除文本和条目小部件即可
self.clear_entry()
self.clear_text()
def显示(自我,信息):
“”“将消息插入文本wiget”“”
self.text.config(state=“normal”)
self.text.insert(“结束”,消息)
self.text.see(“结束”)
self.text.config(state=“disabled”)
def start_线程(自身,事件=无):
“”“启动新线程并调用进程”“”
self.stop()
self.running=True
self.command=self.entry.get()
#self.process由线程的run方法调用
threading.Thread(target=self.process).start()
def过程(自我):
“”“在无限循环中运行,直到self.running为False”“”
自运行时:
self.execute()
def停止(自):
“”“停止最终运行的进程”“”
如果self.popen:
尝试:
self.popen.kill()
除ProcessLookupError外:
通过
self.running=False
def执行(自我):
“”“将逐行插入self.text中
执行self.command“”的输出
尝试:
#self.popen是一个popen对象
self.popen=popen(self.command.split(),stdout=PIPE,bufsize=1)
行迭代器=iter(self.popen.stdout.readline,b“”)
#poll()如果进程未终止,则返回None
#否则poll()返回进程的退出代码
而self.popen.poll()为无:
对于行中的行\u迭代器:
自显示(行解码(“utf-8”))
self.show(“进程”+self.command+”已终止。\n\n)
除FileNotFoundError外:
self.show(“未知命令:“+self.command+”\n\n”)
除索引器外:
self.show(“未输入命令\n\n”)
self.stop()
如果名称=“\uuuuu main\uuuuuuuu”:
root=tk.tk()
root.title(“控制台”)
控制台(root).pack(expand=True,fill=“两者”)
import subprocess
x = subprocess.call(["ping", "127.0.0.1"])
print "x is", x   ## or comment out this line