I would like to display my program’s “live” output on GUI (all what printed in it). how can i access to my output? and what the right way to display it for example in text box?
edited: where am i wrong? (I would like that the “hello world” to appear inside the text box. (Test2 is the running program))
from tkinter import * from subprocess import * print("Hello world") def func(): proc = Popen("Test2.py", stdout=PIPE, shell=True) proc = proc.communicate() output.insert(END, proc) Master = Tk() Check = Button(Master, text="Display output", command=func) Quit = Button(Master, text="Exit", fg="red", command=Master.quit) output = Text(Master, width=40, height=8) Check.pack(padx=20, pady=8) Quit.pack(padx=20, pady=18) output.pack() Master.mainloop()
I took the time to debug and modify the
errorwindow.py module in my answer to another question so it will work in both Python 2 and 3—the code in the linked answer was written for Python 2.x. Note I only did the minimum necessary to get it functioning under the two versions. The modified version of the script has been named
errorwindow3k.py (despite that fact it also works in Python 2).
The majority of the issues were simply due to module renaming, however there was a harder one to figure-out that turned-out was due to the switch to Unicode strings being the default string-type in version 3—apparently (on Windows anyway), pipes between processes are byte-streams, not Unicode characters. Fortunately the “fix” of decoding and then encoding the data on the other side also doesn’t hurt in Python 2 which made correcting the problem fairly easy.
This nice thing is that using it is very easy. Just
import it and from that point on any output sent to either
sys.stdout will cause
tkinter-based output windows to appear as needed to display the information. In your sample code just insert
import errorwindow3k somewhere before the
# Code derived from Bryan Olson's source posted in this related Usenet discussion: # https://groups.google.com/d/msg/comp.lang.python/HWPhLhXKUos/TpFeWxEE9nsJ # https://groups.google.com/d/msg/comp.lang.python/HWPhLhXKUos/eEHYAl4dH9YJ # # See the comments and doc string below. # # Here's a module to show stderr output from console-less Python # apps, and stay out of the way otherwise. I plan to make a ASPN # recipe of it, but I thought I'd run it by this group first. # # To use it, import the module. That's it. Upon import it will # assign sys.stderr. # # In the normal case, your code is perfect so nothing ever gets # written to stderr, and the module won't do much of anything. # Upon the first write to stderr, if any, the module will launch a # new process, and that process will show the stderr output in a # window. The window will live until dismissed; I hate, hate, hate # those vanishing-consoles-with-critical-information. # # The code shows some arguably-cool tricks. To fit everthing in # one file, the module runs the Python interpreter on itself; it # uses the "if __name__ == '__main__'" idiom to behave radically # differently upon import versus direct execution. It uses TkInter # for the window, but that's in a new process; it does not import # TkInter into your application. # # To try it out, save it to a file -- I call it "errorwindow.py" - # - and import it into some subsequently-incorrect code. For # example: # # import errorwindow # # a = 3 + 1 + nonesuchdefined # # should cause a window to appear, showing the traceback of a # Python NameError. # # -- # --Bryan # ---------------------------------------------------------------- # # martineau - Modified to use subprocess.Popen instead of the os.popen # which has been deprecated since Py 2.6. Changed so it # redirects both stdout and stderr. Added numerous # comments, and also inserted double quotes around paths # in case they have embedded space characters in them, as # they did on my Windows system. # # Recently updated it to work in both Python 2 and Python 3. """ Import this module into graphical Python apps to provide a sys.stderr. No functions to call, just import it. It uses only facilities in the Python standard distribution. If nothing is ever written to stderr, then the module just sits there and stays out of your face. Upon write to stderr, it launches a new process, piping it error stream. The new process throws up a window showing the error messages. """ import subprocess import sys try: import thread except ModuleNotFoundError: # Python 3 import _thread as thread import os EXC_INFO_FILENAME = 'exc_info.txt' if __name__ == '__main__': # When spawned as separate process. # create window in which to display output # then copy stdin to the window until EOF # will happen when output is sent to each OutputPipe created try: from Tkinter import BOTH, END, Frame, Text, TOP, YES import tkFont import Queue except ModuleNotFoundError: # Python 3 from tkinter import BOTH, END, Frame, Text, TOP, YES import tkinter.font as tkFont import queue as Queue Q_EMPTY = Queue.Empty # An exception class. queue = Queue.Queue(1000) # FIFO def read_stdin(app, bufsize=4096): fd = sys.stdin.fileno() # File descriptor for os.read() calls. read = os.read put = queue.put while True: put(read(fd, bufsize)) class Application(Frame): def __init__(self, master=None, font_size=8, text_color='#0000AA', rows=25, cols=100): Frame.__init__(self, master) # Create title based on the arguments passed to the spawned script: # argv: name of this script (ignored) # argv: name of script that imported this module # argv: name of redirected stream (optional) if len(sys.argv) < 2: title = "Output stream from unknown source" elif len(sys.argv) < 3: title = "Output stream from %s" % (sys.argv,) else: # Assume it's a least 3. title = "Output stream '%s' from %s" % (sys.argv, sys.argv) self.master.title(title) self.pack(fill=BOTH, expand=YES) font = tkFont.Font(family='Courier', size=font_size) width = font.measure(' ' * (cols+1)) height = font.metrics('linespace') * (rows+1) self.configure(width=width, height=height) self.pack_propagate(0) # Force frame to be configured size. self.logwidget = Text(self, font=font) self.logwidget.pack(side=TOP, fill=BOTH, expand=YES) # Disallow key entry, but allow text copying with <Control-c> self.logwidget.bind('<Key>', lambda x: 'break') self.logwidget.bind('<Control-c>', lambda x: None) self.logwidget.configure(foreground=text_color) self.logwidget.insert(END, '==== Start of Output Stream ====nn') self.logwidget.see(END) self.after(200, self.start_thread, ()) # Start polling thread. def start_thread(self, _): thread.start_new_thread(read_stdin, (self,)) self.after(200, self.check_q, ()) def check_q(self, _): log = self.logwidget log_insert = log.insert log_see = log.see queue_get_nowait = queue.get_nowait go = True while go: try: data = queue_get_nowait().decode() # Must decode for Python 3. if not data: data = '[EOF]' go = False log_insert(END, data) log_see(END) except Q_EMPTY: self.after(200, self.check_q, ()) go = False app = Application() app.mainloop() else: # when module is first imported import traceback class OutputPipe(object): def __init__(self, name=''): self.lock = thread.allocate_lock() self.name = name def flush(self): # NO-OP. pass def __getattr__(self, attr): if attr == 'pipe': # Attribute doesn't exist, so create it. # Launch this module as a separate process to display any output # it receives. # Note: It's important to put double quotes around everything just in # case any have embedded space characters. command = '"%s" "%s" "%s" "%s"' % (sys.executable, # executable __file__, # argv os.path.basename(sys.argv), # argv self.name) # argv # # Typical command and arg values on receiving end: # C:Python3python[w].exe # executable # C:volsFilesPythonLibStack Overflowerrorwindow3k.py # argv # errorwindow3k_test.py # argv # stderr # argv # Execute this script directly as __main__ with a stdin PIPE for sending # output to it. try: # Had to also make stdout and stderr PIPEs too, to work with pythonw.exe self.pipe = subprocess.Popen(command, bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdin except Exception: # Output exception info to a file since this module isn't working. exc_type, exc_value, exc_traceback = sys.exc_info() msg = ('%r exception in %sn' % (exc_type.__name__, os.path.basename(__file__))) with open(EXC_INFO_FILENAME, 'wt') as info: info.write('fatal error occurred spawning output process') info.write('exeception info:' + msg) traceback.print_exc(file=info) sys.exit('fatal error occurred') return super(OutputPipe, self).__getattribute__(attr) def write(self, data): with self.lock: data = data.encode() # Must encode for Python 3. self.pipe.write(data) # First reference to pipe attr will cause an # OutputPipe process for the stream to be created. # Clean-up any left-over debugging files. try: os.remove(DEBUG_FILENAME) # Delete previous file, if any. except Exception: pass try: os.remove(EXC_INFO_FILENAME) # Delete previous file, if any. except Exception: pass # Redirect standard output streams in the process that imported this module. sys.stderr = OutputPipe('stderr') sys.stdout = OutputPipe('stdout')
If you have any questions about how it works, feel free to ask in the comments.