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)
38 self.shell = Shell(parent=self, introText=intro,
39 locals=locals, InterpClass=InterpClass,
40 startupScript=startupScript,
41 execStartupScript=execStartupScript,
43 self.editor = self.shell
44 if rootObject is None:
45 rootObject = self.shell.interp.locals
46 self.notebook = wx.Notebook(parent=self, id=-1)
47 self.shell.interp.locals['notebook'] = self.notebook
48 self.filling = Filling(parent=self.notebook,
49 rootObject=rootObject,
51 rootIsNamespace=rootIsNamespace)
52 # Add 'filling' to the interpreter's locals.
53 self.shell.interp.locals['filling'] = self.filling
54 self.notebook.AddPage(page=self.filling, text='Namespace', select=True)
56 self.display = Display(parent=self.notebook)
57 self.notebook.AddPage(page=self.display, text='Display')
58 # Add 'pp' (pretty print) to the interpreter's locals.
59 self.shell.interp.locals['pp'] = self.display.setItem
60 self.display.nbTab = self.notebook.GetPageCount()-1
62 self.calltip = Calltip(parent=self.notebook)
63 self.notebook.AddPage(page=self.calltip, text='Calltip')
65 self.sessionlisting = SessionListing(parent=self.notebook)
66 self.notebook.AddPage(page=self.sessionlisting, text='History')
68 self.dispatcherlisting = DispatcherListing(parent=self.notebook)
69 self.notebook.AddPage(page=self.dispatcherlisting, text='Dispatcher')
71 self.SplitHorizontally(self.shell, self.notebook, -self.sashoffset)
72 self.SetMinimumPaneSize(100)
74 self.Bind(wx.EVT_SIZE, self.SplitterOnSize)
75 self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnChanged)
78 def OnChanged(self, event):
79 """update sash offset from the bottom of the window"""
80 self.sashoffset = self.GetSize().height - event.GetSashPosition()
84 # Make the splitter expand the top window when resized
85 def SplitterOnSize(self, event):
86 splitter = event.GetEventObject()
87 sz = splitter.GetSize()
88 splitter.SetSashPosition(sz.height - self.sashoffset, True)
92 def LoadSettings(self, config):
93 self.shell.LoadSettings(config)
94 self.filling.LoadSettings(config)
96 pos = config.ReadInt('Sash/CrustPos', 400)
97 wx.CallAfter(self.SetSashPosition, pos)
98 def _updateSashPosValue():
100 self.sashoffset = sz.height - self.GetSashPosition()
101 wx.CallAfter(_updateSashPosValue)
102 zoom = config.ReadInt('View/Zoom/Display', -99)
104 self.display.SetZoom(zoom)
107 def SaveSettings(self, config):
108 self.shell.SaveSettings(config)
109 self.filling.SaveSettings(config)
111 config.WriteInt('Sash/CrustPos', self.GetSashPosition())
112 config.WriteInt('View/Zoom/Display', self.display.GetZoom())
118 class Display(editwindow.EditWindow):
119 """STC used to display an object using Pretty Print."""
121 def __init__(self, parent, id=-1, pos=wx.DefaultPosition,
123 style=wx.CLIP_CHILDREN | wx.SUNKEN_BORDER,
125 """Create Display instance."""
126 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
127 # Configure various defaults and user preferences.
128 self.SetReadOnly(True)
129 self.SetWrapMode(False)
131 dispatcher.connect(receiver=self.push, signal='Interpreter.push')
133 def push(self, command, more):
134 """Receiver for Interpreter.push signal."""
138 if not hasattr(self, "item"):
140 self.SetReadOnly(False)
141 text = pprint.pformat(self.item)
143 self.SetReadOnly(True)
145 def setItem(self, item):
146 """Set item to pretty print in the notebook Display tab."""
149 if self.GetParent().GetSelection() != self.nbTab:
150 focus = wx.Window.FindFocus()
151 self.GetParent().SetSelection(self.nbTab)
152 wx.CallAfter(focus.SetFocus)
155 # TODO: Switch this to a editwindow.EditWindow
156 class Calltip(wx.TextCtrl):
157 """Text control containing the most recent shell calltip."""
159 def __init__(self, parent=None, id=-1):
160 style = (wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2)
161 wx.TextCtrl.__init__(self, parent, id, style=style)
162 self.SetBackgroundColour(wx.Colour(255, 255, 208))
163 dispatcher.connect(receiver=self.display, signal='Shell.calltip')
166 font = wx.Font(df.GetPointSize(), wx.TELETYPE, wx.NORMAL, wx.NORMAL)
169 def display(self, calltip):
170 """Receiver for Shell.calltip signal."""
171 ## self.SetValue(calltip) # Caused refresh problem on Windows.
173 self.AppendText(calltip)
176 # TODO: Switch this to a editwindow.EditWindow
177 class SessionListing(wx.TextCtrl):
178 """Text control containing all commands for session."""
180 def __init__(self, parent=None, id=-1):
181 style = (wx.TE_MULTILINE | wx.TE_READONLY |
182 wx.TE_RICH2 | wx.TE_DONTWRAP)
183 wx.TextCtrl.__init__(self, parent, id, style=style)
184 dispatcher.connect(receiver=self.addHistory, signal="Shell.addHistory")
185 dispatcher.connect(receiver=self.clearHistory, signal="Shell.clearHistory")
186 dispatcher.connect(receiver=self.loadHistory, signal="Shell.loadHistory")
189 font = wx.Font(df.GetPointSize(), wx.TELETYPE, wx.NORMAL, wx.NORMAL)
192 def loadHistory(self, history):
193 # preload the existing history, if any
196 self.SetValue('\n'.join(hist) + '\n')
197 self.SetInsertionPointEnd()
199 def addHistory(self, command):
201 self.SetInsertionPointEnd()
202 self.AppendText(command + '\n')
204 def clearHistory(self):
208 class DispatcherListing(wx.TextCtrl):
209 """Text control containing all dispatches for session."""
211 def __init__(self, parent=None, id=-1):
212 style = (wx.TE_MULTILINE | wx.TE_READONLY |
213 wx.TE_RICH2 | wx.TE_DONTWRAP)
214 wx.TextCtrl.__init__(self, parent, id, style=style)
215 dispatcher.connect(receiver=self.spy)
218 font = wx.Font(df.GetPointSize(), wx.TELETYPE, wx.NORMAL, wx.NORMAL)
221 def spy(self, signal, sender):
222 """Receiver for Any signal from Any sender."""
223 text = '%r from %s' % (signal, sender)
224 self.SetInsertionPointEnd()
225 start, end = self.GetSelection()
227 self.SetSelection(0, 0)
228 self.AppendText(text + '\n')
232 class CrustFrame(frame.Frame, frame.ShellFrameMixin):
233 """Frame containing all the PyCrust components."""
236 revision = __revision__
239 def __init__(self, parent=None, id=-1, title='PyCrust',
240 pos=wx.DefaultPosition, size=wx.DefaultSize,
241 style=wx.DEFAULT_FRAME_STYLE,
242 rootObject=None, rootLabel=None, rootIsNamespace=True,
243 locals=None, InterpClass=None,
244 config=None, dataDir=None,
246 """Create CrustFrame instance."""
247 frame.Frame.__init__(self, parent, id, title, pos, size, style)
248 frame.ShellFrameMixin.__init__(self, config, dataDir)
250 if size == wx.DefaultSize:
251 self.SetSize((800, 600))
253 intro = 'PyCrust %s - The Flakiest Python Shell' % VERSION
254 self.SetStatusText(intro.replace('\n', ', '))
255 self.crust = Crust(parent=self, intro=intro,
256 rootObject=rootObject,
258 rootIsNamespace=rootIsNamespace,
260 InterpClass=InterpClass,
261 startupScript=self.startupScript,
262 execStartupScript=self.execStartupScript,
264 self.shell = self.crust.shell
266 # Override the filling so that status messages go to the status bar.
267 self.crust.filling.tree.setStatusText = self.SetStatusText
269 # Override the shell so that status messages go to the status bar.
270 self.shell.setStatusText = self.SetStatusText
272 self.shell.SetFocus()
276 def OnClose(self, event):
277 """Event handler for closing."""
279 self.crust.shell.destroy()
283 def OnAbout(self, event):
284 """Display an About window."""
285 title = 'About PyCrust'
286 text = 'PyCrust %s\n\n' % VERSION + \
287 'Yet another Python shell, only flakier.\n\n' + \
288 'Half-baked by Patrick K. O\'Brien,\n' + \
289 'the other half is still in the oven.\n\n' + \
290 'Shell Revision: %s\n' % self.shell.revision + \
291 'Interpreter Revision: %s\n\n' % self.shell.interp.revision + \
292 'Platform: %s\n' % sys.platform + \
293 'Python Version: %s\n' % sys.version.split()[0] + \
294 'wxPython Version: %s\n' % wx.VERSION_STRING + \
295 ('\t(%s)\n' % ", ".join(wx.PlatformInfo[1:]))
296 dialog = wx.MessageDialog(self, text, title,
297 wx.OK | wx.ICON_INFORMATION)
302 def OnHelp(self, event):
303 """Show a help dialog."""
304 frame.ShellFrameMixin.OnHelp(self, event)
307 def LoadSettings(self):
308 if self.config is not None:
309 frame.ShellFrameMixin.LoadSettings(self)
310 frame.Frame.LoadSettings(self, self.config)
311 self.crust.LoadSettings(self.config)
314 def SaveSettings(self, force=False):
315 if self.config is not None:
316 frame.ShellFrameMixin.SaveSettings(self)
317 if self.autoSaveSettings or force:
318 frame.Frame.SaveSettings(self, self.config)
319 self.crust.SaveSettings(self.config)
322 def DoSaveSettings(self):
323 if self.config is not None:
324 self.SaveSettings(force=True)