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