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