Updated PyCrust code from Patrick

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@16755 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Robin Dunn
2002-08-24 21:44:57 +00:00
parent 54f07ec450
commit d8a2af5091
4 changed files with 142 additions and 75 deletions

View File

@@ -8,6 +8,7 @@ from wxPython.wx import *
from shell import Shell from shell import Shell
from filling import Filling from filling import Filling
from version import VERSION from version import VERSION
import os
class Crust(wxSplitterWindow): class Crust(wxSplitterWindow):
@@ -55,12 +56,9 @@ class CrustFrame(wxFrame, ShellMenu):
intro += '\nSponsored by Orbtech - Your source for Python programming expertise.' intro += '\nSponsored by Orbtech - Your source for Python programming expertise.'
self.CreateStatusBar() self.CreateStatusBar()
self.SetStatusText(intro.replace('\n', ', ')) self.SetStatusText(intro.replace('\n', ', '))
import os
filename = os.path.join(os.path.dirname(__file__), 'PyCrust.ico') filename = os.path.join(os.path.dirname(__file__), 'PyCrust.ico')
icon = wxIcon(filename, wxBITMAP_TYPE_ICO) icon = wxIcon(filename, wxBITMAP_TYPE_ICO)
self.SetIcon(icon) self.SetIcon(icon)
self.crust = Crust(parent=self, intro=intro, \ self.crust = Crust(parent=self, intro=intro, \
rootObject=rootObject, \ rootObject=rootObject, \
rootLabel=rootLabel, \ rootLabel=rootLabel, \
@@ -78,6 +76,10 @@ class CrustFrame(wxFrame, ShellMenu):
# Temporary hack to share menus between PyCrust and PyShell. # Temporary hack to share menus between PyCrust and PyShell.
self.shell = self.crust.shell self.shell = self.crust.shell
self.createMenus() self.createMenus()
EVT_CLOSE(self, self.OnCloseWindow)
def OnCloseWindow(self, event):
self.crust.shell.destroy()
self.Destroy()

View File

@@ -14,6 +14,14 @@ import keyword
import sys import sys
import types import types
COMMONTYPES = [getattr(types, t) for t in dir(types) \
if not t.startswith('_') \
and t not in ('ClassType', 'InstanceType', 'ModuleType')]
try:
COMMONTYPES.append(type(''.__repr__)) # Method-wrapper in version 2.2.x.
except AttributeError:
pass
class FillingTree(wxTreeCtrl): class FillingTree(wxTreeCtrl):
"""PyCrust FillingTree based on wxTreeCtrl.""" """PyCrust FillingTree based on wxTreeCtrl."""
@@ -39,40 +47,43 @@ class FillingTree(wxTreeCtrl):
EVT_TREE_ITEM_COLLAPSED(self, self.GetId(), self.OnItemCollapsed) EVT_TREE_ITEM_COLLAPSED(self, self.GetId(), self.OnItemCollapsed)
EVT_TREE_SEL_CHANGED(self, self.GetId(), self.OnSelChanged) EVT_TREE_SEL_CHANGED(self, self.GetId(), self.OnSelChanged)
def hasChildren(self, object): def hasChildren(self, o):
"""Return true if object has children.""" """Return true if object has children."""
if self.getChildren(object): if self.getChildren(o):
return true return true
else: else:
return false return false
def getChildren(self, object): def getChildren(self, o):
"""Return a dictionary with the attributes or contents of object.""" """Return a dictionary with the attributes or contents of object."""
dict = {} busy = wxBusyCursor()
objtype = type(object) otype = type(o)
if (objtype is types.DictType) \ if (otype is types.DictType) \
or str(objtype)[17:23] == 'BTrees' and hasattr(object, 'keys'): or str(otype)[17:23] == 'BTrees' and hasattr(o, 'keys'):
dict = object return o
elif (objtype in (types.ClassType, \ d = {}
types.InstanceType, \ if otype is types.ListType:
types.ModuleType)) \ for n in range(len(o)):
or str(objtype)[1:10] == 'extension': key = '[' + str(n) + ']'
for key in introspect.getAttributeNames(object): d[key] = o[n]
if otype not in COMMONTYPES:
for key in introspect.getAttributeNames(o):
# Believe it or not, some attributes can disappear, such as # Believe it or not, some attributes can disappear, such as
# the exc_traceback attribute of the sys module. So this is # the exc_traceback attribute of the sys module. So this is
# nested in a try block. # nested in a try block.
try: try:
dict[key] = getattr(object, key) d[key] = getattr(o, key)
except: except:
pass pass
return dict return d
def OnItemExpanding(self, event): def OnItemExpanding(self, event):
busy = wxBusyCursor()
selection = event.GetItem() selection = event.GetItem()
if self.IsExpanded(selection): if self.IsExpanded(selection):
return return
object = self.GetPyData(selection) o = self.GetPyData(selection)
children = self.getChildren(object) children = self.getChildren(o)
if not children: if not children:
return return
list = children.keys() list = children.keys()
@@ -84,7 +95,7 @@ class FillingTree(wxTreeCtrl):
itemtext = str(item) itemtext = str(item)
# Show string dictionary items with single quotes, except for # Show string dictionary items with single quotes, except for
# the first level of items, if they represent a namespace. # the first level of items, if they represent a namespace.
if type(object) is types.DictType \ if type(o) is types.DictType \
and type(item) is types.StringType \ and type(item) is types.StringType \
and (selection != self.root \ and (selection != self.root \
or (selection == self.root and not self.rootIsNamespace)): or (selection == self.root and not self.rootIsNamespace)):
@@ -95,42 +106,44 @@ class FillingTree(wxTreeCtrl):
def OnItemCollapsed(self, event): def OnItemCollapsed(self, event):
"""Remove all children from the item.""" """Remove all children from the item."""
busy = wxBusyCursor()
item = event.GetItem() item = event.GetItem()
self.DeleteChildren(item) self.DeleteChildren(item)
def OnSelChanged(self, event): def OnSelChanged(self, event):
busy = wxBusyCursor()
item = event.GetItem() item = event.GetItem()
if item == self.root: if item == self.root:
self.setText('') self.setText('')
return return
object = self.GetPyData(item) o = self.GetPyData(item)
otype = type(object) otype = type(o)
text = '' text = ''
text += self.getFullName(item) text += self.getFullName(item)
text += '\n\nType: ' + str(otype) text += '\n\nType: ' + str(otype)
try: try:
value = str(object) value = str(o)
except: except:
value = '' value = ''
if otype is types.StringType or otype is types.UnicodeType: if otype is types.StringType or otype is types.UnicodeType:
value = repr(object) value = repr(o)
text += '\n\nValue: ' + value text += '\n\nValue: ' + value
if otype is types.InstanceType: if otype is types.InstanceType:
try: try:
text += '\n\nClass Definition:\n\n' + \ text += '\n\nClass Definition:\n\n' + \
inspect.getsource(object.__class__) inspect.getsource(o.__class__)
except: except:
try: try:
text += '\n\n"""' + inspect.getdoc(object).strip() + '"""' text += '\n\n"""' + inspect.getdoc(o).strip() + '"""'
except: except:
pass pass
else: else:
try: try:
text += '\n\nSource Code:\n\n' + \ text += '\n\nSource Code:\n\n' + \
inspect.getsource(object) inspect.getsource(o)
except: except:
try: try:
text += '\n\n"""' + inspect.getdoc(object).strip() + '"""' text += '\n\n"""' + inspect.getdoc(o).strip() + '"""'
except: except:
pass pass
self.setText(text) self.setText(text)
@@ -138,13 +151,13 @@ class FillingTree(wxTreeCtrl):
def getFullName(self, item, partial=''): def getFullName(self, item, partial=''):
"""Return a syntactically proper name for item.""" """Return a syntactically proper name for item."""
parent = self.GetItemParent(item) parent = self.GetItemParent(item)
parentobject = self.GetPyData(parent) parento = self.GetPyData(parent)
name = self.GetItemText(item) name = self.GetItemText(item)
# Apply dictionary syntax to dictionary items, except the root # Apply dictionary syntax to dictionary items, except the root
# and first level children of a namepace. # and first level children of a namepace.
if (type(parentobject) is types.DictType \ if (type(parento) is types.DictType \
or str(type(parentobject))[17:23] == 'BTrees' \ or str(type(parento))[17:23] == 'BTrees' \
and hasattr(parentobject, 'keys')) \ and hasattr(parento, 'keys')) \
and ((item != self.root and parent != self.root) \ and ((item != self.root and parent != self.root) \
or (parent == self.root and not self.rootIsNamespace)): or (parent == self.root and not self.rootIsNamespace)):
name = '[' + name + ']' name = '[' + name + ']'
@@ -228,6 +241,10 @@ class FillingText(wxStyledTextCtrl):
self.SetTabWidth(4) self.SetTabWidth(4)
self.SetUseTabs(0) self.SetUseTabs(0)
self.SetReadOnly(1) self.SetReadOnly(1)
try:
self.SetWrapMode(1)
except AttributeError:
pass
def setStyles(self, faces): def setStyles(self, faces):
"""Configure font size, typeface and color for lexer.""" """Configure font size, typeface and color for lexer."""

View File

@@ -78,6 +78,10 @@ class Interpreter(InteractiveInterpreter):
sys.stderr = stderr sys.stderr = stderr
return more return more
def getAutoCompleteKeys(self):
"""Return list of auto-completion keycodes."""
return [ord('.')]
def getAutoCompleteList(self, command='', *args, **kwds): def getAutoCompleteList(self, command='', *args, **kwds):
"""Return list of auto-completion options for a command. """Return list of auto-completion options for a command.

View File

@@ -19,6 +19,8 @@ from pseudo import PseudoFileErr
from version import VERSION from version import VERSION
NAVKEYS = (WXK_END, WXK_LEFT, WXK_RIGHT, WXK_UP, WXK_DOWN, WXK_PRIOR, WXK_NEXT)
if wxPlatform == '__WXMSW__': if wxPlatform == '__WXMSW__':
faces = { 'times' : 'Times New Roman', faces = { 'times' : 'Times New Roman',
'mono' : 'Courier New', 'mono' : 'Courier New',
@@ -66,6 +68,8 @@ class ShellFacade:
'redirectStdout', 'redirectStdout',
'run', 'run',
'runfile', 'runfile',
'wrap',
'zoom',
] ]
for method in methods: for method in methods:
self.__dict__[method] = getattr(other, method) self.__dict__[method] = getattr(other, method)
@@ -160,6 +164,8 @@ class Shell(wxStyledTextCtrl):
stdout=PseudoFileOut(self.writeOut), \ stdout=PseudoFileOut(self.writeOut), \
stderr=PseudoFileErr(self.writeErr), \ stderr=PseudoFileErr(self.writeErr), \
*args, **kwds) *args, **kwds)
# Find out for which keycodes the interpreter will autocomplete.
self.autoCompleteKeys = self.interp.getAutoCompleteKeys()
# Keep track of the last non-continuation prompt positions. # Keep track of the last non-continuation prompt positions.
self.promptPosStart = 0 self.promptPosStart = 0
self.promptPosEnd = 0 self.promptPosEnd = 0
@@ -219,6 +225,7 @@ class Shell(wxStyledTextCtrl):
# Do we want to automatically pop up command argument help? # Do we want to automatically pop up command argument help?
self.autoCallTip = 1 self.autoCallTip = 1
self.CallTipSetBackground(wxColour(255, 255, 232)) self.CallTipSetBackground(wxColour(255, 255, 232))
self.wrap()
def showIntro(self, text=''): def showIntro(self, text=''):
"""Display introductory text in the shell.""" """Display introductory text in the shell."""
@@ -301,6 +308,10 @@ class Shell(wxStyledTextCtrl):
caretPos = self.GetCurrentPos() caretPos = self.GetCurrentPos()
if caretPos > 0: if caretPos > 0:
charBefore = self.GetCharAt(caretPos - 1) charBefore = self.GetCharAt(caretPos - 1)
#*** Patch to fix bug in wxSTC for wxPython < 2.3.3.
if charBefore < 0:
charBefore = 32 # Mimic a space.
#***
styleBefore = self.GetStyleAt(caretPos - 1) styleBefore = self.GetStyleAt(caretPos - 1)
# Check before. # Check before.
@@ -311,6 +322,10 @@ class Shell(wxStyledTextCtrl):
# Check after. # Check after.
if braceAtCaret < 0: if braceAtCaret < 0:
charAfter = self.GetCharAt(caretPos) charAfter = self.GetCharAt(caretPos)
#*** Patch to fix bug in wxSTC for wxPython < 2.3.3.
if charAfter < 0:
charAfter = 32 # Mimic a space.
#***
styleAfter = self.GetStyleAt(caretPos) styleAfter = self.GetStyleAt(caretPos)
if charAfter and chr(charAfter) in '[]{}()' \ if charAfter and chr(charAfter) in '[]{}()' \
and styleAfter == wxSTC_P_OPERATOR: and styleAfter == wxSTC_P_OPERATOR:
@@ -325,20 +340,20 @@ class Shell(wxStyledTextCtrl):
self.BraceHighlight(braceAtCaret, braceOpposite) self.BraceHighlight(braceAtCaret, braceOpposite)
def OnChar(self, event): def OnChar(self, event):
"""Keypress event handler. """Keypress event handler."""
Prevents modification of previously submitted commands/responses.""" # Prevent modification of previously submitted commands/responses.
if not self.CanEdit(): if not self.CanEdit():
return return
key = event.KeyCode() key = event.KeyCode()
currpos = self.GetCurrentPos() currpos = self.GetCurrentPos()
stoppos = self.promptPosEnd stoppos = self.promptPosEnd
if key == ord('.'): if key in self.autoCompleteKeys:
# The dot or period key activates auto completion. # Usually the dot (period) key activates auto completion.
# Get the command between the prompt and the cursor. # Get the command between the prompt and the cursor.
# Add a dot to the end of the command. # Add the autocomplete character to the end of the command.
command = self.GetTextRange(stoppos, currpos) + '.' command = self.GetTextRange(stoppos, currpos) + chr(key)
self.write('.') self.write(chr(key))
if self.autoComplete: self.autoCompleteShow(command) if self.autoComplete: self.autoCompleteShow(command)
elif key == ord('('): elif key == ord('('):
# The left paren activates a call tip and cancels # The left paren activates a call tip and cancels
@@ -355,9 +370,9 @@ class Shell(wxStyledTextCtrl):
event.Skip() event.Skip()
def OnKeyDown(self, event): def OnKeyDown(self, event):
"""Key down event handler. """Key down event handler."""
Prevents modification of previously submitted commands/responses.""" # Prevent modification of previously submitted commands/responses.
key = event.KeyCode() key = event.KeyCode()
controlDown = event.ControlDown() controlDown = event.ControlDown()
altDown = event.AltDown() altDown = event.AltDown()
@@ -401,6 +416,25 @@ class Shell(wxStyledTextCtrl):
elif controlDown and shiftDown \ elif controlDown and shiftDown \
and key in (ord('C'), ord('c'), WXK_INSERT): and key in (ord('C'), ord('c'), WXK_INSERT):
self.CopyWithPrompts() self.CopyWithPrompts()
# Home needs to be aware of the prompt.
elif key == WXK_HOME:
home = self.promptPosEnd
if currpos > home:
selecting = self.GetSelectionStart() != self.GetSelectionEnd()
self.SetCurrentPos(home)
if not selecting and not shiftDown:
self.SetAnchor(home)
self.EnsureCaretVisible()
else:
event.Skip()
#
# The following handlers modify text, so we need to see if there
# is a selection that includes text prior to the prompt.
#
# Don't modify a selection with text prior to the prompt.
elif self.GetSelectionStart() != self.GetSelectionEnd()\
and key not in NAVKEYS and not self.CanEdit():
pass
# Paste from the clipboard. # Paste from the clipboard.
elif (controlDown and not shiftDown \ elif (controlDown and not shiftDown \
and key in (ord('V'), ord('v'))) \ and key in (ord('V'), ord('v'))) \
@@ -419,34 +453,20 @@ class Shell(wxStyledTextCtrl):
or (altDown and key in (ord('N'), ord('n'))): or (altDown and key in (ord('N'), ord('n'))):
self.OnHistoryReplace(step=-1) self.OnHistoryReplace(step=-1)
# Insert the previous command from the history buffer. # Insert the previous command from the history buffer.
elif (shiftDown and key == WXK_UP): elif (shiftDown and key == WXK_UP) and self.CanEdit():
self.OnHistoryInsert(step=+1) self.OnHistoryInsert(step=+1)
# Insert the next command from the history buffer. # Insert the next command from the history buffer.
elif (shiftDown and key == WXK_DOWN): elif (shiftDown and key == WXK_DOWN) and self.CanEdit():
self.OnHistoryInsert(step=-1) self.OnHistoryInsert(step=-1)
# Search up the history for the text in front of the cursor. # Search up the history for the text in front of the cursor.
elif key == WXK_F8: elif key == WXK_F8:
self.OnHistorySearch() self.OnHistorySearch()
# Home needs to be aware of the prompt.
elif key == WXK_HOME:
home = self.promptPosEnd
if currpos >= home:
if event.ShiftDown():
# Select text from current position to end of prompt.
self.SetSelection(self.GetCurrentPos(), home)
else:
self.SetCurrentPos(home)
self.SetAnchor(home)
self.EnsureCaretVisible()
else:
event.Skip()
# Basic navigation keys should work anywhere.
elif key in (WXK_END, WXK_LEFT, WXK_RIGHT, WXK_UP, WXK_DOWN, \
WXK_PRIOR, WXK_NEXT):
event.Skip()
# Don't backspace over the latest non-continuation prompt. # Don't backspace over the latest non-continuation prompt.
elif key == WXK_BACK: elif key == WXK_BACK:
if currpos > self.promptPosEnd: if self.GetSelectionStart() != self.GetSelectionEnd()\
and self.CanEdit():
event.Skip()
elif currpos > self.promptPosEnd:
event.Skip() event.Skip()
# Only allow these keys after the latest prompt. # Only allow these keys after the latest prompt.
elif key in (WXK_TAB, WXK_DELETE): elif key in (WXK_TAB, WXK_DELETE):
@@ -461,6 +481,9 @@ class Shell(wxStyledTextCtrl):
# Don't allow line transposition. # Don't allow line transposition.
elif controlDown and key in (ord('T'), ord('t')): elif controlDown and key in (ord('T'), ord('t')):
pass pass
# Basic navigation keys should work anywhere.
elif key in NAVKEYS:
event.Skip()
# Protect the readonly portion of the shell. # Protect the readonly portion of the shell.
elif not self.CanEdit(): elif not self.CanEdit():
pass pass
@@ -641,6 +664,7 @@ class Shell(wxStyledTextCtrl):
def push(self, command): def push(self, command):
"""Send command to the interpreter for execution.""" """Send command to the interpreter for execution."""
busy = wxBusyCursor()
self.write(os.linesep) self.write(os.linesep)
self.more = self.interp.push(command) self.more = self.interp.push(command)
if not self.more: if not self.more:
@@ -844,6 +868,13 @@ class Shell(wxStyledTextCtrl):
def CanEdit(self): def CanEdit(self):
"""Return true if editing should succeed.""" """Return true if editing should succeed."""
if self.GetSelectionStart() != self.GetSelectionEnd():
if self.GetSelectionStart() >= self.promptPosEnd \
and self.GetSelectionEnd() >= self.promptPosEnd:
return 1
else:
return 0
else:
return self.GetCurrentPos() >= self.promptPosEnd return self.GetCurrentPos() >= self.promptPosEnd
def Cut(self): def Cut(self):
@@ -932,6 +963,20 @@ class Shell(wxStyledTextCtrl):
self.processLine() self.processLine()
wxTheClipboard.Close() wxTheClipboard.Close()
def wrap(self, wrap=1):
"""Sets whether text is word wrapped."""
try:
self.SetWrapMode(wrap)
except AttributeError:
return 'Wrapping is not available in this version of PyCrust.'
def zoom(self, points=0):
"""Set the zoom level.
This number of points is added to the size of all fonts.
It may be positive to magnify or negative to reduce."""
self.SetZoom(points)
wxID_SELECTALL = NewId() # This *should* be defined by wxPython. wxID_SELECTALL = NewId() # This *should* be defined by wxPython.
ID_AUTOCOMP = NewId() ID_AUTOCOMP = NewId()
@@ -1124,19 +1169,18 @@ class ShellFrame(wxFrame, ShellMenu):
intro += '\nSponsored by Orbtech - Your source for Python programming expertise.' intro += '\nSponsored by Orbtech - Your source for Python programming expertise.'
self.CreateStatusBar() self.CreateStatusBar()
self.SetStatusText(intro.replace('\n', ', ')) self.SetStatusText(intro.replace('\n', ', '))
import os
filename = os.path.join(os.path.dirname(__file__), 'PyCrust.ico') filename = os.path.join(os.path.dirname(__file__), 'PyCrust.ico')
icon = wxIcon(filename, wxBITMAP_TYPE_ICO) icon = wxIcon(filename, wxBITMAP_TYPE_ICO)
self.SetIcon(icon) self.SetIcon(icon)
self.shell = Shell(parent=self, id=-1, introText=intro, \ self.shell = Shell(parent=self, id=-1, introText=intro, \
locals=locals, InterpClass=InterpClass, \ locals=locals, InterpClass=InterpClass, \
*args, **kwds) *args, **kwds)
# Override the shell so that status messages go to the status bar. # Override the shell so that status messages go to the status bar.
self.shell.setStatusText = self.SetStatusText self.shell.setStatusText = self.SetStatusText
self.createMenus() self.createMenus()
EVT_CLOSE(self, self.OnCloseWindow)
def OnCloseWindow(self, event):
self.shell.destroy()
self.Destroy()