Reputation: 35
I have gone through many solutions on stackoverflow, but none was helpful to me. I'm stuck on implementing cmd into tkinter to see output inside of gui and be able to enter values there. I appreciate any help, thanks for advance!
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()
This is how I want to see it
Upvotes: 0
Views: 1531
Reputation: 7710
Try this:
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()
I think this is the functionality that you wanted. The code is similar to @acw1668 but I read stdout
in another thread and out the data in a queue named self.stdout_buffer
.
This is just a copy of the answer I gave here.
Upvotes: 1
Reputation: 47194
One of the way is:
Text
box to show the command outputsubprocess.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()
Note that I have used sys.executable
instead of 'python'
to make sure same Python interpreter is used.
Upvotes: 1