【问题标题】:How to send subprocess text output to a Tkinter text widget?如何将子进程文本输出发送到 Tkinter 文本小部件?
【发布时间】:2019-02-14 18:54:25
【问题描述】:
import subprocess
import os 
import time
from tkinter import *
root=Tk()
textbox=Text(root)
textbox.pack()


def redirector(inputStr):
    textbox.insert(INSERT, inputStr)

def address_ping():
        '''
        DOCSTRING -> Ip addresses in servers.txt are
                     192.168.0.1     192.168.0.26
        '''
        while True:
            with open('servers.txt', 'r') as f:
                for ip in f:
                    result=subprocess.Popen(["ping", "-c", "7", "-n", "-W", "2", ip],stdout=f, stderr=f).wait()
                    if result:
                        print("ip address " + ip, "is inactive")
                        sys.stdout.write = redirector

                    else:
                        print("ip address " + ip, "is active")
                        sys.stdout.write = redirector
                    pass    

address_ping()        

root.mainloop()

我正在这里编写一段代码,它将向 IP 地址发送 ping 并返回结果。它在 CLI 上运行良好,但是我希望使用 Tkinter 将其“打印”到Text 小部件。我正处于将它发送到Text 小部件 GUI 的地步,但它只有在我中断程序后才会显示。我希望在 ping 循环过程中滚动输出到 GUI 文本区域。

【问题讨论】:

  • 在这个网站上搜索[tkinter] sleep,你会发现数百个与在tkinter 程序中使用sleep 相关的问题和答案。短版:你不应该在 tkinter 应用程序的主线程中使用 sleep
  • 嗨,布莱恩,感谢您的回复。我不知道在 Tkinter 中不包含睡眠功能。我已将其删除,但程序的行为仍然几乎相同 - 我的文本区域充满了更多 ping 条目。
  • 您可以使用或修改此errorwindow3k 模块中发布的answer 中的代码来做您想做的事情。顺便说一句,您可以使用通用 tkinter after() 方法在 tkinter 应用程序中“休眠”。
  • “我在 if 和 else 之后设置 root.update()”:这仍然不是事件驱动编程。阅读Run your own code alongside Tkinter's event loop
  • @martineau:在我看来,从来没有理由。规范文档非常清楚:“当命令处于休眠状态时,应用程序不会响应事件。”

标签: python tkinter text-widget


【解决方案1】:

这是使用多线程的东西,似乎可以满足您的需求。主程序分为处理 QUI 的部分和一个单独的 workerthread 管理 ping subprocess,从中收集结果并将它们放入 Queue 中,其内容会定期传输到 GUI。

它使用time.sleep(),因为它是在一个单独的线程中完成的,使用 tkinter 所以没关系。

请注意,您可能希望在 GUI 中添加一个垂直滚动条。

import subprocess
import queue
import threading
import time
import tkinter as tk


class GuiPart:
    def __init__(self, master, queue, end_command):
        self.queue = queue
        self.master = master
        self.textbox = tk.Text(root)
        self.textbox.pack()
        btn = tk.Button(master, text='Quit', command=end_command)
        btn.pack(expand=True)

    def process_incoming(self):
        """ Handle all messages currently in the queue. """
        while self.queue.qsize():
            try:
                info = self.queue.get_nowait()
                self.textbox.insert(tk.INSERT, info)
            except queue.Empty:  # Shouldn't happen.
                pass


class ThreadedClient:
    """ Launch the main part of the GUI and the worker thread.
        periodic_call() and end_application() could reside in the GUI part, but
        putting them here keeps all the thread controls in a single place.
    """
    def __init__(self, master):
        self.master = master
        self.queue = queue.Queue()

        # Set up the GUI part.
        self.gui = GuiPart(master, self.queue, self.end_application)

        # Set up the background processing thread.
        self.running = True
        self.thread = threading.Thread(target=self.workerthread)
        self.thread.start()

        # Start periodic checking of the queue.
        self.periodic_call(200)

    def periodic_call(self, delay):
        """ Every delay ms process everything new in the queue. """
        self.gui.process_incoming()
        if not self.running:
            sys.exit(1)
        self.master.after(delay, self.periodic_call, delay)

    # Runs in separate thread - NO tkinter calls allowed.
    def workerthread(self):
        while self.running:
            with open('servers.txt', 'r') as file:
                for ip in file:
                    rc = subprocess.Popen(["ping", "-c", "7", "-n", "-W", "2", ip]).wait()
                    if rc:
                        self.queue.put('ip address {} is inactive\n'.format(ip))
                    time.sleep(1)

    def end_application(self):
        self.running = False  # Stop queue checking.
        self.master.quit()


if __name__ == '__main__':
    root = tk.Tk()
    root.title('Pinger')
    client = ThreadedClient(root)
    root.mainloop()  # Display application window and start tkinter event loop.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    相关资源
    最近更新 更多