Rediriger les résultats de la ligne de commande pour une interface utilisateur graphique tkinter

StackOverflow https://stackoverflow.com/questions/665566

  •  21-08-2019
  •  | 
  •  

Question

Je l'ai créé un programme qui imprime des résultats sur la ligne de commande. (Il est serveur et il imprime journal en ligne de commande.)

Maintenant, je veux voir le même résultat à l'interface graphique.

Comment puis-je rediriger les résultats de la ligne de commande à l'interface graphique?

S'il vous plaît, suggérer une astuce pour transformer facilement l'application de la console à l'interface graphique simple.

Notez que cela devrait fonctionner sous Linux et Windows.

Était-ce utile?

La solution

Vous pouvez créer une enveloppe de script qui exécute votre programme de ligne de commande en tant que sous-processus, puis ajouter la sortie à quelque chose comme un widget texte.

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()

où script est votre programme. Vous pouvez évidemment imprimer les erreurs dans une couleur différente, ou quelque chose comme ça.

Autres conseils

Pour afficher le sous-processus sortie dans une interface graphique alors qu'il est encore en cours d'exécution , une solution stdlib seule portable qui fonctionne aussi bien sur Python 2 et 3 doit utiliser un fil de fond:

#!/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'essence de la solution est:

  • mettre la sortie du sous-processus dans la file d'attente dans un thread d'arrière-plan
  • obtenir la sortie de la file d'attente dans le thread GUI.
.

i.e., appeler process.readline() dans le fil de fond -> attente -> étiquette graphique de mise à jour dans le fil principal. kill-process.py (pas d'interrogation - solution moins un portable qui utilise dans un event_generate fil de fond).

Réorientation stdout à une méthode d'écriture () qui met à jour votre IUG est une façon d'aller, et probablement le plus rapide - bien que l'exécution d'un sous-processus est probablement une solution plus élégante.

Seulement une fois que vous rediriger ce flux êtes vraiment confiant qu'il est et travailler, bien!

Exemple implimentation (fichier script et IUG 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!"

Désolé pour mon mauvais anglais. En fait, je, utilisé d'une manière différente d'imprimer sortie de la commande rapide dans mon nouvel outil d'automatisation. S'il vous plaît trouver ces étapes ci-dessous.

1> Créer un fichier Bat & rediriger sa sortie vers un fichier LOG. Commande invite de commande: tasklist /svc

2> Faire lire ce fichier avec Python 3.x. `ProcessedFile = open ( 'D: \ LOG \ taskLog.txt', 'r')

3> L'étape finale. ttk.Label(Tab4, text=[ProcessFile.read()]).place(x=0, y=27)

** Par conséquent s'il vous plaît être informé que, je pas inclure ce code dans la barre de défilement encore.

Affichage Capture d'écran:

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top