Introduction

Some MS Windows applications are available only as console programs. In some cases, it is desirable to be able to display output from such applications in a wxWindow, for review by the user.

What Objects are Involved

As you will see, this recipe involves little in the way of wxPython coding. Rather this recipe presents an adaptation of the usual recipes for connecting to the input and output handles of a running process, so that the output can be sent to a wxWindow. (I adapted the main specifics of this recipe from the win32 distribution of Python and some suggested code for Twisted, from a developers' list.)

Two scripts are presented below. The one that involves wxPython merely illustrates how the other one is used.

Process Overview

The console program is run in the usual way. However, the output is processed in a separate thread, so that wxPython need not wait for the program to terminate before displaying its output in the wxWindow.

Special Concerns

This recipe offers only the bare bones, in the interests of simplicity. No provision is made for processing stderr (although that would appear to be easy to do), nor for stdin.

Code Sample

   1 from wxPython.wx import *
   2  
   3 # wxPython illustrative driver
   4 
   5 from runproc import Process
   6 
   7 if __name__ == "__main__" :
   8 
   9     class MainFrame ( wxFrame ) :
  10         def __init__ ( self, * args ) :
  11             
  12             wxFrame . __init__ ( self, * args )
  13             
  14             tw = wxTextCtrl ( self, -1, style = wxTE_MULTILINE )
  15             tw . SetForegroundColour ( wxGREEN )
  16             tw . SetBackgroundColour ( wxBLACK )
  17             tw . SetFont ( wxFont ( 12, wxSWISS, wxNORMAL, wxBOLD ) )
  18             
  19             p = Process ( tw . WriteText )
  20             cmd = 'python printlines.py'
  21             p . run ( cmd )
  22             
  23     app = wxPySimpleApp ( )
  24     frame = MainFrame ( None, wxNewId ( ), 'Trial run' )
  25     frame . Show ( )
  26 
  27     app . MainLoop ( )
  28 
  29 # module to provide threaded access 
  30 #
  31 # driver above expects this module to be called 'runproc.py' (say) 
  32 # and available on the Python path
  33 
  34 import win32api
  35 import win32pipe
  36 import win32file
  37 import win32process
  38 import win32security
  39 import win32con
  40 import msvcrt
  41 import os
  42 import threading
  43 
  44 class Process:
  45     
  46     def __init__ ( self, outputLineCallback ) : 
  47         
  48         self . outputLineCallback = outputLineCallback
  49     
  50     def run ( self, cmdline ) :
  51 
  52         sAttrs = win32security . SECURITY_ATTRIBUTES ( )
  53         sAttrs . bInheritHandle = 1
  54 
  55         hStdin_r,  self . hStdin_w  = win32pipe . CreatePipe ( sAttrs, 0 )
  56         self . hStdout_r, hStdout_w = win32pipe . CreatePipe ( sAttrs, 0 )
  57         self . hStderr_r, hStderr_w = win32pipe . CreatePipe ( sAttrs, 0 )
  58         
  59         pid = win32api . GetCurrentProcess ( )
  60         
  61         def ReplaceHandle ( handle ) :
  62             tmp = win32api . DuplicateHandle ( pid, handle, pid, 0, 0, win32con . DUPLICATE_SAME_ACCESS )
  63             win32file . CloseHandle ( handle )
  64             return tmp
  65             
  66         self . hStdin_w = ReplaceHandle ( self . hStdin_w )
  67         self . hStdout_r = ReplaceHandle ( self . hStdout_r )
  68         self . hStderr_r = ReplaceHandle ( self . hStderr_r )
  69         
  70         StartupInfo = win32process . STARTUPINFO ( )
  71         StartupInfo . hStdInput  = hStdin_r
  72         StartupInfo . hStdOutput = hStdout_w
  73         StartupInfo . hStdError  = hStderr_w
  74         StartupInfo . dwFlags = win32process . STARTF_USESTDHANDLES
  75         # StartupInfo . dwFlags = StartupInfo . dwFlags | win32process . STARTF_USESHOWWINDOW
  76         # StartupInfo . wShowWindow = win32con . SW_HIDE
  77         
  78         hProcess, hThread, dwPid, dwTid = win32process . CreateProcess \
  79                  ( None, cmdline, None, None, 1, 0, None, None, StartupInfo )
  80 
  81         win32file . CloseHandle ( hStderr_w )
  82         win32file . CloseHandle ( hStdout_w )
  83         win32file . CloseHandle ( hStdin_r )
  84 
  85         #~ self.stdin = os.fdopen(msvcrt.open_osfhandle(self.hStdin_w, 0), "wb")
  86         self.stdout = os.fdopen(msvcrt.open_osfhandle(self.hStdout_r, 0), "rb")
  87         #~ self.stderr = os.fdopen(msvcrt.open_osfhandle(self.hStderr_r, 0), "rb")
  88         
  89         threading . Thread ( target = self . doReadOut ) . start ( )
  90         
  91     def doReadOut ( self ) :
  92         
  93         while 1:
  94             line = self . stdout . readline ( )
  95             if line :
  96                 self . outputLineCallback ( line [ : -1 ] )
  97             else :
  98                 break
  99     
 100 if __name__ == '__main__':
 101     
 102     def callback ( line ) :
 103         
 104         print line 
 105     
 106     p = Process ( callback ) 
 107     exe = win32api . GetModuleFileName ( 0 )
 108     p . run ( exe + ' printlines.py' )

Comments

I welcome your comments.- Bill Bell

Why not use wx.Process and wx.Execute? You get asynchronous process handling in a single thread that way. Alternatively, you can use the built in subprocess module (in Python 2.4 and later). These options are cross-platform, which your recipe is not. You should also correct your over-use of spaces, which violate all sorts of Python and wxPython style guides. - Josiah Carlson

Capturing DOS Output in a wxWindow (last edited 2008-03-11 10:50:23 by localhost)

NOTE: To edit pages in this wiki you must be a member of the TrustedEditorsGroup.