Embedded Python Shell
We said the GUI development process is opaque and we probably need to explain that one first. What we mean is simply that it can be difficult to figure out what is going on inside the various widgets that make up a graphical interface. Python has wonderful runtime introspection capabilities and for most non-GUI applications it is a relatively simple matter to load modules into the Python shell, type a few commands, and look at the resulting state of any object. But the event-driven nature of GUI applications makes it difficult to do this, resulting in objects and actions that are opaque. We thought it would be nice if you could inspect the state of any graphical object just as easily. We thought it would be helpful to be able to manipulate GUI components from a Python shell, while the application was running. Those abilities were among the first features added to PythonCard, in the form of a graphical Python shell called PyCrust.
Because PyCrust is written in Python using the wxPython toolkit, it can be embedded in any wxPython application without conflicting with the application's event loop. PyCrust's modular design allows you to embed a Python command shell and/or a namespace viewer and expose any part of your wxPython application. A minimal application that includes the PyCrust shell looks like this:
from wxPython.wx import * from PyCrust.shell import Shell class ShellFrame(wxFrame): def __init__(self, parent=None, id=-1, title='PyCrust Minimus'): wxFrame.__init__(self, parent, id, title) self.shell = Shell(parent=self) class App(wxApp): def OnInit(self): self.frame = ShellFrame() self.frame.Show(true) self.SetTopWindow(self.frame) return true def main(): application = App(0) application.MainLoop() if __name__ == '__main__': main()
PythonCard takes this one step further by automatically including the PyCrust shell and namespace viewer in every PythonCard application, along with an event message watcher, logger, and property editor. Users of the application built with PythonCard won't normally see these runtime tools, but a standard set of command-line options exposes one or all of them. They are particularly handy during development and debugging. Together they provide a level of transparency that is all too often missing from GUI development environments. For example, this figure shows the Counter example with all of the runtime tools loaded:
The code that makes the PyCrust shell available to the application (and vice versa) is as simple as this:
class PyCrustFrame(MyMiniFrame): def __init__(self, parent, ID, title, pos, size, parentApp): MyMiniFrame.__init__(self, parent, ID, title, pos, size) parentApp.shell = PyCrust.shell.Shell(self, -1) parentApp.shell.interp.locals['pcapp'] = parentApp self.parentApp = parentApp wx.EVT_CLOSE(self, self.onCloseMe) def onCloseMe(self, evt): self.Show(0)
The shell frame is created as a wxPython MiniFrame. The PyCrust shell is added to that frame. The application itself is added to the shell's local namespace as "pcapp". As you can see in the figure above, from this we now have access to the application, the window, the menu and buttons on the window, the attributes and methods of the menus and buttons, and so forth, while the application is running. This gives us the ability to run nearly any Python command in the PyCrust Python shell.