首页 > 解决方案 > 如果我想在处理过程中看到时钟图标怎么办?

问题描述

如果我想在处理过程中看到时钟图标怎么办?

def refresh():
    window.config(cursor="clock")
    p = subprocess.Popen("ping 8.8.8.8 -c 3", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    while p.stdout is not None:
        line = p.stdout.readline()
        # Add line in list and remove carriage return
        if not line:
            p.stdout.flush()
            break
        text.insert(INSERT, "test\n")


btn_refresh = Button(frame2, text="Refresh Data", command=refresh)

标签: pythonpython-3.xtkinter

解决方案


尝试这个:

from subprocess import Popen, PIPE
from threading import Thread, Lock
import tkinter as tk

# Global variables
proc = None
running = True
stdout_buffer = ""
stdout_buffer_lock = Lock()


def stdout_loop():
    global stdout_buffer
    with stdout_buffer_lock:
        # Get the data and clear the buffer:
        data, stdout_buffer = stdout_buffer, ""
    text.config(state="normal")
    text.insert("end", data)
    text.see("end")
    text.config(state="disabled")
    if proc is None:
        if len(commands) == 0:
            # If we are done with all of the commands:
            text.config(cursor="")
            root.config(cursor="")
        else:
            # If we have more commands to do call `start_next_proc`
            start_next_proc()
    else:
        root.after(100, stdout_loop)

def start_next_proc():
    global proc, commands
    command = commands.pop(0) # Take the first one from the list
    proc = Popen(command, shell=True, stdout=PIPE)
    new_thread = Thread(target=read_stdout, daemon=True)
    new_thread.start()
    stdout_loop()

def start_new_proc():
    global commands
    # For linux use "-c 5". For windows use "-n 5"
    commands = ["ping 8.8.8.8 -c 5",
                "ping 1.1.1.1 -c 5",
                "ping 5.5.5.5 -c 5"]
    start_next_proc()

    text.config(cursor="clock")
    root.config(cursor="clock")

def read_stdout():
    global proc, stdout_buffer
    while proc.poll() is None:
        line = proc.stdout.readline()
        with stdout_buffer_lock:
            stdout_buffer += line.decode()
    proc = None


root = tk.Tk()

text = tk.Text(root, state="disabled")
text.pack()

button = tk.Button(root, text="Run", command=start_new_proc)
button.pack()

root.mainloop()

在代码中,我在主线程中启动进程,然后启动 2 个循环。其中一个循环是while内部的循环read_stdout。它从进程中读取所有数据并将其写入stdout_buffer. 第二个循环是一个tkinter循环(在 stdout_loop 内),它在小部件stdout_buffer内读取和写入它Text

我还使用锁来确保缓冲区不会被破坏。

如果tkinter是线程安全的,那么代码会短得多,但事实并非如此。

如果你想要它作为一个类:

from subprocess import Popen, PIPE
from threading import Thread, Lock
import tkinter as tk


class TkinterPopen(tk.Text):
    def __init__(self, master, state="disabled", **kwargs):
        super().__init__(master, state=state, **kwargs)
        self.commands = []
        self.proc = None
        self.running = True
        self.stdout_buffer = ""
        self.stdout_buffer_lock = Lock()

    def stdout_loop(self) -> None:
        with self.stdout_buffer_lock:
            # Get the data and clear the buffer:
            data, self.stdout_buffer = self.stdout_buffer, ""
        state = super().cget("state")
        super().config(state="normal")
        super().insert("end", data)
        super().see("end")
        super().config(state=state)
        if self.proc is None:
            if len(self.commands) == 0:
                # If we are done with all of the commands:
                super().config(cursor="")
            else:
                # If we have more commands to do call `start_next_proc`
                self.start_next_proc()
        else:
            super().after(100, self.stdout_loop)

    def start_next_proc(self) -> None:
        command = self.commands.pop(0) # Take the first one from the list
        self.proc = Popen(command, shell=True, stdout=PIPE)
        new_thread = Thread(target=self.read_stdout, daemon=True)
        new_thread.start()
        self.stdout_loop()

    def run_commands(self, commands:list) -> None:
        self.commands = commands
        self.start_next_proc()
        super().config(cursor="clock")

    def read_stdout(self):
        while self.proc.poll() is None:
            line = self.proc.stdout.readline()
            with self.stdout_buffer_lock:
                self.stdout_buffer += line.decode()
        self.proc = None


if __name__ == "__main__":
    def start_commands():
        # For linux use "-c 5". For windows use "-n 5"
        commands = ["echo hi",
                    "ping 1.1.1.1 -n 3",
                    "ping 5.5.5.5 -n 3"]
        tkinter_popen.run_commands(commands)

    root = tk.Tk()

    tkinter_popen = TkinterPopen(root)
    tkinter_popen.pack()

    button = tk.Button(root, text="Run", command=start_commands)
    button.pack()

    root.mainloop()

推荐阅读