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)