]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/py/crust.py
PyCrust now has an option for showing/hiding the notebook.
[wxWidgets.git] / wxPython / wx / py / crust.py
1 """Crust combines the shell and filling into one control."""
2
3 __author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
4 __cvsid__ = "$Id$"
5 __revision__ = "$Revision$"[11:-2]
6
7 import wx
8
9 import os
10 import pprint
11 import re
12 import sys
13
14 import dispatcher
15 import editwindow
16 from filling import Filling
17 import frame
18 from shell import Shell
19 from version import VERSION
20
21
22 class Crust(wx.SplitterWindow):
23 """Crust based on SplitterWindow."""
24
25 name = 'Crust'
26 revision = __revision__
27 sashoffset = 300
28
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,
33 InterpClass=None,
34 startupScript=None, execStartupScript=True,
35 *args, **kwds):
36 """Create Crust instance."""
37 wx.SplitterWindow.__init__(self, parent, id, pos, size, style, name)
38
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)
45
46 self.shell = Shell(parent=self, introText=intro,
47 locals=locals, InterpClass=InterpClass,
48 startupScript=startupScript,
49 execStartupScript=execStartupScript,
50 *args, **kwds)
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,
58 rootLabel=rootLabel,
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)
63
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
69
70 self.calltip = Calltip(parent=self.notebook)
71 self.notebook.AddPage(page=self.calltip, text='Calltip')
72
73 self.sessionlisting = SessionListing(parent=self.notebook)
74 self.notebook.AddPage(page=self.sessionlisting, text='History')
75
76 self.dispatcherlisting = DispatcherListing(parent=self.notebook)
77 self.notebook.AddPage(page=self.dispatcherlisting, text='Dispatcher')
78
79
80 # Initialize in an unsplit mode, and check later after loading
81 # settings if we should split or not.
82 self.shell.Hide()
83 self.notebook.Hide()
84 self.Initialize(self.shell)
85 self._shouldsplit = True
86 wx.CallAfter(self._CheckShouldSplit)
87 self.SetMinimumPaneSize(100)
88
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)
92
93 def _CheckShouldSplit(self):
94 if self._shouldsplit:
95 self.SplitHorizontally(self.shell, self.notebook, -self.sashoffset)
96 self.lastsashpos = self.GetSashPosition()
97 else:
98 self.lastsashpos = -1
99
100 def ToggleTools(self):
101 """Toggle the display of the filling and other tools"""
102 if self.issplit:
103 self.Unsplit()
104 else:
105 self.SplitHorizontally(self.shell, self.notebook, -self.sashoffset)
106 self.lastsashpos = self.GetSashPosition()
107 self.issplit = self.IsSplit()
108
109 def ToolsShown(self):
110 return self.issplit
111
112 def OnChanged(self, event):
113 """update sash offset from the bottom of the window"""
114 self.sashoffset = self.GetSize().height - event.GetSashPosition()
115 self.lastsashpos = event.GetSashPosition()
116 event.Skip()
117
118 def OnSashDClick(self, event):
119 self.Unsplit()
120 self.issplit = False
121
122 # Make the splitter expand the top window when resized
123 def SplitterOnSize(self, event):
124 splitter = event.GetEventObject()
125 sz = splitter.GetSize()
126 splitter.SetSashPosition(sz.height - self.sashoffset, True)
127 event.Skip()
128
129
130 def LoadSettings(self, config):
131 self.shell.LoadSettings(config)
132 self.filling.LoadSettings(config)
133
134 pos = config.ReadInt('Sash/CrustPos', 400)
135 wx.CallAfter(self.SetSashPosition, pos)
136 def _updateSashPosValue():
137 sz = self.GetSize()
138 self.sashoffset = sz.height - self.GetSashPosition()
139 wx.CallAfter(_updateSashPosValue)
140 zoom = config.ReadInt('View/Zoom/Display', -99)
141 if zoom != -99:
142 self.display.SetZoom(zoom)
143 self.issplit = config.ReadInt('Sash/IsSplit', True)
144 if not self.issplit:
145 self._shouldsplit = False
146
147 def SaveSettings(self, config):
148 self.shell.SaveSettings(config)
149 self.filling.SaveSettings(config)
150
151 if self.lastsashpos != -1:
152 config.WriteInt('Sash/CrustPos', self.lastsashpos)
153 config.WriteInt('Sash/IsSplit', self.issplit)
154 config.WriteInt('View/Zoom/Display', self.display.GetZoom())
155
156
157
158
159
160 class Display(editwindow.EditWindow):
161 """STC used to display an object using Pretty Print."""
162
163 def __init__(self, parent, id=-1, pos=wx.DefaultPosition,
164 size=wx.DefaultSize,
165 style=wx.CLIP_CHILDREN | wx.SUNKEN_BORDER,
166 static=False):
167 """Create Display instance."""
168 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
169 # Configure various defaults and user preferences.
170 self.SetReadOnly(True)
171 self.SetWrapMode(False)
172 if not static:
173 dispatcher.connect(receiver=self.push, signal='Interpreter.push')
174
175 def push(self, command, more):
176 """Receiver for Interpreter.push signal."""
177 self.Refresh()
178
179 def Refresh(self):
180 if not hasattr(self, "item"):
181 return
182 self.SetReadOnly(False)
183 text = pprint.pformat(self.item)
184 self.SetText(text)
185 self.SetReadOnly(True)
186
187 def setItem(self, item):
188 """Set item to pretty print in the notebook Display tab."""
189 self.item = item
190 self.Refresh()
191 if self.GetParent().GetSelection() != self.nbTab:
192 focus = wx.Window.FindFocus()
193 self.GetParent().SetSelection(self.nbTab)
194 wx.CallAfter(focus.SetFocus)
195
196
197 # TODO: Switch this to a editwindow.EditWindow
198 class Calltip(wx.TextCtrl):
199 """Text control containing the most recent shell calltip."""
200
201 def __init__(self, parent=None, id=-1):
202 style = (wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2)
203 wx.TextCtrl.__init__(self, parent, id, style=style)
204 self.SetBackgroundColour(wx.Colour(255, 255, 208))
205 dispatcher.connect(receiver=self.display, signal='Shell.calltip')
206
207 df = self.GetFont()
208 font = wx.Font(df.GetPointSize(), wx.TELETYPE, wx.NORMAL, wx.NORMAL)
209 self.SetFont(font)
210
211 def display(self, calltip):
212 """Receiver for Shell.calltip signal."""
213 ## self.SetValue(calltip) # Caused refresh problem on Windows.
214 self.Clear()
215 self.AppendText(calltip)
216
217
218 # TODO: Switch this to a editwindow.EditWindow
219 class SessionListing(wx.TextCtrl):
220 """Text control containing all commands for session."""
221
222 def __init__(self, parent=None, id=-1):
223 style = (wx.TE_MULTILINE | wx.TE_READONLY |
224 wx.TE_RICH2 | wx.TE_DONTWRAP)
225 wx.TextCtrl.__init__(self, parent, id, style=style)
226 dispatcher.connect(receiver=self.addHistory, signal="Shell.addHistory")
227 dispatcher.connect(receiver=self.clearHistory, signal="Shell.clearHistory")
228 dispatcher.connect(receiver=self.loadHistory, signal="Shell.loadHistory")
229
230 df = self.GetFont()
231 font = wx.Font(df.GetPointSize(), wx.TELETYPE, wx.NORMAL, wx.NORMAL)
232 self.SetFont(font)
233
234 def loadHistory(self, history):
235 # preload the existing history, if any
236 hist = history[:]
237 hist.reverse()
238 self.SetValue('\n'.join(hist) + '\n')
239 self.SetInsertionPointEnd()
240
241 def addHistory(self, command):
242 if command:
243 self.SetInsertionPointEnd()
244 self.AppendText(command + '\n')
245
246 def clearHistory(self):
247 self.SetValue("")
248
249
250 class DispatcherListing(wx.TextCtrl):
251 """Text control containing all dispatches for session."""
252
253 def __init__(self, parent=None, id=-1):
254 style = (wx.TE_MULTILINE | wx.TE_READONLY |
255 wx.TE_RICH2 | wx.TE_DONTWRAP)
256 wx.TextCtrl.__init__(self, parent, id, style=style)
257 dispatcher.connect(receiver=self.spy)
258
259 df = self.GetFont()
260 font = wx.Font(df.GetPointSize(), wx.TELETYPE, wx.NORMAL, wx.NORMAL)
261 self.SetFont(font)
262
263 def spy(self, signal, sender):
264 """Receiver for Any signal from Any sender."""
265 text = '%r from %s' % (signal, sender)
266 self.SetInsertionPointEnd()
267 start, end = self.GetSelection()
268 if start != end:
269 self.SetSelection(0, 0)
270 self.AppendText(text + '\n')
271
272
273
274 class CrustFrame(frame.Frame, frame.ShellFrameMixin):
275 """Frame containing all the PyCrust components."""
276
277 name = 'CrustFrame'
278 revision = __revision__
279
280
281 def __init__(self, parent=None, id=-1, title='PyCrust',
282 pos=wx.DefaultPosition, size=wx.DefaultSize,
283 style=wx.DEFAULT_FRAME_STYLE,
284 rootObject=None, rootLabel=None, rootIsNamespace=True,
285 locals=None, InterpClass=None,
286 config=None, dataDir=None,
287 *args, **kwds):
288 """Create CrustFrame instance."""
289 frame.Frame.__init__(self, parent, id, title, pos, size, style)
290 frame.ShellFrameMixin.__init__(self, config, dataDir)
291
292 if size == wx.DefaultSize:
293 self.SetSize((800, 600))
294
295 intro = 'PyCrust %s - The Flakiest Python Shell' % VERSION
296 self.SetStatusText(intro.replace('\n', ', '))
297 self.crust = Crust(parent=self, intro=intro,
298 rootObject=rootObject,
299 rootLabel=rootLabel,
300 rootIsNamespace=rootIsNamespace,
301 locals=locals,
302 InterpClass=InterpClass,
303 startupScript=self.startupScript,
304 execStartupScript=self.execStartupScript,
305 *args, **kwds)
306 self.shell = self.crust.shell
307
308 # Override the filling so that status messages go to the status bar.
309 self.crust.filling.tree.setStatusText = self.SetStatusText
310
311 # Override the shell so that status messages go to the status bar.
312 self.shell.setStatusText = self.SetStatusText
313
314 self.shell.SetFocus()
315 self.LoadSettings()
316
317
318 def OnClose(self, event):
319 """Event handler for closing."""
320 self.SaveSettings()
321 self.crust.shell.destroy()
322 self.Destroy()
323
324
325 def OnAbout(self, event):
326 """Display an About window."""
327 title = 'About PyCrust'
328 text = 'PyCrust %s\n\n' % VERSION + \
329 'Yet another Python shell, only flakier.\n\n' + \
330 'Half-baked by Patrick K. O\'Brien,\n' + \
331 'the other half is still in the oven.\n\n' + \
332 'Shell Revision: %s\n' % self.shell.revision + \
333 'Interpreter Revision: %s\n\n' % self.shell.interp.revision + \
334 'Platform: %s\n' % sys.platform + \
335 'Python Version: %s\n' % sys.version.split()[0] + \
336 'wxPython Version: %s\n' % wx.VERSION_STRING + \
337 ('\t(%s)\n' % ", ".join(wx.PlatformInfo[1:]))
338 dialog = wx.MessageDialog(self, text, title,
339 wx.OK | wx.ICON_INFORMATION)
340 dialog.ShowModal()
341 dialog.Destroy()
342
343
344 def ToggleTools(self):
345 """Toggle the display of the filling and other tools"""
346 return self.crust.ToggleTools()
347
348 def ToolsShown(self):
349 return self.crust.ToolsShown()
350
351 def OnHelp(self, event):
352 """Show a help dialog."""
353 frame.ShellFrameMixin.OnHelp(self, event)
354
355
356 def LoadSettings(self):
357 if self.config is not None:
358 frame.ShellFrameMixin.LoadSettings(self)
359 frame.Frame.LoadSettings(self, self.config)
360 self.crust.LoadSettings(self.config)
361
362
363 def SaveSettings(self, force=False):
364 if self.config is not None:
365 frame.ShellFrameMixin.SaveSettings(self)
366 if self.autoSaveSettings or force:
367 frame.Frame.SaveSettings(self, self.config)
368 self.crust.SaveSettings(self.config)
369
370
371 def DoSaveSettings(self):
372 if self.config is not None:
373 self.SaveSettings(force=True)
374 self.config.Flush()
375
376
377