1 """Crust combines the shell and filling into one control.""" 
   3 __author__ 
= "Patrick K. O'Brien <pobrien@orbtech.com>" 
   5 __revision__ 
= "$Revision$"[11:-2] 
  16 from filling 
import Filling
 
  18 from shell 
import Shell
 
  19 from version 
import VERSION
 
  22 class Crust(wx
.SplitterWindow
): 
  23     """Crust based on SplitterWindow.""" 
  26     revision 
= __revision__
 
  29     def __init__(self
, parent
, id=-1, pos
=wx
.DefaultPosition
, 
  30                  size
=wx
.DefaultSize
, style
=wx
.SP_3D|wx
.SP_LIVE_UPDATE
, 
  31                  name
='Crust Window', rootObject
=None, rootLabel
=None, 
  32                  rootIsNamespace
=True, intro
='', locals=None, 
  34                  startupScript
=None, execStartupScript
=True, 
  36         """Create Crust instance.""" 
  37         wx
.SplitterWindow
.__init
__(self
, parent
, id, pos
, size
, style
, name
) 
  39         # Turn off the tab-traversal style that is automatically 
  40         # turned on by wx.SplitterWindow.  We do this because on 
  41         # Windows the event for Ctrl-Enter is stolen and used as a 
  42         # navigation key, but the Shell window uses it to insert lines. 
  43         style 
= self
.GetWindowStyle() 
  44         self
.SetWindowStyle(style 
& ~wx
.TAB_TRAVERSAL
) 
  46         self
.shell 
= Shell(parent
=self
, introText
=intro
, 
  47                            locals=locals, InterpClass
=InterpClass
, 
  48                            startupScript
=startupScript
, 
  49                            execStartupScript
=execStartupScript
, 
  51         self
.editor 
= self
.shell
 
  52         if rootObject 
is None: 
  53             rootObject 
= self
.shell
.interp
.locals 
  54         self
.notebook 
= wx
.Notebook(parent
=self
, id=-1) 
  55         self
.shell
.interp
.locals['notebook'] = self
.notebook
 
  56         self
.filling 
= Filling(parent
=self
.notebook
, 
  57                                rootObject
=rootObject
, 
  59                                rootIsNamespace
=rootIsNamespace
) 
  60         # Add 'filling' to the interpreter's locals. 
  61         self
.shell
.interp
.locals['filling'] = self
.filling
 
  62         self
.notebook
.AddPage(page
=self
.filling
, text
='Namespace', select
=True) 
  64         self
.display 
= Display(parent
=self
.notebook
) 
  65         self
.notebook
.AddPage(page
=self
.display
, text
='Display') 
  66         # Add 'pp' (pretty print) to the interpreter's locals. 
  67         self
.shell
.interp
.locals['pp'] = self
.display
.setItem
 
  68         self
.display
.nbTab 
= self
.notebook
.GetPageCount()-1 
  70         self
.calltip 
= Calltip(parent
=self
.notebook
) 
  71         self
.notebook
.AddPage(page
=self
.calltip
, text
='Calltip') 
  73         self
.sessionlisting 
= SessionListing(parent
=self
.notebook
) 
  74         self
.notebook
.AddPage(page
=self
.sessionlisting
, text
='History') 
  76         self
.dispatcherlisting 
= DispatcherListing(parent
=self
.notebook
) 
  77         self
.notebook
.AddPage(page
=self
.dispatcherlisting
, text
='Dispatcher') 
  80         # Initialize in an unsplit mode, and check later after loading 
  81         # settings if we should split or not. 
  84         self
.Initialize(self
.shell
) 
  85         self
._shouldsplit 
= True 
  86         wx
.CallAfter(self
._CheckShouldSplit
) 
  87         self
.SetMinimumPaneSize(100) 
  89         self
.Bind(wx
.EVT_SIZE
, self
.SplitterOnSize
) 
  90         self
.Bind(wx
.EVT_SPLITTER_SASH_POS_CHANGED
, self
.OnChanged
) 
  91         self
.Bind(wx
.EVT_SPLITTER_DCLICK
, self
.OnSashDClick
) 
  93     def _CheckShouldSplit(self
): 
  95             self
.SplitHorizontally(self
.shell
, self
.notebook
, -self
.sashoffset
) 
  96             self
.lastsashpos 
= self
.GetSashPosition() 
  99         self
.issplit 
= self
.IsSplit()        
 101     def ToggleTools(self
): 
 102         """Toggle the display of the filling and other tools""" 
 106             self
.SplitHorizontally(self
.shell
, self
.notebook
, -self
.sashoffset
) 
 107             self
.lastsashpos 
= self
.GetSashPosition() 
 108         self
.issplit 
= self
.IsSplit() 
 110     def ToolsShown(self
): 
 113     def OnChanged(self
, event
): 
 114         """update sash offset from the bottom of the window""" 
 115         self
.sashoffset 
= self
.GetSize().height 
- event
.GetSashPosition() 
 116         self
.lastsashpos 
= event
.GetSashPosition() 
 119     def OnSashDClick(self
, event
): 
 123     # Make the splitter expand the top window when resized 
 124     def SplitterOnSize(self
, event
): 
 125         splitter 
= event
.GetEventObject() 
 126         sz 
= splitter
.GetSize() 
 127         splitter
.SetSashPosition(sz
.height 
- self
.sashoffset
, True) 
 131     def LoadSettings(self
, config
): 
 132         self
.shell
.LoadSettings(config
) 
 133         self
.filling
.LoadSettings(config
) 
 135         pos 
= config
.ReadInt('Sash/CrustPos', 400) 
 136         wx
.CallAfter(self
.SetSashPosition
, pos
) 
 137         def _updateSashPosValue(): 
 139             self
.sashoffset 
= sz
.height 
- self
.GetSashPosition() 
 140         wx
.CallAfter(_updateSashPosValue
) 
 141         zoom 
= config
.ReadInt('View/Zoom/Display', -99) 
 143             self
.display
.SetZoom(zoom
) 
 144         self
.issplit 
= config
.ReadInt('Sash/IsSplit', True) 
 146             self
._shouldsplit 
= False 
 148     def SaveSettings(self
, config
): 
 149         self
.shell
.SaveSettings(config
) 
 150         self
.filling
.SaveSettings(config
) 
 152         if self
.lastsashpos 
!= -1: 
 153             config
.WriteInt('Sash/CrustPos', self
.lastsashpos
) 
 154         config
.WriteInt('Sash/IsSplit', self
.issplit
) 
 155         config
.WriteInt('View/Zoom/Display', self
.display
.GetZoom()) 
 161 class Display(editwindow
.EditWindow
): 
 162     """STC used to display an object using Pretty Print.""" 
 164     def __init__(self
, parent
, id=-1, pos
=wx
.DefaultPosition
, 
 166                  style
=wx
.CLIP_CHILDREN | wx
.SUNKEN_BORDER
, 
 168         """Create Display instance.""" 
 169         editwindow
.EditWindow
.__init
__(self
, parent
, id, pos
, size
, style
) 
 170         # Configure various defaults and user preferences. 
 171         self
.SetReadOnly(True) 
 172         self
.SetWrapMode(False) 
 174             dispatcher
.connect(receiver
=self
.push
, signal
='Interpreter.push') 
 176     def push(self
, command
, more
): 
 177         """Receiver for Interpreter.push signal.""" 
 181         if not hasattr(self
, "item"): 
 183         self
.SetReadOnly(False) 
 184         text 
= pprint
.pformat(self
.item
) 
 186         self
.SetReadOnly(True) 
 188     def setItem(self
, item
): 
 189         """Set item to pretty print in the notebook Display tab.""" 
 192         if self
.GetParent().GetSelection() != self
.nbTab
: 
 193             focus 
= wx
.Window
.FindFocus() 
 194             self
.GetParent().SetSelection(self
.nbTab
) 
 195             wx
.CallAfter(focus
.SetFocus
) 
 198 # TODO: Switch this to a editwindow.EditWindow 
 199 class Calltip(wx
.TextCtrl
): 
 200     """Text control containing the most recent shell calltip.""" 
 202     def __init__(self
, parent
=None, id=-1): 
 203         style 
= (wx
.TE_MULTILINE | wx
.TE_READONLY | wx
.TE_RICH2
) 
 204         wx
.TextCtrl
.__init
__(self
, parent
, id, style
=style
) 
 205         self
.SetBackgroundColour(wx
.Colour(255, 255, 208)) 
 206         dispatcher
.connect(receiver
=self
.display
, signal
='Shell.calltip') 
 209         font 
= wx
.Font(df
.GetPointSize(), wx
.TELETYPE
, wx
.NORMAL
, wx
.NORMAL
) 
 212     def display(self
, calltip
): 
 213         """Receiver for Shell.calltip signal.""" 
 214         ## self.SetValue(calltip)  # Caused refresh problem on Windows. 
 216         self
.AppendText(calltip
) 
 219 # TODO: Switch this to a editwindow.EditWindow 
 220 class SessionListing(wx
.TextCtrl
): 
 221     """Text control containing all commands for session.""" 
 223     def __init__(self
, parent
=None, id=-1): 
 224         style 
= (wx
.TE_MULTILINE | wx
.TE_READONLY |
 
 225                  wx
.TE_RICH2 | wx
.TE_DONTWRAP
) 
 226         wx
.TextCtrl
.__init
__(self
, parent
, id, style
=style
) 
 227         dispatcher
.connect(receiver
=self
.addHistory
, signal
="Shell.addHistory") 
 228         dispatcher
.connect(receiver
=self
.clearHistory
, signal
="Shell.clearHistory") 
 229         dispatcher
.connect(receiver
=self
.loadHistory
, signal
="Shell.loadHistory") 
 232         font 
= wx
.Font(df
.GetPointSize(), wx
.TELETYPE
, wx
.NORMAL
, wx
.NORMAL
) 
 235     def loadHistory(self
, history
): 
 236         # preload the existing history, if any 
 239         self
.SetValue('\n'.join(hist
) + '\n') 
 240         self
.SetInsertionPointEnd() 
 242     def addHistory(self
, command
): 
 244             self
.SetInsertionPointEnd() 
 245             self
.AppendText(command 
+ '\n') 
 247     def clearHistory(self
): 
 251 class DispatcherListing(wx
.TextCtrl
): 
 252     """Text control containing all dispatches for session.""" 
 254     def __init__(self
, parent
=None, id=-1): 
 255         style 
= (wx
.TE_MULTILINE | wx
.TE_READONLY |
 
 256                  wx
.TE_RICH2 | wx
.TE_DONTWRAP
) 
 257         wx
.TextCtrl
.__init
__(self
, parent
, id, style
=style
) 
 258         dispatcher
.connect(receiver
=self
.spy
) 
 261         font 
= wx
.Font(df
.GetPointSize(), wx
.TELETYPE
, wx
.NORMAL
, wx
.NORMAL
) 
 264     def spy(self
, signal
, sender
): 
 265         """Receiver for Any signal from Any sender.""" 
 266         text 
= '%r from %s' % (signal
, sender
) 
 267         self
.SetInsertionPointEnd() 
 268         start
, end 
= self
.GetSelection() 
 270             self
.SetSelection(0, 0) 
 271         self
.AppendText(text 
+ '\n') 
 275 class CrustFrame(frame
.Frame
, frame
.ShellFrameMixin
): 
 276     """Frame containing all the PyCrust components.""" 
 279     revision 
= __revision__
 
 282     def __init__(self
, parent
=None, id=-1, title
='PyCrust', 
 283                  pos
=wx
.DefaultPosition
, size
=wx
.DefaultSize
, 
 284                  style
=wx
.DEFAULT_FRAME_STYLE
, 
 285                  rootObject
=None, rootLabel
=None, rootIsNamespace
=True, 
 286                  locals=None, InterpClass
=None, 
 287                  config
=None, dataDir
=None, 
 289         """Create CrustFrame instance.""" 
 290         frame
.Frame
.__init
__(self
, parent
, id, title
, pos
, size
, style
) 
 291         frame
.ShellFrameMixin
.__init
__(self
, config
, dataDir
) 
 293         if size 
== wx
.DefaultSize
: 
 294             self
.SetSize((800, 600)) 
 296         intro 
= 'PyCrust %s - The Flakiest Python Shell' % VERSION
 
 297         self
.SetStatusText(intro
.replace('\n', ', ')) 
 298         self
.crust 
= Crust(parent
=self
, intro
=intro
, 
 299                            rootObject
=rootObject
, 
 301                            rootIsNamespace
=rootIsNamespace
, 
 303                            InterpClass
=InterpClass
, 
 304                            startupScript
=self
.startupScript
, 
 305                            execStartupScript
=self
.execStartupScript
, 
 307         self
.shell 
= self
.crust
.shell
 
 309         # Override the filling so that status messages go to the status bar. 
 310         self
.crust
.filling
.tree
.setStatusText 
= self
.SetStatusText
 
 312         # Override the shell so that status messages go to the status bar. 
 313         self
.shell
.setStatusText 
= self
.SetStatusText
 
 315         self
.shell
.SetFocus() 
 319     def OnClose(self
, event
): 
 320         """Event handler for closing.""" 
 322         self
.crust
.shell
.destroy() 
 326     def OnAbout(self
, event
): 
 327         """Display an About window.""" 
 328         title 
= 'About PyCrust' 
 329         text 
= 'PyCrust %s\n\n' % VERSION 
+ \
 
 330                'Yet another Python shell, only flakier.\n\n' + \
 
 331                'Half-baked by Patrick K. O\'Brien,\n' + \
 
 332                'the other half is still in the oven.\n\n' + \
 
 333                'Shell Revision: %s\n' % self
.shell
.revision 
+ \
 
 334                'Interpreter Revision: %s\n\n' % self
.shell
.interp
.revision 
+ \
 
 335                'Platform: %s\n' % sys
.platform 
+ \
 
 336                'Python Version: %s\n' % sys
.version
.split()[0] + \
 
 337                'wxPython Version: %s\n' % wx
.VERSION_STRING 
+ \
 
 338                ('\t(%s)\n' % ", ".join(wx
.PlatformInfo
[1:])) 
 339         dialog 
= wx
.MessageDialog(self
, text
, title
, 
 340                                   wx
.OK | wx
.ICON_INFORMATION
) 
 345     def ToggleTools(self
): 
 346         """Toggle the display of the filling and other tools""" 
 347         return self
.crust
.ToggleTools() 
 349     def ToolsShown(self
): 
 350         return self
.crust
.ToolsShown() 
 352     def OnHelp(self
, event
): 
 353         """Show a help dialog.""" 
 354         frame
.ShellFrameMixin
.OnHelp(self
, event
) 
 357     def LoadSettings(self
): 
 358         if self
.config 
is not None: 
 359             frame
.ShellFrameMixin
.LoadSettings(self
) 
 360             frame
.Frame
.LoadSettings(self
, self
.config
) 
 361             self
.crust
.LoadSettings(self
.config
) 
 364     def SaveSettings(self
, force
=False): 
 365         if self
.config 
is not None: 
 366             frame
.ShellFrameMixin
.SaveSettings(self
) 
 367             if self
.autoSaveSettings 
or force
: 
 368                 frame
.Frame
.SaveSettings(self
, self
.config
) 
 369                 self
.crust
.SaveSettings(self
.config
) 
 372     def DoSaveSettings(self
): 
 373         if self
.config 
is not None: 
 374             self
.SaveSettings(force
=True)