首页 > 解决方案 > 如何在 tkinter gui 中实现 cmd 输出?

问题描述

我在 stackoverflow 上经历了许多解决方案,但没有一个对我有帮助。我坚持在 tkinter 中实现 cmd 以查看 gui 内的输出并能够在那里输入值。感谢您的帮助,感谢您的提前!

from subprocess import Popen
from tkinter import Tk, Button, messagebox, Label
from PIL import ImageTk, Image


gui = Tk(className='IDPass')
gui.geometry('500x500')
gui.iconbitmap('Turnstile/icons/mini_logo.ico')
img = ImageTk.PhotoImage(Image.open('Turnstile/icons/logo.png'))
panel = Label(gui, image=img)


def run_server():
    global process
    process = Popen(['python', 'C:/Test/Turnstile/manage.py', 'runserver'])

def run_rfid_scanner():
    global process
    process = Popen('python C:/Test/Turnstile/rfid_scanner.py')
    
def run_face_scanner():
    global process
    process = Popen('python C:/Test/Turnstile/face_scanner.py')
    
def run_photo_deleter():
    global process
    process = Popen('python C:/Test/Turnstile/photo_deleter.py')
    
def run_face_recognizer():
    global process
    process = Popen('python C:/Test/Turnstile/face_recognizer.py')

def stop_program():
    process.kill()
    messagebox.showinfo('Информационное окно', 'Программа остановлена')


server = Button(gui, text='Запустить сервер', command=run_server, bg='green')
rfid_scanner = Button(gui, text='Запустить RFID сканер', command=run_rfid_scanner, bg='green')
face_scanner = Button(gui, text='Добавить фото для сканирования', command=run_face_scanner, bg='green')
face_recognizer = Button(gui, text='Начать распознавание лица', command=run_face_recognizer, bg='green')

photo_deleter = Button(gui, text='Удалить фото пользователя', command=run_photo_deleter, bg='grey')
stop_programm = Button(gui, text='Остановить выполнение программы', command=stop_program, bg='grey')

panel.pack()
server.pack()
rfid_scanner.pack()
face_scanner.pack()
face_recognizer.pack()
photo_deleter.pack()
stop_programm.pack()

gui.mainloop()

这就是我想看到的样子

在此处输入图像描述

标签: pythontkintersubprocess

解决方案


尝试这个:

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, last_loop:bool=False) -> 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:
                if last_loop:
                    return None
                super().after(100, self.stdout_loop, True)
            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, 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()

    def read_stdout(self):
        while self.proc.poll() is None:
            self._read_stdout()
        self._read_stdout()
        self.proc = None

    def _read_stdout(self) -> None:
        line = self.proc.stdout.readline()
        with self.stdout_buffer_lock:
            self.stdout_buffer += line.decode()


if __name__ == "__main__":
    def start_echo():
        command = ["echo", "hi"]
        tkinter_popen.run_commands([command])

    def start_ping():
        # For linux use "-c". For windows use "-n"
        command = ["ping", "1.1.1.1", "-n", "3"]
        tkinter_popen.run_commands([command])

    root = tk.Tk()

    tkinter_popen = TkinterPopen(root)
    tkinter_popen.pack()

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

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

    root.mainloop()

我认为这是您想要的功能。该代码类似于@acw1668,但我stdout在另一个线程中读取并在名为self.stdout_buffer.

这只是我在这里给出的答案的副本。


推荐阅读