Section 11.5. GuiStreams: Redirecting Streams to Widgets


11.5. GuiStreams: Redirecting Streams to Widgets

The script in Example 11-9 arranges to map input and output sources to pop-up windows in a GUI application, much as we did with strings in the stream redirection topics in Chapter 3. Although this module is really just a first-cut prototype and needs improvement itself (e.g., each input line request pops up a new input dialog), it demonstrates the concepts in general.

Its GuiOutput and GuiInput objects define methods that allow them to masquerade as files in any interface that expects a real file. As we learned earlier in Chapter 3, this includes standard stream processing tools, such as print and raw_input, and it includes explicit read and write calls. The two top-level interfaces in this module handle common use cases:

  • The redirectedGuiFunc function uses this plug-and-play file compatibility to run a function with its standard input and output streams mapped completely to pop-up windows rather than to the console window (or wherever streams would otherwise be mapped in the system shell).

  • The redirectedGuiShellCmd function similarly routes the output of a spawned shell command line to a pop-up window. It can be used to display the output of any program in a GUIincluding that printed by a Python program.

The module's GuiInput and GuiOutput classes can also be used or customized directly by clients that need more fine-grained control over the process.

Example 11-9. PP3E\Gui\Tools\guiStreams.py

 ############################################################################## # first-cut implementation of file-like classes that can be used to redirect # input and output streams to GUI displays; as is, input comes from a common # dialog pop-up (a single output+input interface or a persistent Entry field # for input would be better); this also does not properly span lines for read # requests with a byte count > len(line); see guiStreamsTools.py for more; ############################################################################## from Tkinter import * from ScrolledText import ScrolledText from tkSimpleDialog import askstring class GuiOutput:     font = ('courier', 9, 'normal')              # in class for all, self for one     def _ _init_ _(self, parent=None):         self.text = None         if parent: self.popupnow(parent)         # pop up now or on first write     def popupnow(self, parent=None):             # in parent now, Toplevel later         if self.text: return         self.text = ScrolledText(parent or Toplevel( ))         self.text.config(font=self.font)         self.text.pack( )     def write(self, text):         self.popupnow( )         self.text.insert(END, str(text))         self.text.see(END)         self.text.update( )     def writelines(self, lines):                 # lines already have '\n'         for line in lines: self.write(line)      # or map(self.write, lines) class GuiInput:     def _ _init_ _(self):         self.buff = ''     def inputLine(self):         line = askstring('GuiInput', 'Enter input line + <crlf> (cancel=eof)')         if line == None:             return ''                            # pop-up dialog for each line         else:                                    # cancel button means eof             return line + '\n'                   # else add end-line marker     def read(self, bytes=None):         if not self.buff:             self.buff = self.inputLine( )         if bytes:                                # read by byte count             text = self.buff[:bytes]             # doesn't span lines             self.buff = self.buff[bytes:]         else:             text = ''                            # read all till eof             line = self.buff             while line:                 text = text + line                 line = self.inputLine( )          # until cancel=eof=''         return text     def readline(self):         text = self.buff or self.inputLine( )     # emulate file read methods         self.buff = ''         return text     def readlines(self):         lines = []                               # read all lines         while 1:             next = self.readline( )             if not next: break             lines.append(next)         return lines def redirectedGuiFunc(func, *pargs, **kargs):     import sys     saveStreams = sys.stdin, sys.stdout          # map func streams to pop ups     sys.stdin   = GuiInput( )                        # pops up dialog as needed     sys.stdout  = GuiOutput( )                      # new output window per call     sys.stderr  = sys.stdout     result = func(*pargs, **kargs)               # this is a blocking call     sys.stdin, sys.stdout = saveStreams     return result def redirectedGuiShellCmd(command):     import os     input  = os.popen(command, 'r')     output = GuiOutput( )     def reader(input, output):                   # show a shell command's         while True:                              # standard output in a new             line = input.readline( )                 # pop-up text box widget;             if not line: break                   # the readline call may block             output.write(line)     reader(input, output) if _ _name_ _ == '_ _main_ _':     def makeUpper( ):                             # use standard streams         while 1:             try:                 line = raw_input('Line? ')             except:                 break             print line.upper( )         print 'end of file'     def makeLower(input, output):                # use explicit files         while 1:             line = input.readline( )             if not line: break             output.write(line.lower( ))         print 'end of file'     root = Tk( )     Button(root, text='test streams',            command=lambda: redirectedGuiFunc(makeUpper)).pack(fill=X)     Button(root, text='test files  ',            command=lambda: makeLower(GuiInput(), GuiOutput( )) ).pack(fill=X)     Button(root, text='test popen  ',            command=lambda: redirectedGuiShellCmd('dir *')).pack(fill=X)     root.mainloop( ) 

