Redirect risultati riga di comando per una GUI Tkinter
-
21-08-2019 - |
Domanda
Ho creato un programma che consente di stampare i risultati sulla linea di comando. (È server e la stampa di registro sulla riga di comando.)
Ora, voglio vedere lo stesso risultato di GUI.
Come faccio a reindirizzare i risultati della riga di comando di GUI?
Per cortesia, indicate un trucco per trasformare facilmente un'applicazione console di semplice interfaccia grafica.
Si noti che dovrebbe funzionare su Linux e Windows.
Soluzione
Si potrebbe creare un wrapper script che esegue il programma da riga di comando come un processo sub, quindi aggiungere l'output a qualcosa di simile a un widget di testo.
from tkinter import *
import subprocess as sub
p = sub.Popen('./script',stdout=sub.PIPE,stderr=sub.PIPE)
output, errors = p.communicate()
root = Tk()
text = Text(root)
text.pack()
text.insert(END, output)
root.mainloop()
dove lo script è il tuo programma. È ovviamente possibile stampare gli errori in un colore diverso, o qualcosa del genere.
Altri suggerimenti
Per visualizzare il sottoprocesso uscita in una GUI mentre è ancora in esecuzione , una soluzione stdlib solo portatile che funziona sia su Python 2 e 3 deve usare un thread in background:
#!/usr/bin/python
"""
- read output from a subprocess in a background thread
- show the output in the GUI
"""
import sys
from itertools import islice
from subprocess import Popen, PIPE
from textwrap import dedent
from threading import Thread
try:
import Tkinter as tk
from Queue import Queue, Empty
except ImportError:
import tkinter as tk # Python 3
from queue import Queue, Empty # Python 3
def iter_except(function, exception):
"""Works like builtin 2-argument `iter()`, but stops on `exception`."""
try:
while True:
yield function()
except exception:
return
class DisplaySubprocessOutputDemo:
def __init__(self, root):
self.root = root
# start dummy subprocess to generate some output
self.process = Popen([sys.executable, "-u", "-c", dedent("""
import itertools, time
for i in itertools.count():
print("%d.%d" % divmod(i, 10))
time.sleep(0.1)
""")], stdout=PIPE)
# launch thread to read the subprocess output
# (put the subprocess output into the queue in a background thread,
# get output from the queue in the GUI thread.
# Output chain: process.readline -> queue -> label)
q = Queue(maxsize=1024) # limit output buffering (may stall subprocess)
t = Thread(target=self.reader_thread, args=[q])
t.daemon = True # close pipe if GUI process exits
t.start()
# show subprocess' stdout in GUI
self.label = tk.Label(root, text=" ", font=(None, 200))
self.label.pack(ipadx=4, padx=4, ipady=4, pady=4, fill='both')
self.update(q) # start update loop
def reader_thread(self, q):
"""Read subprocess output and put it into the queue."""
try:
with self.process.stdout as pipe:
for line in iter(pipe.readline, b''):
q.put(line)
finally:
q.put(None)
def update(self, q):
"""Update GUI with items from the queue."""
for line in iter_except(q.get_nowait, Empty): # display all content
if line is None:
self.quit()
return
else:
self.label['text'] = line # update GUI
break # display no more than one line per 40 milliseconds
self.root.after(40, self.update, q) # schedule next update
def quit(self):
self.process.kill() # exit subprocess if GUI is closed (zombie!)
self.root.destroy()
root = tk.Tk()
app = DisplaySubprocessOutputDemo(root)
root.protocol("WM_DELETE_WINDOW", app.quit)
# center window
root.eval('tk::PlaceWindow %s center' % root.winfo_pathname(root.winfo_id()))
root.mainloop()
L'essenza della soluzione è:
- mettere l'uscita sottoprocesso nella coda in un thread in background
- ottenere l'uscita dalla coda nel thread GUI.
cioè, chiamare process.readline()
in thread in background -> coda -> etichetta aggiornamento GUI nel thread principale. Related kill-process.py
(senza polling - una soluzione portatile meno che event_generate
utilizza in un thread in background).
Ridirezionamento stdout a un metodo write () che aggiorna la vostra interfaccia grafica è un modo per andare, e probabilmente il più veloce - anche se l'esecuzione di un sottoprocesso è probabilmente una soluzione più elegante.
reindirizzare stderr Solo una volta che sei davvero sicuro che è installato e funzionante, però!
Esempio implimentation (file GUI e script di test):
test_gui.py:
from Tkinter import *
import sys
sys.path.append("/path/to/script/file/directory/")
class App(Frame):
def run_script(self):
sys.stdout = self
## sys.stderr = self
try:
del(sys.modules["test_script"])
except:
## Yeah, it's a real ugly solution...
pass
import test_script
test_script.HelloWorld()
sys.stdout = sys.__stdout__
## sys.stderr = __stderr__
def build_widgets(self):
self.text1 = Text(self)
self.text1.pack(side=TOP)
self.button = Button(self)
self.button["text"] = "Trigger script"
self.button["command"] = self.run_script
self.button.pack(side=TOP)
def write(self, txt):
self.text1.insert(INSERT, txt)
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.build_widgets()
root = Tk()
app = App(master = root)
app.mainloop()
test_script.py:
print "Hello world!"
def HelloWorld():
print "HelloWorldFromDef!"
Ci dispiace per il mio cattivo inglese. Io in realtà, usato un modo diverso per stampare Command Prompt uscita nel mio nuovo strumento di automazione. Si prega di trovare quei passaggi di seguito.
1> Crea un file bat e reindirizzare l'output ad un file di log.
Comando del prompt dei comandi: tasklist /svc
2> Fare leggere quel file con Python 3.x. `ProcessedFile = open ( 'D: \ LOG \ taskLog.txt', 'r')
3> Il passo Finale.
ttk.Label(Tab4, text=[ProcessFile.read()]).place(x=0, y=27)
** Quindi Si informa che, non ho includere barra di scorrimento in questo codice ancora.
Schermata della pubblicazione: