2009-03-20 5 views
15

Ho creato un programma che stampa i risultati sulla riga di comando. (È il server e stampa la riga di comando di accesso.)Reindirizza i risultati della riga di comando a una GUI di tkinter

Ora, voglio vedere lo stesso risultato alla GUI.

Come posso reindirizzare i risultati della riga di comando alla GUI?

Si prega di suggerire un trucco per trasformare facilmente l'applicazione della console in una GUI semplice.

Nota che dovrebbe funzionare su Linux e Windows.

+0

correlato: [Display uscita in tempo reale di un sottoprocesso in un widget Tkinter] (http://stackoverflow.com/q/15362372/4279) – jfs

risposta

9

È possibile creare un wrapper di script che esegua il programma della riga di comando come processo secondario, 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 script è il programma. Ovviamente puoi stampare gli errori in un colore diverso, o qualcosa del genere.

+0

ho provato su Linux, non il suo lavoro, si prega di suggerire una soluzione .. –

+0

Manca un root.mainloop() alla fine per avviare il ciclo degli eventi. a parte questo, sembra che dovrebbe funzionare. – mavnn

+0

Sì, non avevo un sistema linux per provarlo, quindi era tutto dalla memoria ... –

4

Reindirizzamento dello stdout a un metodo write() che aggiorna il tuo gui è un modo per andare, e probabilmente il più veloce - sebbene l'esecuzione di un sottoprocesso sia probabilmente una soluzione più elegante.

Reindirizza stderr solo quando sei davvero sicuro che funzioni e sia in funzione, comunque!

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!" 
+0

non reindirizza 'sys.stdout' al livello del descrittore di file, ad esempio, se chiamate' os.write (1, b'not reindirizzato ') 'in' test_script.py', non lo vedrete nella GUI.Vedi [Reindirizza lo stdout in un file in Python?] (Http://stackoverflow.com/a/22434262/4279) – jfs

7

Per visualizzare sottoprocesso uscita in una GUI mentre è ancora in esecuzione, una soluzione solo stdlib che funziona su entrambi Python 2 e 3 deve utilizzare 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 è:

  • messo l'uscita sottoprocesso nella coda in un thread in background
  • ottenere l'uscita dalla coda nel thread GUI.

, ad esempio chiamare process.readline() nel thread in background -> coda -> aggiornare l'etichetta della GUI nel thread principale.

Problemi correlati