📄 shell.py
字号:
"""Shell is an interactive text control in which a user types in
commands to be sent to the interpreter. This particular shell is
based on wxPython's wxStyledTextCtrl.
Sponsored by Orbtech - Your source for Python programming expertise."""
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id: shell.py,v 1.18 2006/06/11 00:12:52 RD Exp $"
__revision__ = "$Revision: 1.18 $"[11:-2]
import wx
from wx import stc
import keyword
import os
import sys
import time
from buffer import Buffer
import dispatcher
import editwindow
import frame
from pseudo import PseudoFileIn
from pseudo import PseudoFileOut
from pseudo import PseudoFileErr
from version import VERSION
sys.ps3 = '<-- ' # Input prompt.
NAVKEYS = (wx.WXK_END, wx.WXK_LEFT, wx.WXK_RIGHT,
wx.WXK_UP, wx.WXK_DOWN, wx.WXK_PRIOR, wx.WXK_NEXT)
class ShellFrame(frame.Frame, frame.ShellFrameMixin):
"""Frame containing the shell component."""
name = 'Shell Frame'
revision = __revision__
def __init__(self, parent=None, id=-1, title='PyShell',
pos=wx.DefaultPosition, size=wx.DefaultSize,
style=wx.DEFAULT_FRAME_STYLE, locals=None,
InterpClass=None,
config=None, dataDir=None,
*args, **kwds):
"""Create ShellFrame instance."""
frame.Frame.__init__(self, parent, id, title, pos, size, style)
frame.ShellFrameMixin.__init__(self, config, dataDir)
if size == wx.DefaultSize:
self.SetSize((750, 525))
intro = 'PyShell %s - The Flakiest Python Shell' % VERSION
self.SetStatusText(intro.replace('\n', ', '))
self.shell = Shell(parent=self, id=-1, introText=intro,
locals=locals, InterpClass=InterpClass,
startupScript=self.startupScript,
execStartupScript=self.execStartupScript,
*args, **kwds)
# Override the shell so that status messages go to the status bar.
self.shell.setStatusText = self.SetStatusText
self.shell.SetFocus()
self.LoadSettings()
def OnClose(self, event):
"""Event handler for closing."""
# This isn't working the way I want, but I'll leave it for now.
if self.shell.waiting:
if event.CanVeto():
event.Veto(True)
else:
self.SaveSettings()
self.shell.destroy()
self.Destroy()
def OnAbout(self, event):
"""Display an About window."""
title = 'About PyShell'
text = 'PyShell %s\n\n' % VERSION + \
'Yet another Python shell, only flakier.\n\n' + \
'Half-baked by Patrick K. O\'Brien,\n' + \
'the other half is still in the oven.\n\n' + \
'Shell Revision: %s\n' % self.shell.revision + \
'Interpreter Revision: %s\n\n' % self.shell.interp.revision + \
'Platform: %s\n' % sys.platform + \
'Python Version: %s\n' % sys.version.split()[0] + \
'wxPython Version: %s\n' % wx.VERSION_STRING + \
('\t(%s)\n' % ", ".join(wx.PlatformInfo[1:]))
dialog = wx.MessageDialog(self, text, title,
wx.OK | wx.ICON_INFORMATION)
dialog.ShowModal()
dialog.Destroy()
def OnHelp(self, event):
"""Show a help dialog."""
frame.ShellFrameMixin.OnHelp(self, event)
def LoadSettings(self):
if self.config is not None:
frame.ShellFrameMixin.LoadSettings(self)
frame.Frame.LoadSettings(self, self.config)
self.shell.LoadSettings(self.config)
def SaveSettings(self, force=False):
if self.config is not None:
frame.ShellFrameMixin.SaveSettings(self)
if self.autoSaveSettings or force:
frame.Frame.SaveSettings(self, self.config)
self.shell.SaveSettings(self.config)
def DoSaveSettings(self):
if self.config is not None:
self.SaveSettings(force=True)
self.config.Flush()
HELP_TEXT = """\
* Key bindings:
Home Go to the beginning of the command or line.
Shift+Home Select to the beginning of the command or line.
Shift+End Select to the end of the line.
End Go to the end of the line.
Ctrl+C Copy selected text, removing prompts.
Ctrl+Shift+C Copy selected text, retaining prompts.
Alt+C Copy to the clipboard, including prefixed prompts.
Ctrl+X Cut selected text.
Ctrl+V Paste from clipboard.
Ctrl+Shift+V Paste and run multiple commands from clipboard.
Ctrl+Up Arrow Retrieve Previous History item.
Alt+P Retrieve Previous History item.
Ctrl+Down Arrow Retrieve Next History item.
Alt+N Retrieve Next History item.
Shift+Up Arrow Insert Previous History item.
Shift+Down Arrow Insert Next History item.
F8 Command-completion of History item.
(Type a few characters of a previous command and press F8.)
Ctrl+Enter Insert new line into multiline command.
Ctrl+] Increase font size.
Ctrl+[ Decrease font size.
Ctrl+= Default font size.
Ctrl-Space Show Auto Completion.
Ctrl-Alt-Space Show Call Tip.
Shift+Enter Complete Text from History.
Ctrl+F Search
F3 Search next
Ctrl+H "hide" lines containing selection / "unhide"
F12 on/off "free-edit" mode
"""
class ShellFacade:
"""Simplified interface to all shell-related functionality.
This is a semi-transparent facade, in that all attributes of other
are accessible, even though only some are visible to the user."""
name = 'Shell Interface'
revision = __revision__
def __init__(self, other):
"""Create a ShellFacade instance."""
d = self.__dict__
d['other'] = other
d['helpText'] = HELP_TEXT
d['this'] = other.this
def help(self):
"""Display some useful information about how to use the shell."""
self.write(self.helpText)
def __getattr__(self, name):
if hasattr(self.other, name):
return getattr(self.other, name)
else:
raise AttributeError, name
def __setattr__(self, name, value):
if self.__dict__.has_key(name):
self.__dict__[name] = value
elif hasattr(self.other, name):
setattr(self.other, name, value)
else:
raise AttributeError, name
def _getAttributeNames(self):
"""Return list of magic attributes to extend introspection."""
list = [
'about',
'ask',
'autoCallTip',
'autoComplete',
'autoCompleteAutoHide',
'autoCompleteCaseInsensitive',
'autoCompleteIncludeDouble',
'autoCompleteIncludeMagic',
'autoCompleteIncludeSingle',
'callTipInsert',
'clear',
'pause',
'prompt',
'quit',
'redirectStderr',
'redirectStdin',
'redirectStdout',
'run',
'runfile',
'wrap',
'zoom',
]
list.sort()
return list
class Shell(editwindow.EditWindow):
"""Shell based on StyledTextCtrl."""
name = 'Shell'
revision = __revision__
def __init__(self, parent, id=-1, pos=wx.DefaultPosition,
size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
introText='', locals=None, InterpClass=None,
startupScript=None, execStartupScript=True,
*args, **kwds):
"""Create Shell instance."""
editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
self.wrap()
if locals is None:
import __main__
locals = __main__.__dict__
# Grab these so they can be restored by self.redirect* methods.
self.stdin = sys.stdin
self.stdout = sys.stdout
self.stderr = sys.stderr
# Import a default interpreter class if one isn't provided.
if InterpClass == None:
from interpreter import Interpreter
else:
Interpreter = InterpClass
# Create a replacement for stdin.
self.reader = PseudoFileIn(self.readline, self.readlines)
self.reader.input = ''
self.reader.isreading = False
# Set up the interpreter.
self.interp = Interpreter(locals=locals,
rawin=self.raw_input,
stdin=self.reader,
stdout=PseudoFileOut(self.writeOut),
stderr=PseudoFileErr(self.writeErr),
*args, **kwds)
# Set up the buffer.
self.buffer = Buffer()
# Find out for which keycodes the interpreter will autocomplete.
self.autoCompleteKeys = self.interp.getAutoCompleteKeys()
# Keep track of the last non-continuation prompt positions.
self.promptPosStart = 0
self.promptPosEnd = 0
# Keep track of multi-line commands.
self.more = False
# Create the command history. Commands are added into the
# front of the list (ie. at index 0) as they are entered.
# self.historyIndex is the current position in the history; it
# gets incremented as you retrieve the previous command,
# decremented as you retrieve the next, and reset when you hit
# Enter. self.historyIndex == -1 means you're on the current
# command, not in the history.
self.history = []
self.historyIndex = -1
#seb add mode for "free edit"
self.noteMode = 0
self.MarkerDefine(0,stc.STC_MARK_ROUNDRECT) # marker for hidden
self.searchTxt = ""
# Assign handlers for keyboard events.
self.Bind(wx.EVT_CHAR, self.OnChar)
self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
# Assign handler for idle time.
self.waiting = False
self.Bind(wx.EVT_IDLE, self.OnIdle)
# Display the introductory banner information.
self.showIntro(introText)
# Assign some pseudo keywords to the interpreter's namespace.
self.setBuiltinKeywords()
# Add 'shell' to the interpreter's local namespace.
self.setLocalShell()
## NOTE: See note at bottom of this file...
## #seb: File drag and drop
## self.SetDropTarget( FileDropTarget(self) )
# Do this last so the user has complete control over their
# environment. They can override anything they want.
if execStartupScript:
if startupScript is None:
startupScript = os.environ.get('PYTHONSTARTUP')
self.execStartupScript(startupScript)
else:
self.prompt()
wx.CallAfter(self.ScrollToLine, 0)
def clearHistory(self):
self.history = []
self.historyIndex = -1
dispatcher.send(signal="Shell.clearHistory")
def destroy(self):
del self.interp
def setFocus(self):
"""Set focus to the shell."""
self.SetFocus()
def OnIdle(self, event):
"""Free the CPU to do other things."""
if self.waiting:
time.sleep(0.05)
event.Skip()
def showIntro(self, text=''):
"""Display introductory text in the shell."""
if text:
if not text.endswith(os.linesep):
text += os.linesep
self.write(text)
try:
self.write(self.interp.introText)
except AttributeError:
pass
def setBuiltinKeywords(self):
"""Create pseudo keywords as part of builtins.
This sets "close", "exit" and "quit" to a helpful string.
"""
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -