Problems with wx.Process & wx.Execute

John Jackson jjackson at pobox.com
Tue Mar 11 12:08:40 PDT 2008


I've been trying to use wx.Process to launch a Django from a wxPython  
application with no luck.

What I see in my log are messages such as

11:36:46: Starting server with command:
/Library/Frameworks/Python.framework/Versions/Current/bin/python ~/ 
Source/Mine/DjangoTest/mysite/manage.py runserver
11:36:46: Debug: wxMacExecute Bad bundle: /Library/Frameworks/ 
Python.framework/Versions/Current/bin/python
11:36:46: Debug: pid=22933
11:36:46: Debug: Successfully added notification to the runloop
11:36:46: DjangoStarter: serverPID "22933" started.
11:36:46: Debug: Process ended

I'm also sometimes seeing GetOutputStream() returning a stream, and  
sometimes not, even when Exists() is False.

Googling "wxMacExecute Bad bundle" returns some hits, but nothing  
that helped. There was a request by Robin for a sample, application,  
but none seemed to be offered.

So, see below for a sample application. You'll need to have Django  
installed, and then either modify the command to point to a site, or  
use the "Set Server Command" button to do the same.

I'm going to try this under Windows and see if the same results happen.

#
#  djangoStarter.py
#  DjangoRunner
#
#  Created by John Jackson on 3/10/08.
#  version 1.0

import wx

APP_TITLE = "DjangoStarter"
STARTED = {True: "started", False: "stopped"}
START_STOP_TEXT = {False: "Start Django Server",
                    True: "Stop Django Server"}
SPACING = 5
DOUBLE_SPACING = 2 * SPACING
QUAD_SPACING = 4 * SPACING

# Note: change this command based on your installation...
SERVER_COMMAND = r"/Library/Frameworks/Python.framework/Versions/ 
Current/bin/python "\
     + "~/Source/Mine/DjangoTest/mysite/manage.py runserver"


class DjangoStarter(wx.Frame):
     def __init__(self, parent=None, ID=-1, title=APP_TITLE,  
controller=None):
         wx.Frame.__init__(self, parent, ID, title, size=(600,400))
         self.controller = controller
         self.BuildLogView()
         self.BuildButtons()
         self.BuildSizers()
         # These bindings throw you into the debugger...
