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 version
import VERSION
19 if wxPlatform
== '__WXMSW__':
20 faces
= { 'times' : 'Times New Roman',
21 'mono' : 'Courier New',
22 'helv' : 'Lucida Console',
23 'lucida' : 'Lucida Console',
24 'other' : 'Comic Sans MS',
30 faces
= { 'times' : 'Times',
33 'other' : 'new century schoolbook',
40 class Shell(wxStyledTextCtrl
):
41 """PyCrust Shell based on wxStyledTextCtrl."""
43 name
= 'PyCrust Shell'
44 revision
= __version__
46 def __init__(self
, parent
, id=-1, pos
=wxDefaultPosition
, \
47 size
=wxDefaultSize
, style
=wxCLIP_CHILDREN
, introText
='', \
48 locals=None, InterpClass
=None, *args
, **kwds
):
49 """Create a PyCrust Shell instance."""
50 wxStyledTextCtrl
.__init
__(self
, parent
, id, pos
, size
, style
)
51 # Import a default interpreter class if one isn't provided.
52 if InterpClass
== None:
53 from interpreter
import Interpreter
55 Interpreter
= InterpClass
56 # Create default locals so we have something interesting.
57 shellLocals
= {'__name__': 'PyShell',
58 '__doc__': 'PyShell, The PyCrust Python Shell.',
59 '__version__': VERSION
,
61 # Add the dictionary that was passed in.
63 shellLocals
.update(locals)
64 from pseudo
import PseudoFileIn
, PseudoFileOut
, PseudoFileErr
65 self
.interp
= Interpreter(locals=shellLocals
, \
67 stdin
=PseudoFileIn(self
.readIn
), \
68 stdout
=PseudoFileOut(self
.writeOut
), \
69 stderr
=PseudoFileErr(self
.writeErr
), \
71 # Keep track of the most recent prompt starting and ending positions.
72 self
.promptPos
= [0, 0]
73 # Keep track of multi-line commands.
75 # Create the command history. Commands are added into the front of
76 # the list (ie. at index 0) as they are entered. self.historyPos is
77 # the current position in the history; it gets incremented as you
78 # retrieve the previous command, decremented as you retrieve the next,
79 # and reset when you hit Enter. self.historyPos == -1 means you're on
80 # the current command, not in the history. self.tempCommand is
81 # storage space for whatever was on the last line when you first hit
82 # "Retrieve-Previous", so that the final "Retrieve-Next" will restore
83 # whatever was originally there. self.lastCommandRecalled remembers
84 # the index of the last command to be recalled from the history, so
85 # you can repeat a group of commands by going up-up-up-enter to find
86 # the first one in the group then down-enter-down-enter to recall each
87 # subsequent command. Also useful for multiline commands, in lieu of
88 # a proper implementation of those.
92 self
.lastCommandRecalled
= -1
93 # Assign handlers for keyboard events.
94 EVT_KEY_DOWN(self
, self
.OnKeyDown
)
95 EVT_CHAR(self
, self
.OnChar
)
96 # Configure various defaults and user preferences.
98 # Display the introductory banner information.
99 try: self
.showIntro(introText
)
101 # Assign some pseudo keywords to the interpreter's namespace.
102 try: self
.setBuiltinKeywords()
104 # Add 'shell' to the interpreter's local namespace.
105 try: self
.setLocalShell()
107 # Do this last so the user has complete control over their
108 # environment. They can override anything they want.
109 try: self
.execStartupScript(self
.interp
.startupScript
)
116 """Configure shell based on user preferences."""
117 self
.SetMarginType(1, wxSTC_MARGIN_NUMBER
)
118 self
.SetMarginWidth(1, 40)
120 self
.SetLexer(wxSTC_LEX_PYTHON
)
121 self
.SetKeyWords(0, ' '.join(keyword
.kwlist
))
123 self
.setStyles(faces
)
124 self
.SetViewWhiteSpace(0)
127 # Do we want to automatically pop up command completion options?
128 self
.autoComplete
= 1
129 self
.autoCompleteIncludeMagic
= 1
130 self
.autoCompleteIncludeSingle
= 1
131 self
.autoCompleteIncludeDouble
= 1
132 self
.autoCompleteCaseInsensitive
= 1
133 self
.AutoCompSetIgnoreCase(self
.autoCompleteCaseInsensitive
)
134 # De we want to automatically pop up command argument help?
136 self
.CallTipSetBackground(wxColour(255, 255, 232))
138 def showIntro(self
, text
=''):
139 """Display introductory text in the shell."""
141 if not text
.endswith(os
.linesep
): text
+= os
.linesep
144 self
.write(self
.interp
.introText
)
145 except AttributeError:
148 def setBuiltinKeywords(self
):
149 """Create pseudo keywords as part of builtins.
151 This is a rather clever hack that sets "close", "exit" and "quit"
152 to a PseudoKeyword object so that we can make them do what we want.
153 In this case what we want is to call our self.quit() method.
154 The user can type "close", "exit" or "quit" without the final parens.
156 ## POB: This is having some weird side-effects so I'm taking it out.
157 ## import __builtin__
158 ## from pseudo import PseudoKeyword
159 ## __builtin__.close = __builtin__.exit = __builtin__.quit = \
160 ## PseudoKeyword(self.quit)
162 from pseudo
import PseudoKeyword
163 __builtin__
.close
= __builtin__
.exit
= __builtin__
.quit
= \
164 'Click on the close button to leave the application.'
167 """Quit the application."""
169 # XXX Good enough for now but later we want to send a close event.
171 # In the close event handler we can prompt to make sure they want to quit.
172 # Other applications, like PythonCard, may choose to hide rather than
173 # quit so we should just post the event and let the surrounding app
174 # decide what it wants to do.
175 self
.write('Click on the close button to leave the application.')
177 def setLocalShell(self
):
178 """Add 'shell' to locals."""
179 self
.interp
.locals['shell'] = self
181 def execStartupScript(self
, startupScript
):
182 """Execute the user's PYTHONSTARTUP script if they have one."""
183 if startupScript
and os
.path
.isfile(startupScript
):
184 startupText
= 'Startup script executed: ' + startupScript
185 self
.push('print %s;execfile(%s)' % \
186 (`startupText`
, `startupScript`
))
190 def setStyles(self
, faces
):
191 """Configure font size, typeface and color for lexer."""
194 self
.StyleSetSpec(wxSTC_STYLE_DEFAULT
, "face:%(mono)s,size:%(size)d" % faces
)
199 self
.StyleSetSpec(wxSTC_STYLE_LINENUMBER
, "back:#C0C0C0,face:%(mono)s,size:%(lnsize)d" % faces
)
200 self
.StyleSetSpec(wxSTC_STYLE_CONTROLCHAR
, "face:%(mono)s" % faces
)
201 self
.StyleSetSpec(wxSTC_STYLE_BRACELIGHT
, "fore:#0000FF,back:#FFFF88")
202 self
.StyleSetSpec(wxSTC_STYLE_BRACEBAD
, "fore:#FF0000,back:#FFFF88")
205 self
.StyleSetSpec(wxSTC_P_DEFAULT
, "face:%(mono)s" % faces
)
206 self
.StyleSetSpec(wxSTC_P_COMMENTLINE
, "fore:#007F00,face:%(mono)s" % faces
)
207 self
.StyleSetSpec(wxSTC_P_NUMBER
, "")
208 self
.StyleSetSpec(wxSTC_P_STRING
, "fore:#7F007F,face:%(mono)s" % faces
)
209 self
.StyleSetSpec(wxSTC_P_CHARACTER
, "fore:#7F007F,face:%(mono)s" % faces
)
210 self
.StyleSetSpec(wxSTC_P_WORD
, "fore:#00007F,bold")
211 self
.StyleSetSpec(wxSTC_P_TRIPLE
, "fore:#7F0000")
212 self
.StyleSetSpec(wxSTC_P_TRIPLEDOUBLE
, "fore:#000033,back:#FFFFE8")
213 self
.StyleSetSpec(wxSTC_P_CLASSNAME
, "fore:#0000FF,bold")
214 self
.StyleSetSpec(wxSTC_P_DEFNAME
, "fore:#007F7F,bold")
215 self
.StyleSetSpec(wxSTC_P_OPERATOR
, "")
216 self
.StyleSetSpec(wxSTC_P_IDENTIFIER
, "")
217 self
.StyleSetSpec(wxSTC_P_COMMENTBLOCK
, "fore:#7F7F7F")
218 self
.StyleSetSpec(wxSTC_P_STRINGEOL
, "fore:#000000,face:%(mono)s,back:#E0C0E0,eolfilled" % faces
)
220 def OnKeyDown(self
, event
):
221 """Key down event handler.
223 The main goal here is to not allow modifications to previous
225 key
= event
.KeyCode()
226 currpos
= self
.GetCurrentPos()
227 stoppos
= self
.promptPos
[1]
228 # If the auto-complete window is up let it do its thing.
229 if self
.AutoCompActive():
231 # Control+UpArrow steps up through the history.
232 elif key
== WXK_UP
and event
.ControlDown() \
233 and self
.historyPos
< len(self
.history
) - 1:
234 # Move to the end of the buffer.
235 endpos
= self
.GetTextLength()
236 self
.SetCurrentPos(endpos
)
237 # The first Control+Up stores the current command;
238 # Control+Down brings it back.
239 if self
.historyPos
== -1:
240 self
.tempCommand
= self
.getCommand()
241 # Now replace the current line with the next one from the history.
242 self
.historyPos
= self
.historyPos
+ 1
243 self
.SetSelection(stoppos
, endpos
)
244 self
.ReplaceSelection(self
.history
[self
.historyPos
])
245 # Control+DownArrow steps down through the history.
246 elif key
== WXK_DOWN
and event
.ControlDown():
247 # Move to the end of the buffer.
248 endpos
= self
.GetTextLength()
249 self
.SetCurrentPos(endpos
)
250 # Are we at the bottom end of the history?
251 if self
.historyPos
== -1:
252 # Do we have a lastCommandRecalled stored?
253 if self
.lastCommandRecalled
>= 0:
254 # Replace the current line with the command after the
255 # last-recalled command (you'd think there should be a +1
256 # here but there isn't because the history was shuffled up
257 # by 1 after the previous command was recalled).
258 self
.SetSelection(stoppos
, endpos
)
259 self
.ReplaceSelection(self
.history
[self
.lastCommandRecalled
])
260 # We've now warped into middle of the history.
261 self
.historyPos
= self
.lastCommandRecalled
262 self
.lastCommandRecalled
= -1
264 # Fetch either the previous line from the history, or the saved
265 # command if we're back at the start.
266 self
.historyPos
= self
.historyPos
- 1
267 if self
.historyPos
== -1:
268 newText
= self
.tempCommand
270 newText
= self
.history
[self
.historyPos
]
271 # Replace the current line with the new text.
272 self
.SetSelection(stoppos
, endpos
)
273 self
.ReplaceSelection(newText
)
274 # F8 on the last line does a search up the history for the text in
275 # front of the cursor.
276 elif key
== WXK_F8
and self
.GetCurrentLine() == self
.GetLineCount()-1:
277 tempCommand
= self
.getCommand()
278 # The first F8 saves the current command, just like Control+Up.
279 if self
.historyPos
== -1:
280 self
.tempCommand
= tempCommand
281 # The text up to the cursor is what we search for.
282 searchText
= tempCommand
283 numCharsAfterCursor
= self
.GetTextLength() - self
.GetCurrentPos()
284 if numCharsAfterCursor
> 0:
285 searchText
= searchText
[:-numCharsAfterCursor
]
286 # Search upwards from the current history position and loop back
287 # to the beginning if we don't find anything.
288 for i
in range(self
.historyPos
+1, len(self
.history
)) + \
289 range(self
.historyPos
):
290 command
= self
.history
[i
]
291 if command
[:len(searchText
)] == searchText
:
292 # Replace the current line with the one we've found.
293 endpos
= self
.GetTextLength()
294 self
.SetSelection(stoppos
, endpos
)
295 self
.ReplaceSelection(command
)
296 # Put the cursor back at the end of the search text.
297 pos
= self
.GetTextLength() - len(command
) + len(searchText
)
298 self
.SetCurrentPos(pos
)
300 # We've now warped into middle of the history.
302 self
.lastCommandRecalled
= -1
304 # Return is used to submit a command to the interpreter.
305 elif key
== WXK_RETURN
:
306 if self
.CallTipActive
: self
.CallTipCancel()
308 # Home needs to be aware of the prompt.
309 elif key
== WXK_HOME
:
310 if currpos
>= stoppos
:
311 self
.SetCurrentPos(stoppos
)
312 self
.SetAnchor(stoppos
)
315 # Basic navigation keys should work anywhere.
316 elif key
in (WXK_END
, WXK_LEFT
, WXK_RIGHT
, WXK_UP
, WXK_DOWN
, \
317 WXK_PRIOR
, WXK_NEXT
):
319 # Don't backspace over the latest prompt.
320 elif key
== WXK_BACK
:
321 if currpos
> stoppos
:
323 # Only allow these keys after the latest prompt.
324 elif key
in (WXK_TAB
, WXK_DELETE
):
325 if currpos
>= stoppos
:
327 # Don't toggle between insert mode and overwrite mode.
328 elif key
== WXK_INSERT
:
333 def OnChar(self
, event
):
334 """Keypress event handler.
336 The main goal here is to not allow modifications to previous
338 key
= event
.KeyCode()
339 currpos
= self
.GetCurrentPos()
340 stoppos
= self
.promptPos
[1]
341 if currpos
>= stoppos
:
343 # "." The dot or period key activates auto completion.
344 # Get the command between the prompt and the cursor.
345 # Add a dot to the end of the command.
346 command
= self
.GetTextRange(stoppos
, currpos
) + '.'
348 if self
.autoComplete
: self
.autoCompleteShow(command
)
350 # "(" The left paren activates a call tip and cancels
351 # an active auto completion.
352 if self
.AutoCompActive(): self
.AutoCompCancel()
353 # Get the command between the prompt and the cursor.
354 # Add the '(' to the end of the command.
355 command
= self
.GetTextRange(stoppos
, currpos
) + '('
357 if self
.autoCallTip
: self
.autoCallTipShow(command
)
359 # Allow the normal event handling to take place.
364 def setStatusText(self
, text
):
365 """Display status information."""
367 # This method will most likely be replaced by the enclosing app
368 # to do something more interesting, like write to a status bar.
371 def processLine(self
):
372 """Process the line of text at which the user hit Enter."""
374 # The user hit ENTER and we need to decide what to do. They could be
375 # sitting on any line in the shell.
377 # Grab information about the current line.
378 thepos
= self
.GetCurrentPos()
379 theline
= self
.GetCurrentLine()
380 command
= self
.getCommand()
381 # Go to the very bottom of the text.
382 endpos
= self
.GetTextLength()
383 self
.SetCurrentPos(endpos
)
384 endline
= self
.GetCurrentLine()
385 # If they hit RETURN on the last line, execute the command.
386 if theline
== endline
:
387 # Store the last-recalled command; see the main comment for
388 # self.lastCommandRecalled.
390 self
.lastCommandRecalled
= self
.historyPos
391 # Reset the history position.
393 # Insert this command into the history, unless it's a blank line
394 # or the same as the last command.
396 and (len(self
.history
) == 0 or command
!= self
.history
[0]):
397 self
.history
.insert(0, command
)
399 # Otherwise, replace the last line with the new line.
401 # If the new line contains a command (even an invalid one).
403 startpos
= self
.PositionFromLine(endline
)
404 self
.SetSelection(startpos
, endpos
)
405 self
.ReplaceSelection('')
408 # Otherwise, put the cursor back where we started.
410 self
.SetCurrentPos(thepos
)
411 self
.SetAnchor(thepos
)
413 def getCommand(self
, text
=None):
414 """Extract a command from text which may include a shell prompt.
416 The command may not necessarily be valid Python syntax."""
418 text
= self
.GetCurLine()[0]
419 ## This is a hack due to a bug in the wxPython 2.3.2 beta. The following
420 ## two lines of code should go away once the bug has been fixed and the
421 ## line above should be restored.
423 ## text = self.GetCurLine()[0][:-1]
424 # XXX Need to extract real prompts here. Need to keep track of the
425 # prompt every time a command is issued.
431 # Strip the prompt off the front of text leaving just the command.
432 if text
[:ps1size
] == ps1
:
433 command
= text
[ps1size
:]
434 elif text
[:ps2size
] == ps2
:
435 command
= text
[ps2size
:]
440 def push(self
, command
):
441 """Send command to the interpreter for execution."""
442 self
.write(os
.linesep
)
443 self
.more
= self
.interp
.push(command
)
445 # Keep the undo feature from undoing previous responses. The only
446 # thing that can be undone is stuff typed after the prompt, before
447 # hitting enter. After they hit enter it becomes permanent.
448 self
.EmptyUndoBuffer()
450 def write(self
, text
):
451 """Display text in the shell.
453 Replace line endings with OS-specific endings."""
454 lines
= text
.split('\r\n')
455 for l
in range(len(lines
)):
456 chunks
= lines
[l
].split('\r')
457 for c
in range(len(chunks
)):
458 chunks
[c
] = os
.linesep
.join(chunks
[c
].split('\n'))
459 lines
[l
] = os
.linesep
.join(chunks
)
460 text
= os
.linesep
.join(lines
)
462 self
.EnsureCaretVisible()
463 #self.ScrollToColumn(0)
466 """Display appropriate prompt for the context, either ps1 or ps2.
468 If this is a continuation line, autoindent as necessary."""
470 prompt
= str(sys
.ps2
)
472 prompt
= str(sys
.ps1
)
473 pos
= self
.GetCurLine()[1]
474 if pos
> 0: self
.write(os
.linesep
)
475 self
.promptPos
[0] = self
.GetCurrentPos()
477 self
.promptPos
[1] = self
.GetCurrentPos()
478 # XXX Add some autoindent magic here if more.
480 self
.write('\t') # Temporary hack indentation.
481 self
.EnsureCaretVisible()
482 self
.ScrollToColumn(0)
485 """Replacement for stdin."""
486 prompt
= 'Please enter your response:'
487 dialog
= wxTextEntryDialog(None, prompt
, \
488 'Input Dialog (Standard)', '')
490 if dialog
.ShowModal() == wxID_OK
:
491 text
= dialog
.GetValue()
492 self
.write(text
+ os
.linesep
)
498 def readRaw(self
, prompt
='Please enter your response:'):
499 """Replacement for raw_input."""
500 dialog
= wxTextEntryDialog(None, prompt
, \
501 'Input Dialog (Raw)', '')
503 if dialog
.ShowModal() == wxID_OK
:
504 text
= dialog
.GetValue()
510 def ask(self
, prompt
='Please enter your response:'):
511 """Get response from the user."""
512 return raw_input(prompt
=prompt
)
515 """Halt execution pending a response from the user."""
516 self
.ask('Press enter to continue:')
519 """Delete all text from the shell."""
522 def run(self
, command
, prompt
=1, verbose
=1):
523 """Execute command within the shell as if it was typed in directly.
524 >>> shell.run('print "this"')
529 command
= command
.rstrip()
530 if prompt
: self
.prompt()
531 if verbose
: self
.write(command
)
534 def runfile(self
, filename
):
535 """Execute all commands in file as if they were typed into the shell."""
536 file = open(filename
)
539 for command
in file.readlines():
540 if command
[:6] == 'shell.': # Run shell methods silently.
541 self
.run(command
, prompt
=0, verbose
=0)
543 self
.run(command
, prompt
=0, verbose
=1)
547 def autoCompleteShow(self
, command
):
548 """Display auto-completion popup list."""
549 list = self
.interp
.getAutoCompleteList(command
, \
550 includeMagic
=self
.autoCompleteIncludeMagic
, \
551 includeSingle
=self
.autoCompleteIncludeSingle
, \
552 includeDouble
=self
.autoCompleteIncludeDouble
)
554 options
= ' '.join(list)
556 self
.AutoCompShow(offset
, options
)
558 def autoCallTipShow(self
, command
):
559 """Display argument spec and docstring in a popup bubble thingie."""
560 if self
.CallTipActive
: self
.CallTipCancel()
561 tip
= self
.interp
.getCallTip(command
)
563 offset
= self
.GetCurrentPos()
564 self
.CallTipShow(offset
, tip
)
566 def writeOut(self
, text
):
567 """Replacement for stdout."""
570 def writeErr(self
, text
):
571 """Replacement for stderr."""
575 """Return true if text is selected and can be cut."""
576 return self
.GetSelectionStart() != self
.GetSelectionEnd()
579 """Return true if text is selected and can be copied."""
580 return self
.GetSelectionStart() != self
.GetSelectionEnd()
583 wxID_SELECTALL
= NewId() # This *should* be defined by wxPython.
584 ID_AUTOCOMP
= NewId()
585 ID_AUTOCOMP_SHOW
= NewId()
586 ID_AUTOCOMP_INCLUDE_MAGIC
= NewId()
587 ID_AUTOCOMP_INCLUDE_SINGLE
= NewId()
588 ID_AUTOCOMP_INCLUDE_DOUBLE
= NewId()
589 ID_CALLTIPS
= NewId()
590 ID_CALLTIPS_SHOW
= NewId()
593 class ShellFrame(wxFrame
):
594 """Frame containing the PyCrust shell component."""
596 name
= 'PyCrust Shell Frame'
597 revision
= __version__
599 def __init__(self
, parent
=None, id=-1, title
='PyShell', \
600 pos
=wxDefaultPosition
, size
=wxDefaultSize
, \
601 style
=wxDEFAULT_FRAME_STYLE
, locals=None, \
602 InterpClass
=None, *args
, **kwds
):
603 """Create a PyCrust ShellFrame instance."""
604 wxFrame
.__init
__(self
, parent
, id, title
, pos
, size
, style
)
605 intro
= 'Welcome To PyCrust %s - The Flakiest Python Shell' % VERSION
606 self
.CreateStatusBar()
607 self
.SetStatusText(intro
)
608 if wxPlatform
== '__WXMSW__':
609 icon
= wxIcon('PyCrust.ico', wxBITMAP_TYPE_ICO
)
612 self
.shell
= Shell(parent
=self
, id=-1, introText
=intro
, \
613 locals=locals, InterpClass
=InterpClass
, \
615 # Override the shell so that status messages go to the status bar.
616 self
.shell
.setStatusText
= self
.SetStatusText
618 def createMenus(self
):
619 m
= self
.fileMenu
= wxMenu()
621 m
.Append(wxID_EXIT
, 'E&xit', 'Exit PyCrust')
623 m
= self
.editMenu
= wxMenu()
624 m
.Append(wxID_UNDO
, '&Undo \tCtrl+Z', 'Undo the last action')
625 m
.Append(wxID_REDO
, '&Redo \tCtrl+Y', 'Redo the last undone action')
627 m
.Append(wxID_CUT
, 'Cu&t \tCtrl+X', 'Cut the selection')
628 m
.Append(wxID_COPY
, '&Copy \tCtrl+C', 'Copy the selection')
629 m
.Append(wxID_PASTE
, '&Paste \tCtrl+V', 'Paste')
631 m
.Append(wxID_CLEAR
, 'Cle&ar \tDel', 'Delete the selection')
632 m
.Append(wxID_SELECTALL
, 'Select A&ll \tCtrl+A', 'Select all text')
634 m
= self
.autocompMenu
= wxMenu()
635 m
.Append(ID_AUTOCOMP_SHOW
, 'Show Auto Completion', \
636 'Show auto completion during dot syntax', \
638 m
.Append(ID_AUTOCOMP_INCLUDE_MAGIC
, 'Include Magic Attributes', \
639 'Include attributes visible to __getattr__ and __setattr__', \
641 m
.Append(ID_AUTOCOMP_INCLUDE_SINGLE
, 'Include Single Underscores', \
642 'Include attibutes prefixed by a single underscore', \
644 m
.Append(ID_AUTOCOMP_INCLUDE_DOUBLE
, 'Include Double Underscores', \
645 'Include attibutes prefixed by a double underscore', \
648 m
= self
.calltipsMenu
= wxMenu()
649 m
.Append(ID_CALLTIPS_SHOW
, 'Show Call Tips', \
650 'Show call tips with argument specifications', checkable
=1)
652 m
= self
.optionsMenu
= wxMenu()
653 m
.AppendMenu(ID_AUTOCOMP
, '&Auto Completion', self
.autocompMenu
, \
654 'Auto Completion Options')
655 m
.AppendMenu(ID_CALLTIPS
, '&Call Tips', self
.calltipsMenu
, \
658 m
= self
.helpMenu
= wxMenu()
660 m
.Append(wxID_ABOUT
, '&About...', 'About PyCrust')
662 b
= self
.menuBar
= wxMenuBar()
663 b
.Append(self
.fileMenu
, '&File')
664 b
.Append(self
.editMenu
, '&Edit')
665 b
.Append(self
.optionsMenu
, '&Options')
666 b
.Append(self
.helpMenu
, '&Help')
669 EVT_MENU(self
, wxID_EXIT
, self
.OnExit
)
670 EVT_MENU(self
, wxID_UNDO
, self
.OnUndo
)
671 EVT_MENU(self
, wxID_REDO
, self
.OnRedo
)
672 EVT_MENU(self
, wxID_CUT
, self
.OnCut
)
673 EVT_MENU(self
, wxID_COPY
, self
.OnCopy
)
674 EVT_MENU(self
, wxID_PASTE
, self
.OnPaste
)
675 EVT_MENU(self
, wxID_CLEAR
, self
.OnClear
)
676 EVT_MENU(self
, wxID_SELECTALL
, self
.OnSelectAll
)
677 EVT_MENU(self
, wxID_ABOUT
, self
.OnAbout
)
678 EVT_MENU(self
, ID_AUTOCOMP_SHOW
, \
679 self
.OnAutoCompleteShow
)
680 EVT_MENU(self
, ID_AUTOCOMP_INCLUDE_MAGIC
, \
681 self
.OnAutoCompleteIncludeMagic
)
682 EVT_MENU(self
, ID_AUTOCOMP_INCLUDE_SINGLE
, \
683 self
.OnAutoCompleteIncludeSingle
)
684 EVT_MENU(self
, ID_AUTOCOMP_INCLUDE_DOUBLE
, \
685 self
.OnAutoCompleteIncludeDouble
)
686 EVT_MENU(self
, ID_CALLTIPS_SHOW
, \
689 EVT_UPDATE_UI(self
, wxID_UNDO
, self
.OnUpdateMenu
)
690 EVT_UPDATE_UI(self
, wxID_REDO
, self
.OnUpdateMenu
)
691 EVT_UPDATE_UI(self
, wxID_CUT
, self
.OnUpdateMenu
)
692 EVT_UPDATE_UI(self
, wxID_COPY
, self
.OnUpdateMenu
)
693 EVT_UPDATE_UI(self
, wxID_PASTE
, self
.OnUpdateMenu
)
694 EVT_UPDATE_UI(self
, wxID_CLEAR
, self
.OnUpdateMenu
)
695 EVT_UPDATE_UI(self
, ID_AUTOCOMP_SHOW
, self
.OnUpdateMenu
)
696 EVT_UPDATE_UI(self
, ID_AUTOCOMP_INCLUDE_MAGIC
, self
.OnUpdateMenu
)
697 EVT_UPDATE_UI(self
, ID_AUTOCOMP_INCLUDE_SINGLE
, self
.OnUpdateMenu
)
698 EVT_UPDATE_UI(self
, ID_AUTOCOMP_INCLUDE_DOUBLE
, self
.OnUpdateMenu
)
699 EVT_UPDATE_UI(self
, ID_CALLTIPS_SHOW
, self
.OnUpdateMenu
)
701 def OnExit(self
, event
):
704 def OnUndo(self
, event
):
707 def OnRedo(self
, event
):
710 def OnCut(self
, event
):
713 def OnCopy(self
, event
):
716 def OnPaste(self
, event
):
719 def OnClear(self
, event
):
722 def OnSelectAll(self
, event
):
723 self
.shell
.SelectAll()
725 def OnAbout(self
, event
):
726 """Display an About PyCrust window."""
728 title
= 'About PyCrust'
729 text
= 'PyCrust %s\n\n' % VERSION
+ \
730 'Yet another Python shell, only flakier.\n\n' + \
731 'Half-baked by Patrick K. O\'Brien,\n' + \
732 'the other half is still in the oven.\n\n' + \
733 'Shell Revision: %s\n' % self
.shell
.revision
+ \
734 'Interpreter Revision: %s\n\n' % self
.shell
.interp
.revision
+ \
735 'Python Version: %s\n' % sys
.version
.split()[0] + \
736 'wxPython Version: %s\n' % wx
.__version
__ + \
737 'Platform: %s\n' % sys
.platform
738 dialog
= wxMessageDialog(self
, text
, title
, wxOK | wxICON_INFORMATION
)
742 def OnAutoCompleteShow(self
, event
):
743 self
.shell
.autoComplete
= event
.IsChecked()
745 def OnAutoCompleteIncludeMagic(self
, event
):
746 self
.shell
.autoCompleteIncludeMagic
= event
.IsChecked()
748 def OnAutoCompleteIncludeSingle(self
, event
):
749 self
.shell
.autoCompleteIncludeSingle
= event
.IsChecked()
751 def OnAutoCompleteIncludeDouble(self
, event
):
752 self
.shell
.autoCompleteIncludeDouble
= event
.IsChecked()
754 def OnCallTipsShow(self
, event
):
755 self
.shell
.autoCallTip
= event
.IsChecked()
757 def OnUpdateMenu(self
, event
):
758 """Update menu items based on current status."""
761 event
.Enable(self
.shell
.CanUndo())
762 elif id == wxID_REDO
:
763 event
.Enable(self
.shell
.CanRedo())
765 event
.Enable(self
.shell
.CanCut())
766 elif id == wxID_COPY
:
767 event
.Enable(self
.shell
.CanCopy())
768 elif id == wxID_PASTE
:
769 event
.Enable(self
.shell
.CanPaste())
770 elif id == wxID_CLEAR
:
771 event
.Enable(self
.shell
.CanCut())
772 elif id == ID_AUTOCOMP_SHOW
:
773 event
.Check(self
.shell
.autoComplete
)
774 elif id == ID_AUTOCOMP_INCLUDE_MAGIC
:
775 event
.Check(self
.shell
.autoCompleteIncludeMagic
)
776 elif id == ID_AUTOCOMP_INCLUDE_SINGLE
:
777 event
.Check(self
.shell
.autoCompleteIncludeSingle
)
778 elif id == ID_AUTOCOMP_INCLUDE_DOUBLE
:
779 event
.Check(self
.shell
.autoCompleteIncludeDouble
)
780 elif id == ID_CALLTIPS_SHOW
:
781 event
.Check(self
.shell
.autoCallTip
)