运行带有实时输出到 Tkinter GUI 的进程 [英] Run process with realtime output to a Tkinter GUI

查看:115
本文介绍了运行带有实时输出到 Tkinter GUI 的进程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I was trying to create a GUI in Tkinter python. I want to display the output of a tool to my Tkinter interface. The tool works great in command line but it is a continuous scanner. Somewhat like a continuous ping (I mean by ping command in Linux with no options).

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

Now the problem is since the output of ping is never complete, therefore I cannot print the output in Tkinter. It also makes my application go freeze. I also cannot stop the command after few seconds to display output. Run process with realtime output in PHP I found this above link helpful for php, but How can I convert this code in python:

https://stackoverflow.com/a/6144213/4931414

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

Here is some sample code that I want to display on tkinter frame

#!/usr....

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

这在命令行上效果很好,但我没有在 tkinter 界面上获得输出.

This works great on command line but I am not getting output on tkinter interface.

推荐答案

首先,我必须承认我对模块subprocessthreading,但我尝试创建一个简单的控制台来接受您编写命令,其输出将显示在 Text 小部件.

First of all, I must admit that I am not so familiar with the module subprocess and threading, but I have tried to create a simple console that accepts you to write a command, whose output will be shown in a Text widget.

基本思想是有一个新的并行运行线程,当您单击按钮Execute 时,它会处理命令.我们不断遍历 stdout 行并将它们插入到 Text 小部件中.

The basic idea is to have a new running parallel thread that processes a command when you click the button Execute. We keep iterating through the lines of stdout and inserting them into the Text widget.

它似乎适用于任何命令,但我很确定存在一些问题和错误.如果你们更熟悉我上面引用的模块,发现我的代码有任何严重问题,或者有任何改进建议,我一定会听取你们的意见,以改进这个例子.

It seems to work for any command, but I am pretty sure that there are some problems and mistakes. If you guys more familiar with the modules I cited above see any serious problem with my code, or have any suggestions to improve it, I would definitely listen to you in order to improve this example.

现在,这是代码:

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 GUI 的进程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