#        self.Bind(wx.EVT_IDLE, self.OnIdle)
#        self.Bind(wx.EVT_END_PROCESS, self.OnIdle)

     def BuildLogView(self):
         # Set up the log window
         self.logTextCtrl = wx.TextCtrl(self, -1,
                               style = wx.TE_MULTILINE|wx.TE_READONLY)
         if wx.Platform == "__WXMAC__":
             self.logTextCtrl.MacCheckSpelling(False)
         wx.Log_SetActiveTarget(wx.LogTextCtrl(self.logTextCtrl))

     def BuildButtons(self):
         self.setCommandButton = wx.Button(self, label="Set Server  
Command")
         self.Bind(wx.EVT_BUTTON, self.OnSetPath, self.setCommandButton)
         self.startStopButton = wx.Button(self)
         self.Bind(wx.EVT_BUTTON, self.OnStartStop,  
self.startStopButton)
         self.SetStartStopButtonLabel()

     def BuildSizers(self):
         self.buttonsSizer = wx.BoxSizer(wx.HORIZONTAL)
         self.buttonsSizer.Add(self.setCommandButton, 0,
             wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, SPACING)
         self.buttonsSizer.Add((SPACING, DOUBLE_SPACING), 1, wx.EXPAND)
         self.buttonsSizer.Add(self.startStopButton, 0,
             wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, SPACING)
         self.buttonsSizer.Add((SPACING, DOUBLE_SPACING), 0, wx.EXPAND)

         self.contentSizer = wx.BoxSizer(wx.VERTICAL)
         self.contentSizer.Add(self.logTextCtrl, 1,
             wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP|wx.BOTTOM,  
DOUBLE_SPACING)
         self.contentSizer.Add(self.buttonsSizer, 0,
             wx.EXPAND|wx.TOP|wx.BOTTOM, SPACING)
         self.contentSizer.Add((SPACING, QUAD_SPACING), 0, wx.EXPAND)

         self.SetSizer(self.contentSizer)
         self.SetAutoLayout(True)
         self.Layout()

     def OnSetPath(self, event):
         if self.controller.serverCommand:
             serverCommand = self.controller.serverCommand
         else:
             serverCommand = SERVER_COMMAND
         dlg = wx.TextEntryDialog(self, "Set Django Server Start  
Command",
                                 "Enter a command with a full local  
path",
                                 serverCommand, wx.OK|wx.CANCEL)
         dlg.CentreOnParent()
         if dlg.ShowModal() == wx.ID_OK:
             self.controller.serverCommand = dlg.GetValue()
         dlg.Destroy()

     def OnStartStop(self, event):
         self.controller.start_stop_server()
         self.SetStartStopButtonLabel()

     def OnIdle(self, event):
         self.controller.log_server()

     def SetStartStopButtonLabel(self):
         self.startStopButton.SetLabel(START_STOP_TEXT 
[self.controller.serverStarted])


class Controller(object):
     def __init__(self):
         self.serverStarted = False
         self.serverCommand = SERVER_COMMAND
         self.serverPID = None
         self.serverProcess = None
         # Start application
         self.app = wx.PySimpleApp()
         self.gui = DjangoStarter(controller=self)
         self.gui.Show(1)
         self.app.MainLoop()

     def __del__(self):
         if self.serverProcess is not None:
             self.serverProcess.Detach()
             self.serverProcess.CloseOutput()
             self.serverProcess = None

     def log(self, message):
         # Adds message to the log
         wx.LogMessage(message)
         # Need to yield here so that scrollbar can move.
         wx.Yield()

     def start_stop_server(self, window=None, serverCommand=None):
         if serverCommand:
             self.serverCommand = serverCommand
         if not self.serverStarted:
             self._start_server(window, self.serverCommand)
         else:
             self._stop_server()

     def log_server(self):
         if self.serverProcess:
             self._read_stream(self.serverProcess.IsInputAvailable,
                 self.serverProcess.GetInputStream)
             self._read_stream(self.serverProcess.IsErrorAvailable,
                 self.serverProcess.GetErrorStream)

     def _read_stream(self, testStreamFunction, getStreamFunction):
         if testStreamFunction():
             stream = getStreamFunction()
             if stream.CanRead():
                 text = stream.Read()
             else:
                 text = "Stream cannot be read: %s" % getStreamFunction
         else:
             text = "Stream not available: %s" % testStreamFunction
         self.log(text)

     def _start_server(self, window, serverCommand=None):
         self.log("Starting server with command:\n%s" %  
self.serverCommand)
         self.serverCommand = serverCommand
         self.serverProcess = wx.Process(window)
         self.serverProcess.Redirect();
         self.serverPID = wx.Execute(self.serverCommand,  
wx.EXEC_ASYNC, self.serverProcess)
         self.serverStarted = True
         self._log_server()

     def _stop_server(self):
         self.log('serverPID: %s\n serverStarted: %s\n serverProcess:  
%s' % (self.serverPID,
                 self.serverStarted, self.serverProcess))
         if self.serverProcess:
             exists = self.serverProcess.Exists(self.serverPID)
             self.log('serverProcess.Exists: %s' % exists)
             CTRL_C = 3
             stream = self.serverProcess.GetOutputStream()
             self.log('serverProcess.GetOutputStream: %s' % stream)
             if stream and exists:
                 stream.write(chr(CTRL_C) + '\n')
             self.serverProcess.Detach()
             self.log('ServerProcess detached')
             self.serverProcess.CloseOutput()
             self.log('ServerProcess closed output')
             self.serverProcess = None
         self.serverStarted = False
         self._log_server()
         self.serverPID = 0

     def _log_server(self):
         self.log('%s: serverPID "%s" %s.' % (APP_TITLE,
                 self.serverPID, STARTED[self.serverStarted]))


if __name__ == '__main__':
     Controller()





More information about the wxpython-mac mailing list