1 """The PyCrust Shell is an interactive text control in which a user types in
2 commands to be sent to the interpreter. This particular shell is based on
3 wxPython's wxStyledTextCtrl. The latest files are always available at the
4 SourceForge project page at http://sourceforge.net/projects/pycrust/."""
6 __author__
= "Patrick K. O'Brien <pobrien@orbtech.com>"
8 __date__
= "July 1, 2001"
9 __version__
= "$Revision$"[11:-2]
11 from wxPython
.wx
import *
12 from wxPython
.stc
import *
16 from pseudo
import PseudoFileIn
17 from pseudo
import PseudoFileOut
18 from pseudo
import PseudoFileErr
19 from version
import VERSION
22 if wxPlatform
== '__WXMSW__':
23 faces
= { 'times' : 'Times New Roman',
24 'mono' : 'Courier New',
25 'helv' : 'Lucida Console',
26 'lucida' : 'Lucida Console',
27 'other' : 'Comic Sans MS',
33 faces
= { 'times' : 'Times',
36 'other' : 'new century schoolbook',
43 class Shell(wxStyledTextCtrl
):
44 """PyCrust Shell based on wxStyledTextCtrl."""
46 name
= 'PyCrust Shell'
47 revision
= __version__
49 def __init__(self
, parent
, id=-1, pos
=wxDefaultPosition
, \
50 size
=wxDefaultSize
, style
=wxCLIP_CHILDREN
, introText
='', \
51 locals=None, InterpClass
=None, *args
, **kwds
):
52 """Create a PyCrust Shell instance."""
53 wxStyledTextCtrl
.__init
__(self
, parent
, id, pos
, size
, style
)
54 # Grab these so they can be restored by self.redirect* methods.
55 self
.stdin
= sys
.stdin
56 self
.stdout
= sys
.stdout
57 self
.stderr
= sys
.stderr
58 # Add the current working directory "." to the search path.
59 sys
.path
.insert(0, os
.curdir
)
60 # Import a default interpreter class if one isn't provided.
61 if InterpClass
== None:
62 from interpreter
import Interpreter
64 Interpreter
= InterpClass
65 # Create default locals so we have something interesting.
66 shellLocals
= {'__name__': 'PyShell',
67 '__doc__': 'PyShell, The PyCrust Python Shell.',
68 '__version__': VERSION
,
70 # Add the dictionary that was passed in.
72 shellLocals
.update(locals)
73 self
.interp
= Interpreter(locals=shellLocals
, \
75 stdin
=PseudoFileIn(self
.readIn
), \
76 stdout
=PseudoFileOut(self
.writeOut
), \
77 stderr
=PseudoFileErr(self
.writeErr
), \
79 # Keep track of the most recent prompt starting and ending positions.
80 self
.promptPos
= [0, 0]
81 # Keep track of multi-line commands.
83 # Create the command history. Commands are added into the front of
84 # the list (ie. at index 0) as they are entered. self.historyPos is
85 # the current position in the history; it gets incremented as you
86 # retrieve the previous command, decremented as you retrieve the next,
87 # and reset when you hit Enter. self.historyPos == -1 means you're on
88 # the current command, not in the history. self.tempCommand is
89 # storage space for whatever was on the last line when you first hit
90 # "Retrieve-Previous", so that the final "Retrieve-Next" will restore
91 # whatever was originally there. self.lastCommandRecalled remembers
92 # the index of the last command to be recalled from the history, so
93 # you can repeat a group of commands by going up-up-up-enter to find
94 # the first one in the group then down-enter-down-enter to recall each
95 # subsequent command. Also useful for multiline commands, in lieu of
96 # a proper implementation of those.
100 self
.lastCommandRecalled
= -1
101 # Assign handlers for keyboard events.
102 EVT_KEY_DOWN(self
, self
.OnKeyDown
)
103 EVT_CHAR(self
, self
.OnChar
)
104 # Configure various defaults and user preferences.
106 # Display the introductory banner information.
107 try: self
.showIntro(introText
)
109 # Assign some pseudo keywords to the interpreter's namespace.
110 try: self
.setBuiltinKeywords()
112 # Add 'shell' to the interpreter's local namespace.
113 try: self
.setLocalShell()
115 # Do this last so the user has complete control over their
116 # environment. They can override anything they want.
117 try: self
.execStartupScript(self
.interp
.startupScript
)
124 """Configure shell based on user preferences."""
125 self
.SetMarginType(1, wxSTC_MARGIN_NUMBER
)
126 self
.SetMarginWidth(1, 40)
128 self
.SetLexer(wxSTC_LEX_PYTHON
)
129 self
.SetKeyWords(0, ' '.join(keyword
.kwlist
))
131 self
.setStyles(faces
)
132 self
.SetViewWhiteSpace(0)
135 # Do we want to automatically pop up command completion options?
136 self
.autoComplete
= 1
137 self
.autoCompleteIncludeMagic
= 1
138 self
.autoCompleteIncludeSingle
= 1
139 self
.autoCompleteIncludeDouble
= 1
140 self
.autoCompleteCaseInsensitive
= 1
141 self
.AutoCompSetIgnoreCase(self
.autoCompleteCaseInsensitive
)
142 # De we want to automatically pop up command argument help?
144 self
.CallTipSetBackground(wxColour(255, 255, 232))
146 def showIntro(self
, text
=''):
147 """Display introductory text in the shell."""
149 if not text
.endswith(os
.linesep
): text
+= os
.linesep
152 self
.write(self
.interp
.introText
)
153 except AttributeError:
156 def setBuiltinKeywords(self
):
157 """Create pseudo keywords as part of builtins.
159 This is a rather clever hack that sets "close", "exit" and "quit"
160 to a PseudoKeyword object so that we can make them do what we want.
161 In this case what we want is to call our self.quit() method.
162 The user can type "close", "exit" or "quit" without the final parens.
164 ## POB: This is having some weird side-effects so I'm taking it out.
165 ## import __builtin__
166 ## from pseudo import PseudoKeyword
167 ## __builtin__.close = __builtin__.exit = __builtin__.quit = \
168 ## PseudoKeyword(self.quit)
170 from pseudo
import PseudoKeyword
171 __builtin__
.close
= __builtin__
.exit
= __builtin__
.quit
= \
172 'Click on the close button to leave the application.'
175 """Quit the application."""
177 # XXX Good enough for now but later we want to send a close event.
179 # In the close event handler we can prompt to make sure they want to quit.
180 # Other applications, like PythonCard, may choose to hide rather than
181 # quit so we should just post the event and let the surrounding app
182 # decide what it wants to do.
183 self
.write('Click on the close button to leave the application.')
185 def setLocalShell(self
):
186 """Add 'shell' to locals."""
187 self
.interp
.locals['shell'] = self
189 def execStartupScript(self
, startupScript
):
190 """Execute the user's PYTHONSTARTUP script if they have one."""
191 if startupScript
and os
.path
.isfile(startupScript
):
192 startupText
= 'Startup script executed: ' + startupScript
193 self
.push('print %s;execfile(%s)' % \
194 (`startupText`
, `startupScript`
))
198 def setStyles(self
, faces
):
199 """Configure font size, typeface and color for lexer."""
202 self
.StyleSetSpec(wxSTC_STYLE_DEFAULT
, "face:%(mono)s,size:%(size)d" % faces
)
207 self
.StyleSetSpec(wxSTC_STYLE_LINENUMBER
, "back:#C0C0C0,face:%(mono)s,size:%(lnsize)d" % faces
)
208 self
.StyleSetSpec(wxSTC_STYLE_CONTROLCHAR
, "face:%(mono)s" % faces
)
209 self
.StyleSetSpec(wxSTC_STYLE_BRACELIGHT
, "fore:#0000FF,back:#FFFF88")
210 self
.StyleSetSpec(wxSTC_STYLE_BRACEBAD
, "fore:#FF0000,back:#FFFF88")
213 self
.StyleSetSpec(wxSTC_P_DEFAULT
, "face:%(mono)s" % faces
)
214 self
.StyleSetSpec(wxSTC_P_COMMENTLINE
, "fore:#007F00,face:%(mono)s" % faces
)
215 self
.StyleSetSpec(wxSTC_P_NUMBER
, "")
216 self
.StyleSetSpec(wxSTC_P_STRING
, "fore:#7F007F,face:%(mono)s" % faces
)
217 self
.StyleSetSpec(wxSTC_P_CHARACTER
, "fore:#7F007F,face:%(mono)s" % faces
)
218 self
.StyleSetSpec(wxSTC_P_WORD
, "fore:#00007F,bold")
219 self
.StyleSetSpec(wxSTC_P_TRIPLE
, "fore:#7F0000")
220 self
.StyleSetSpec(wxSTC_P_TRIPLEDOUBLE
, "fore:#000033,back:#FFFFE8")
221 self
.StyleSetSpec(wxSTC_P_CLASSNAME
, "fore:#0000FF,bold")
222 self
.StyleSetSpec(wxSTC_P_DEFNAME
, "fore:#007F7F,bold")
223 self
.StyleSetSpec(wxSTC_P_OPERATOR
, "")
224 self
.StyleSetSpec(wxSTC_P_IDENTIFIER
, "")
225 self
.StyleSetSpec(wxSTC_P_COMMENTBLOCK
, "fore:#7F7F7F")
226 self
.StyleSetSpec(wxSTC_P_STRINGEOL
, "fore:#000000,face:%(mono)s,back:#E0C0E0,eolfilled" % faces
)
228 def OnKeyDown(self
, event
):
229 """Key down event handler.
231 The main goal here is to not allow modifications to previous
233 key
= event
.KeyCode()
234 currpos
= self
.GetCurrentPos()
235 stoppos
= self
.promptPos
[1]
236 # If the auto-complete window is up let it do its thing.
237 if self
.AutoCompActive():
239 # Control+UpArrow steps up through the history.
240 elif key
== WXK_UP
and event
.ControlDown() \
241 and self
.historyPos
< len(self
.history
) - 1:
242 # Move to the end of the buffer.
243 endpos
= self
.GetTextLength()
244 self
.SetCurrentPos(endpos
)
245 # The first Control+Up stores the current command;
246 # Control+Down brings it back.
247 if self
.historyPos
== -1:
248 self
.tempCommand
= self
.getCommand()
249 # Now replace the current line with the next one from the history.
250 self
.historyPos
= self
.historyPos
+ 1
251 self
.SetSelection(stoppos
, endpos
)
252 self
.ReplaceSelection(self
.history
[self
.historyPos
])
253 # Control+DownArrow steps down through the history.
254 elif key
== WXK_DOWN
and event
.ControlDown():
255 # Move to the end of the buffer.
256 endpos
= self
.GetTextLength()
257 self
.SetCurrentPos(endpos
)
258 # Are we at the bottom end of the history?
259 if self
.historyPos
== -1:
260 # Do we have a lastCommandRecalled stored?
261 if self
.lastCommandRecalled
>= 0:
262 # Replace the current line with the command after the
263 # last-recalled command (you'd think there should be a +1
264 # here but there isn't because the history was shuffled up
265 # by 1 after the previous command was recalled).
266 self
.SetSelection(stoppos
, endpos
)
267 self
.ReplaceSelection(self
.history
[self
.lastCommandRecalled
])
268 # We've now warped into middle of the history.
269 self
.historyPos
= self
.lastCommandRecalled
270 self
.lastCommandRecalled
= -1
272 # Fetch either the previous line from the history, or the saved
273 # command if we're back at the start.
274 self
.historyPos
= self
.historyPos
- 1
275 if self
.historyPos
== -1:
276 newText
= self
.tempCommand
278 newText
= self
.history
[self
.historyPos
]
279 # Replace the current line with the new text.
280 self
.SetSelection(stoppos
, endpos
)
281 self
.ReplaceSelection(newText
)
282 # F8 on the last line does a search up the history for the text in
283 # front of the cursor.
284 elif key
== WXK_F8
and self
.GetCurrentLine() == self
.GetLineCount()-1:
285 tempCommand
= self
.getCommand()
286 # The first F8 saves the current command, just like Control+Up.
287 if self
.historyPos
== -1:
288 self
.tempCommand
= tempCommand
289 # The text up to the cursor is what we search for.
290 searchText
= tempCommand
291 numCharsAfterCursor
= self
.GetTextLength() - self
.GetCurrentPos()
292 if numCharsAfterCursor
> 0:
293 searchText
= searchText
[:-numCharsAfterCursor
]
294 # Search upwards from the current history position and loop back
295 # to the beginning if we don't find anything.
296 for i
in range(self
.historyPos
+1, len(self
.history
)) + \
297 range(self
.historyPos
):
298 command
= self
.history
[i
]
299 if command
[:len(searchText
)] == searchText
:
300 # Replace the current line with the one we've found.
301 endpos
= self
.GetTextLength()
302 self
.SetSelection(stoppos
, endpos
)
303 self
.ReplaceSelection(command
)
304 # Put the cursor back at the end of the search text.
305 pos
= self
.GetTextLength() - len(command
) + len(searchText
)
306 self
.SetCurrentPos(pos
)
308 # We've now warped into middle of the history.
310 self
.lastCommandRecalled
= -1
312 # Return is used to submit a command to the interpreter.
313 elif key
== WXK_RETURN
:
314 if self
.CallTipActive
: self
.CallTipCancel()
316 # Home needs to be aware of the prompt.
317 elif key
== WXK_HOME
:
318 if currpos
>= stoppos
:
319 self
.SetCurrentPos(stoppos
)
320 self
.SetAnchor(stoppos
)
323 # Basic navigation keys should work anywhere.
324 elif key
in (WXK_END
, WXK_LEFT
, WXK_RIGHT
, WXK_UP
, WXK_DOWN
, \
325 WXK_PRIOR
, WXK_NEXT
):
327 # Don't backspace over the latest prompt.
328 elif key
== WXK_BACK
:
329 if currpos
> stoppos
:
331 # Only allow these keys after the latest prompt.
332 elif key
in (WXK_TAB
, WXK_DELETE
):
333 if currpos
>= stoppos
:
335 # Don't toggle between insert mode and overwrite mode.
336 elif key
== WXK_INSERT
:
341 def OnChar(self
, event
):
342 """Keypress event handler.
344 The main goal here is to not allow modifications to previous
346 key
= event
.KeyCode()
347 currpos
= self
.GetCurrentPos()
348 stoppos
= self
.promptPos
[1]
349 if currpos
>= stoppos
:
351 # "." The dot or period key activates auto completion.
352 # Get the command between the prompt and the cursor.
353 # Add a dot to the end of the command.
354 command
= self
.GetTextRange(stoppos
, currpos
) + '.'
356 if self
.autoComplete
: self
.autoCompleteShow(command
)
358 # "(" The left paren activates a call tip and cancels
359 # an active auto completion.
360 if self
.AutoCompActive(): self
.AutoCompCancel()
361 # Get the command between the prompt and the cursor.
362 # Add the '(' to the end of the command.
363 command
= self
.GetTextRange(stoppos
, currpos
) + '('
365 if self
.autoCallTip
: self
.autoCallTipShow(command
)
367 # Allow the normal event handling to take place.
372 def setStatusText(self
, text
):
373 """Display status information."""
375 # This method will most likely be replaced by the enclosing app
376 # to do something more interesting, like write to a status bar.
379 def processLine(self
):
380 """Process the line of text at which the user hit Enter."""
382 # The user hit ENTER and we need to decide what to do. They could be
383 # sitting on any line in the shell.
385 # Grab information about the current line.
386 thepos
= self
.GetCurrentPos()
387 theline
= self
.GetCurrentLine()
388 command
= self
.getCommand()
389 # Go to the very bottom of the text.
390 endpos
= self
.GetTextLength()
391 self
.SetCurrentPos(endpos
)
392 endline
= self
.GetCurrentLine()
393 # If they hit RETURN on the last line, execute the command.
394 if theline
== endline
:
396 # Otherwise, replace the last line with the new line.
398 # If the new line contains a command (even an invalid one).
400 startpos
= self
.PositionFromLine(endline
)
401 self
.SetSelection(startpos
, endpos
)
402 self
.ReplaceSelection('')
405 # Otherwise, put the cursor back where we started.
407 self
.SetCurrentPos(thepos
)
408 self
.SetAnchor(thepos
)
410 def getCommand(self
, text
=None):
411 """Extract a command from text which may include a shell prompt.
413 The command may not necessarily be valid Python syntax."""
415 text
= self
.GetCurLine()[0]
416 # XXX Need to extract real prompts here. Need to keep track of the
417 # prompt every time a command is issued.
423 # Strip the prompt off the front of text leaving just the command.
424 if text
[:ps1size
] == ps1
:
425 command
= text
[ps1size
:]
426 elif text
[:ps2size
] == ps2
:
427 command
= text
[ps2size
:]
432 def push(self
, command
):
433 """Send command to the interpreter for execution."""
434 self
.addHistory(command
)
435 self
.write(os
.linesep
)
436 self
.more
= self
.interp
.push(command
)
438 # Keep the undo feature from undoing previous responses. The only
439 # thing that can be undone is stuff typed after the prompt, before
440 # hitting enter. After they hit enter it becomes permanent.
441 self
.EmptyUndoBuffer()
443 def addHistory(self
, command
):
444 """Add command to the command history."""
445 # Store the last-recalled command; see the main comment for
446 # self.lastCommandRecalled.
448 self
.lastCommandRecalled
= self
.historyPos
449 # Reset the history position.
451 # Insert this command into the history, unless it's a blank
452 # line or the same as the last command.
454 and (len(self
.history
) == 0 or command
!= self
.history
[0]):
455 self
.history
.insert(0, command
)
457 def write(self
, text
):
458 """Display text in the shell.
460 Replace line endings with OS-specific endings."""
461 lines
= text
.split('\r\n')
462 for l
in range(len(lines
)):
463 chunks
= lines
[l
].split('\r')
464 for c
in range(len(chunks
)):
465 chunks
[c
] = os
.linesep
.join(chunks
[c
].split('\n'))
466 lines
[l
] = os
.linesep
.join(chunks
)
467 text
= os
.linesep
.join(lines
)
469 self
.EnsureCaretVisible()
470 #self.ScrollToColumn(0)
473 """Display appropriate prompt for the context, either ps1 or ps2.
475 If this is a continuation line, autoindent as necessary."""
477 prompt
= str(sys
.ps2
)
479 prompt
= str(sys
.ps1
)
480 pos
= self
.GetCurLine()[1]
481 if pos
> 0: self
.write(os
.linesep
)
482 self
.promptPos
[0] = self
.GetCurrentPos()
484 self
.promptPos
[1] = self
.GetCurrentPos()
485 # XXX Add some autoindent magic here if more.
487 self
.write('\t') # Temporary hack indentation.
488 self
.EnsureCaretVisible()
489 self
.ScrollToColumn(0)
492 """Replacement for stdin."""
493 prompt
= 'Please enter your response:'
494 dialog
= wxTextEntryDialog(None, prompt
, \
495 'Input Dialog (Standard)', '')
497 if dialog
.ShowModal() == wxID_OK
:
498 text
= dialog
.GetValue()
499 self
.write(text
+ os
.linesep
)
505 def readRaw(self
, prompt
='Please enter your response:'):
506 """Replacement for raw_input."""
507 dialog
= wxTextEntryDialog(None, prompt
, \
508 'Input Dialog (Raw)', '')
510 if dialog
.ShowModal() == wxID_OK
:
511 text
= dialog
.GetValue()
517 def ask(self
, prompt
='Please enter your response:'):
518 """Get response from the user."""
519 return raw_input(prompt
=prompt
)
522 """Halt execution pending a response from the user."""
523 self
.ask('Press enter to continue:')
526 """Delete all text from the shell."""
529 def run(self
, command
, prompt
=1, verbose
=1):
530 """Execute command within the shell as if it was typed in directly.
531 >>> shell.run('print "this"')
536 command
= command
.rstrip()
537 if prompt
: self
.prompt()
538 if verbose
: self
.write(command
)
541 def runfile(self
, filename
):
542 """Execute all commands in file as if they were typed into the shell."""
543 file = open(filename
)
546 for command
in file.readlines():
547 if command
[:6] == 'shell.': # Run shell methods silently.
548 self
.run(command
, prompt
=0, verbose
=0)
550 self
.run(command
, prompt
=0, verbose
=1)
554 def autoCompleteShow(self
, command
):
555 """Display auto-completion popup list."""
556 list = self
.interp
.getAutoCompleteList(command
, \
557 includeMagic
=self
.autoCompleteIncludeMagic
, \
558 includeSingle
=self
.autoCompleteIncludeSingle
, \
559 includeDouble
=self
.autoCompleteIncludeDouble
)
561 options
= ' '.join(list)
563 self
.AutoCompShow(offset
, options
)
565 def autoCallTipShow(self
, command
):
566 """Display argument spec and docstring in a popup bubble thingie."""
567 if self
.CallTipActive
: self
.CallTipCancel()
568 tip
= self
.interp
.getCallTip(command
)
570 offset
= self
.GetCurrentPos()
571 self
.CallTipShow(offset
, tip
)
573 def writeOut(self
, text
):
574 """Replacement for stdout."""
577 def writeErr(self
, text
):
578 """Replacement for stderr."""
581 def redirectStdin(self
, redirect
=1):
582 """If redirect is true then sys.stdin will come from the shell."""
584 sys
.stdin
= PseudoFileIn(self
.readIn
)
586 sys
.stdin
= self
.stdin
588 def redirectStdout(self
, redirect
=1):
589 """If redirect is true then sys.stdout will go to the shell."""
591 sys
.stdout
= PseudoFileOut(self
.writeOut
)
593 sys
.stdout
= self
.stdout
595 def redirectStderr(self
, redirect
=1):
596 """If redirect is true then sys.stderr will go to the shell."""
598 sys
.stderr
= PseudoFileErr(self
.writeErr
)
600 sys
.stderr
= self
.stderr
603 """Return true if text is selected and can be cut."""
604 return self
.GetSelectionStart() != self
.GetSelectionEnd()
607 """Return true if text is selected and can be copied."""
608 return self
.GetSelectionStart() != self
.GetSelectionEnd()
611 wxID_SELECTALL
= NewId() # This *should* be defined by wxPython.
612 ID_AUTOCOMP
= NewId()
613 ID_AUTOCOMP_SHOW
= NewId()
614 ID_AUTOCOMP_INCLUDE_MAGIC
= NewId()
615 ID_AUTOCOMP_INCLUDE_SINGLE
= NewId()
616 ID_AUTOCOMP_INCLUDE_DOUBLE
= NewId()
617 ID_CALLTIPS
= NewId()
618 ID_CALLTIPS_SHOW
= NewId()
622 """Mixin class to add standard menu items."""
624 def createMenus(self
):
625 m
= self
.fileMenu
= wxMenu()
627 m
.Append(wxID_EXIT
, 'E&xit', 'Exit PyCrust')
629 m
= self
.editMenu
= wxMenu()
630 m
.Append(wxID_UNDO
, '&Undo \tCtrl+Z', 'Undo the last action')
631 m
.Append(wxID_REDO
, '&Redo \tCtrl+Y', 'Redo the last undone action')
633 m
.Append(wxID_CUT
, 'Cu&t', 'Cut the selection')
634 m
.Append(wxID_COPY
, '&Copy', 'Copy the selection')
635 m
.Append(wxID_PASTE
, '&Paste', 'Paste')
637 m
.Append(wxID_CLEAR
, 'Cle&ar', 'Delete the selection')
638 m
.Append(wxID_SELECTALL
, 'Select A&ll', 'Select all text')
640 m
= self
.autocompMenu
= wxMenu()
641 m
.Append(ID_AUTOCOMP_SHOW
, 'Show Auto Completion', \
642 'Show auto completion during dot syntax', \
644 m
.Append(ID_AUTOCOMP_INCLUDE_MAGIC
, 'Include Magic Attributes', \
645 'Include attributes visible to __getattr__ and __setattr__', \
647 m
.Append(ID_AUTOCOMP_INCLUDE_SINGLE
, 'Include Single Underscores', \
648 'Include attibutes prefixed by a single underscore', \
650 m
.Append(ID_AUTOCOMP_INCLUDE_DOUBLE
, 'Include Double Underscores', \
651 'Include attibutes prefixed by a double underscore', \
654 m
= self
.calltipsMenu
= wxMenu()
655 m
.Append(ID_CALLTIPS_SHOW
, 'Show Call Tips', \
656 'Show call tips with argument specifications', checkable
=1)
658 m
= self
.optionsMenu
= wxMenu()
659 m
.AppendMenu(ID_AUTOCOMP
, '&Auto Completion', self
.autocompMenu
, \
660 'Auto Completion Options')
661 m
.AppendMenu(ID_CALLTIPS
, '&Call Tips', self
.calltipsMenu
, \
664 m
= self
.helpMenu
= wxMenu()
666 m
.Append(wxID_ABOUT
, '&About...', 'About PyCrust')
668 b
= self
.menuBar
= wxMenuBar()
669 b
.Append(self
.fileMenu
, '&File')
670 b
.Append(self
.editMenu
, '&Edit')
671 b
.Append(self
.optionsMenu
, '&Options')
672 b
.Append(self
.helpMenu
, '&Help')
675 EVT_MENU(self
, wxID_EXIT
, self
.OnExit
)
676 EVT_MENU(self
, wxID_UNDO
, self
.OnUndo
)
677 EVT_MENU(self
, wxID_REDO
, self
.OnRedo
)
678 EVT_MENU(self
, wxID_CUT
, self
.OnCut
)
679 EVT_MENU(self
, wxID_COPY
, self
.OnCopy
)
680 EVT_MENU(self
, wxID_PASTE
, self
.OnPaste
)
681 EVT_MENU(self
, wxID_CLEAR
, self
.OnClear
)
682 EVT_MENU(self
, wxID_SELECTALL
, self
.OnSelectAll
)
683 EVT_MENU(self
, wxID_ABOUT
, self
.OnAbout
)
684 EVT_MENU(self
, ID_AUTOCOMP_SHOW
, \
685 self
.OnAutoCompleteShow
)
686 EVT_MENU(self
, ID_AUTOCOMP_INCLUDE_MAGIC
, \
687 self
.OnAutoCompleteIncludeMagic
)
688 EVT_MENU(self
, ID_AUTOCOMP_INCLUDE_SINGLE
, \
689 self
.OnAutoCompleteIncludeSingle
)
690 EVT_MENU(self
, ID_AUTOCOMP_INCLUDE_DOUBLE
, \
691 self
.OnAutoCompleteIncludeDouble
)
692 EVT_MENU(self
, ID_CALLTIPS_SHOW
, \
695 EVT_UPDATE_UI(self
, wxID_UNDO
, self
.OnUpdateMenu
)
696 EVT_UPDATE_UI(self
, wxID_REDO
, self
.OnUpdateMenu
)
697 EVT_UPDATE_UI(self
, wxID_CUT
, self
.OnUpdateMenu
)
698 EVT_UPDATE_UI(self
, wxID_COPY
, self
.OnUpdateMenu
)
699 EVT_UPDATE_UI(self
, wxID_PASTE
, self
.OnUpdateMenu
)
700 EVT_UPDATE_UI(self
, wxID_CLEAR
, self
.OnUpdateMenu
)
701 EVT_UPDATE_UI(self
, ID_AUTOCOMP_SHOW
, self
.OnUpdateMenu
)
702 EVT_UPDATE_UI(self
, ID_AUTOCOMP_INCLUDE_MAGIC
, self
.OnUpdateMenu
)
703 EVT_UPDATE_UI(self
, ID_AUTOCOMP_INCLUDE_SINGLE
, self
.OnUpdateMenu
)
704 EVT_UPDATE_UI(self
, ID_AUTOCOMP_INCLUDE_DOUBLE
, self
.OnUpdateMenu
)
705 EVT_UPDATE_UI(self
, ID_CALLTIPS_SHOW
, self
.OnUpdateMenu
)
707 def OnExit(self
, event
):
710 def OnUndo(self
, event
):
713 def OnRedo(self
, event
):
716 def OnCut(self
, event
):
719 def OnCopy(self
, event
):
722 def OnPaste(self
, event
):
725 def OnClear(self
, event
):
728 def OnSelectAll(self
, event
):
729 self
.shell
.SelectAll()
731 def OnAbout(self
, event
):
732 """Display an About PyCrust window."""
734 title
= 'About PyCrust'
735 text
= 'PyCrust %s\n\n' % VERSION
+ \
736 'Yet another Python shell, only flakier.\n\n' + \
737 'Half-baked by Patrick K. O\'Brien,\n' + \
738 'the other half is still in the oven.\n\n' + \
739 'Shell Revision: %s\n' % self
.shell
.revision
+ \
740 'Interpreter Revision: %s\n\n' % self
.shell
.interp
.revision
+ \
741 'Python Version: %s\n' % sys
.version
.split()[0] + \
742 'wxPython Version: %s\n' % wx
.__version
__ + \
743 'Platform: %s\n' % sys
.platform
744 dialog
= wxMessageDialog(self
, text
, title
, wxOK | wxICON_INFORMATION
)
748 def OnAutoCompleteShow(self
, event
):
749 self
.shell
.autoComplete
= event
.IsChecked()
751 def OnAutoCompleteIncludeMagic(self
, event
):
752 self
.shell
.autoCompleteIncludeMagic
= event
.IsChecked()
754 def OnAutoCompleteIncludeSingle(self
, event
):
755 self
.shell
.autoCompleteIncludeSingle
= event
.IsChecked()
757 def OnAutoCompleteIncludeDouble(self
, event
):
758 self
.shell
.autoCompleteIncludeDouble
= event
.IsChecked()
760 def OnCallTipsShow(self
, event
):
761 self
.shell
.autoCallTip
= event
.IsChecked()
763 def OnUpdateMenu(self
, event
):
764 """Update menu items based on current status."""
767 event
.Enable(self
.shell
.CanUndo())
768 elif id == wxID_REDO
:
769 event
.Enable(self
.shell
.CanRedo())
771 event
.Enable(self
.shell
.CanCut())
772 elif id == wxID_COPY
:
773 event
.Enable(self
.shell
.CanCopy())
774 elif id == wxID_PASTE
:
775 event
.Enable(self
.shell
.CanPaste())
776 elif id == wxID_CLEAR
:
777 event
.Enable(self
.shell
.CanCut())
778 elif id == ID_AUTOCOMP_SHOW
:
779 event
.Check(self
.shell
.autoComplete
)
780 elif id == ID_AUTOCOMP_INCLUDE_MAGIC
:
781 event
.Check(self
.shell
.autoCompleteIncludeMagic
)
782 elif id == ID_AUTOCOMP_INCLUDE_SINGLE
:
783 event
.Check(self
.shell
.autoCompleteIncludeSingle
)
784 elif id == ID_AUTOCOMP_INCLUDE_DOUBLE
:
785 event
.Check(self
.shell
.autoCompleteIncludeDouble
)
786 elif id == ID_CALLTIPS_SHOW
:
787 event
.Check(self
.shell
.autoCallTip
)
790 class ShellFrame(wxFrame
, ShellMenu
):
791 """Frame containing the PyCrust shell component."""
793 name
= 'PyCrust Shell Frame'
794 revision
= __version__
796 def __init__(self
, parent
=None, id=-1, title
='PyShell', \
797 pos
=wxDefaultPosition
, size
=wxDefaultSize
, \
798 style
=wxDEFAULT_FRAME_STYLE
, locals=None, \
799 InterpClass
=None, *args
, **kwds
):
800 """Create a PyCrust ShellFrame instance."""
801 wxFrame
.__init
__(self
, parent
, id, title
, pos
, size
, style
)
802 intro
= 'Welcome To PyCrust %s - The Flakiest Python Shell' % VERSION
803 self
.CreateStatusBar()
804 self
.SetStatusText(intro
)
805 if wxPlatform
== '__WXMSW__':
806 icon
= wxIcon('PyCrust.ico', wxBITMAP_TYPE_ICO
)
808 self
.shell
= Shell(parent
=self
, id=-1, introText
=intro
, \
809 locals=locals, InterpClass
=InterpClass
, \
811 # Override the shell so that status messages go to the status bar.
812 self
.shell
.setStatusText
= self
.SetStatusText