As coded here, GuiOutput either attaches a ScrolledText to a parent container or pops up a new top-level window to serve as the container on the first write call. GuiInput pops up a new standard input dialog every time a read request requires a new line of input. Neither one of these policies is ideal for all scenarios (input would be better mapped to a more long-lived widget), but they prove the general point. Figure 11-8 shows the scene generated by this script's self-test code, after capturing the output of a shell dir listing command (on the left) and two interactive loop tests (the one with "Line?" prompts and uppercase letters represents the makeUpper streams test). An input dialog has just popped up for a new makeLower files test.

Figure 11-8. guiStreams routing streams to pop-up windows


Before we move on, we should note that this module's calls to a redirected function as well as its loop that reads from a spawned shell command are potentially blockingthey won't return to the GUI's event loop until the function or shell command exits. In redirectedGuiShellCmd, for example, the call to input.readline will pause until input is received from the spawned program, rendering the GUI unresponsive. Because the output object runs an update call, the display is still updated during the pause (an update call enters the Tk event loop momentarily). This blocking model is simplistic, though, and might be an issue in a larger GUI. We'll revisit this later in the chapter when we meet threads. For now, the code suits our present purpose.

11.5.1. Using Redirection for the Packing Scripts

Now, to use such redirection tools to map command-line script output back to a GUI, we simply run calls and command lines with the two redirected functions in this module. Example 11-10 shows one way to wrap the packing operation to force its printed output to appear in a pop-up window when generated, instead of in the console.

Example 11-10. PP3E\Gui\ShellGui\packdlg-redirect.py

 # wrap command-line script in GUI redirection tool to pop p its output from Tkinter import * from packdlg import runPackDialog from PP3E.Gui.Tools.guiStreams import redirectedGuiFunc def runPackDialog_Wrapped( ):     redirectedGuiFunc(runPackDialog)    # wrap entire callback handler if _ _name_ _ == '_ _main_ _':     root = Tk( )     Button(root, text='pop', command=runPackDialog_Wrapped).pack(fill=X)     root.mainloop( ) 

You can run this script directly to test its effect, without bringing up the ShellGui window. Figure 11-9 shows the resulting stdout window after the pack input dialog is dismissed. This window pops up as soon as script output is generated, and it is a bit more GUI user friendly than hunting for messages in a console. You can similarly code the unpack parameters dialog to route its output to a pop-up.[*] In fact, you can use this technique to route the output of any function call or command line to a pop-up window; as usual, the notion of compatible object interfaces is at the heart of much of Python code's flexibility.

[*] These two scripts are something of a unique case; because the App superclass they employ saves away standard streams in its own attributes at object creation time, you must kick off the GUI redirection wrapper calls as soon as possible so that App finds the redirected GUI streams in sys when saving them locally. Most other scripts aren't quite as tricky when it comes to internal stream redirections. Trace through the code to see what I mean.

Figure 11-9. Routing script outputs to GUI pop ups





Programming Python
Programming Python
ISBN: 0596009259
EAN: 2147483647
Year: 2004
Pages: 270
Authors: Mark Lutz

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net