如何在tkinter gui中实现cmd输出?

2024-06-16 08:42:33 发布

您现在位置:Python中文网/ 问答频道 /正文

我已经讨论了许多有关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()

这就是我希望看到它的方式

enter image description here


Tags: runtexttestdefguibuttonprocessglobal
2条回答

试试这个:

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的队列中取出了数据

这只是我给出的答案的副本

其中一个方法是:

  • 创建一个Text框以显示命令输出
  • 创建一个线程化任务以获取流程输出,并将输出放入队列中
  • 创建定期任务以从队列中获取输出,并将其插入文本框
  • 使用subprocess.PIPE重定向命令输出
import sys
import threading
from queue import Queue
from subprocess import Popen, PIPE
from tkinter import Tk, Button, messagebox, Label, Text
...
process = None
queue = Queue()

def run_server():
    global process
    if process:
        process.terminate()
    #process = Popen(['python', 'C:/Test/Turnstile/manage.py', 'runserver'])
    process = Popen([sys.executable, '-u', 'C:/Test/Turnstile/manage.py', 'runserver'], stdout=PIPE, bufsize=1, text=True)

...

output = Text(gui, width=100, height=20)
output.pack(padx=20, pady=20)

def monitor_output(q):
    while True:
        if process and process.stdout:
            msg = process.stdout.readline()
            if msg:
                q.put(msg)

def check_output(q):
    while not q.empty():
        output.insert('end', q.get())
        output.see('end')
    gui.after(10, check_output, q)

threading.Thread(target=monitor_output, args=[queue], daemon=True).start()
check_output(queue)

gui.mainloop()

注意,我使用了sys.executable而不是'python'来确保使用相同的Python解释器

相关问题 更多 >