61071e6160
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@44235 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
379 lines
13 KiB
Python
379 lines
13 KiB
Python
"""Crust combines the shell and filling into one control."""
|
|
|
|
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
|
|
__cvsid__ = "$Id$"
|
|
__revision__ = "$Revision$"[11:-2]
|
|
|
|
import wx
|
|
|
|
import os
|
|
import pprint
|
|
import re
|
|
import sys
|
|
|
|
import dispatcher
|
|
import editwindow
|
|
from filling import Filling
|
|
import frame
|
|
from shell import Shell
|
|
from version import VERSION
|
|
|
|
|
|
class Crust(wx.SplitterWindow):
|
|
"""Crust based on SplitterWindow."""
|
|
|
|
name = 'Crust'
|
|
revision = __revision__
|
|
sashoffset = 300
|
|
|
|
def __init__(self, parent, id=-1, pos=wx.DefaultPosition,
|
|
size=wx.DefaultSize, style=wx.SP_3D|wx.SP_LIVE_UPDATE,
|
|
name='Crust Window', rootObject=None, rootLabel=None,
|
|
rootIsNamespace=True, intro='', locals=None,
|
|
InterpClass=None,
|
|
startupScript=None, execStartupScript=True,
|
|
*args, **kwds):
|
|
"""Create Crust instance."""
|
|
wx.SplitterWindow.__init__(self, parent, id, pos, size, style, name)
|
|
|
|
# Turn off the tab-traversal style that is automatically
|
|
# turned on by wx.SplitterWindow. We do this because on
|
|
# Windows the event for Ctrl-Enter is stolen and used as a
|
|
# navigation key, but the Shell window uses it to insert lines.
|
|
style = self.GetWindowStyle()
|
|
self.SetWindowStyle(style & ~wx.TAB_TRAVERSAL)
|
|
|
|
self.shell = Shell(parent=self, introText=intro,
|
|
locals=locals, InterpClass=InterpClass,
|
|
startupScript=startupScript,
|
|
execStartupScript=execStartupScript,
|
|
*args, **kwds)
|
|
self.editor = self.shell
|
|
if rootObject is None:
|
|
rootObject = self.shell.interp.locals
|
|
self.notebook = wx.Notebook(parent=self, id=-1)
|
|
self.shell.interp.locals['notebook'] = self.notebook
|
|
self.filling = Filling(parent=self.notebook,
|
|
rootObject=rootObject,
|
|
rootLabel=rootLabel,
|
|
rootIsNamespace=rootIsNamespace)
|
|
# Add 'filling' to the interpreter's locals.
|
|
self.shell.interp.locals['filling'] = self.filling
|
|
self.notebook.AddPage(page=self.filling, text='Namespace', select=True)
|
|
|
|
self.display = Display(parent=self.notebook)
|
|
self.notebook.AddPage(page=self.display, text='Display')
|
|
# Add 'pp' (pretty print) to the interpreter's locals.
|
|
self.shell.interp.locals['pp'] = self.display.setItem
|
|
self.display.nbTab = self.notebook.GetPageCount()-1
|
|
|
|
self.calltip = Calltip(parent=self.notebook)
|
|
self.notebook.AddPage(page=self.calltip, text='Calltip')
|
|
|
|
self.sessionlisting = SessionListing(parent=self.notebook)
|
|
self.notebook.AddPage(page=self.sessionlisting, text='History')
|
|
|
|
self.dispatcherlisting = DispatcherListing(parent=self.notebook)
|
|
self.notebook.AddPage(page=self.dispatcherlisting, text='Dispatcher')
|
|
|
|
|
|
# Initialize in an unsplit mode, and check later after loading
|
|
# settings if we should split or not.
|
|
self.shell.Hide()
|
|
self.notebook.Hide()
|
|
self.Initialize(self.shell)
|
|
self._shouldsplit = True
|
|
wx.CallAfter(self._CheckShouldSplit)
|
|
self.SetMinimumPaneSize(100)
|
|
|
|
self.Bind(wx.EVT_SIZE, self.SplitterOnSize)
|
|
self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnChanged)
|
|
self.Bind(wx.EVT_SPLITTER_DCLICK, self.OnSashDClick)
|
|
|
|
def _CheckShouldSplit(self):
|
|
if self._shouldsplit:
|
|
self.SplitHorizontally(self.shell, self.notebook, -self.sashoffset)
|
|
self.lastsashpos = self.GetSashPosition()
|
|
else:
|
|
self.lastsashpos = -1
|
|
self.issplit = self.IsSplit()
|
|
|
|
def ToggleTools(self):
|
|
"""Toggle the display of the filling and other tools"""
|
|
if self.issplit:
|
|
self.Unsplit()
|
|
else:
|
|
self.SplitHorizontally(self.shell, self.notebook, -self.sashoffset)
|
|
self.lastsashpos = self.GetSashPosition()
|
|
self.issplit = self.IsSplit()
|
|
|
|
def ToolsShown(self):
|
|
return self.issplit
|
|
|
|
def OnChanged(self, event):
|
|
"""update sash offset from the bottom of the window"""
|
|
self.sashoffset = self.GetSize().height - event.GetSashPosition()
|
|
self.lastsashpos = event.GetSashPosition()
|
|
event.Skip()
|
|
|
|
def OnSashDClick(self, event):
|
|
self.Unsplit()
|
|
self.issplit = False
|
|
|
|
# Make the splitter expand the top window when resized
|
|
def SplitterOnSize(self, event):
|
|
splitter = event.GetEventObject()
|
|
sz = splitter.GetSize()
|
|
splitter.SetSashPosition(sz.height - self.sashoffset, True)
|
|
event.Skip()
|
|
|
|
|
|
def LoadSettings(self, config):
|
|
self.shell.LoadSettings(config)
|
|
self.filling.LoadSettings(config)
|
|
|
|
pos = config.ReadInt('Sash/CrustPos', 400)
|
|
wx.CallAfter(self.SetSashPosition, pos)
|
|
def _updateSashPosValue():
|
|
sz = self.GetSize()
|
|
self.sashoffset = sz.height - self.GetSashPosition()
|
|
wx.CallAfter(_updateSashPosValue)
|
|
zoom = config.ReadInt('View/Zoom/Display', -99)
|
|
if zoom != -99:
|
|
self.display.SetZoom(zoom)
|
|
self.issplit = config.ReadInt('Sash/IsSplit', True)
|
|
if not self.issplit:
|
|
self._shouldsplit = False
|
|
|
|
def SaveSettings(self, config):
|
|
self.shell.SaveSettings(config)
|
|
self.filling.SaveSettings(config)
|
|
|
|
if self.lastsashpos != -1:
|
|
config.WriteInt('Sash/CrustPos', self.lastsashpos)
|
|
config.WriteInt('Sash/IsSplit', self.issplit)
|
|
config.WriteInt('View/Zoom/Display', self.display.GetZoom())
|
|
|
|
|
|
|
|
|
|
|
|
class Display(editwindow.EditWindow):
|
|
"""STC used to display an object using Pretty Print."""
|
|
|
|
def __init__(self, parent, id=-1, pos=wx.DefaultPosition,
|
|
size=wx.DefaultSize,
|
|
style=wx.CLIP_CHILDREN | wx.SUNKEN_BORDER,
|
|
static=False):
|
|
"""Create Display instance."""
|
|
editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
|
|
# Configure various defaults and user preferences.
|
|
self.SetReadOnly(True)
|
|
self.SetWrapMode(False)
|
|
if not static:
|
|
dispatcher.connect(receiver=self.push, signal='Interpreter.push')
|
|
|
|
def push(self, command, more):
|
|
"""Receiver for Interpreter.push signal."""
|
|
self.Refresh()
|
|
|
|
def Refresh(self):
|
|
if not hasattr(self, "item"):
|
|
return
|
|
self.SetReadOnly(False)
|
|
text = pprint.pformat(self.item)
|
|
self.SetText(text)
|
|
self.SetReadOnly(True)
|
|
|
|
def setItem(self, item):
|
|
"""Set item to pretty print in the notebook Display tab."""
|
|
self.item = item
|
|
self.Refresh()
|
|
if self.GetParent().GetSelection() != self.nbTab:
|
|
focus = wx.Window.FindFocus()
|
|
self.GetParent().SetSelection(self.nbTab)
|
|
wx.CallAfter(focus.SetFocus)
|
|
|
|
|
|
# TODO: Switch this to a editwindow.EditWindow
|
|
class Calltip(wx.TextCtrl):
|
|
"""Text control containing the most recent shell calltip."""
|
|
|
|
def __init__(self, parent=None, id=-1):
|
|
style = (wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2)
|
|
wx.TextCtrl.__init__(self, parent, id, style=style)
|
|
self.SetBackgroundColour(wx.Colour(255, 255, 208))
|
|
dispatcher.connect(receiver=self.display, signal='Shell.calltip')
|
|
|
|
df = self.GetFont()
|
|
font = wx.Font(df.GetPointSize(), wx.TELETYPE, wx.NORMAL, wx.NORMAL)
|
|
self.SetFont(font)
|
|
|
|
def display(self, calltip):
|
|
"""Receiver for Shell.calltip signal."""
|
|
## self.SetValue(calltip) # Caused refresh problem on Windows.
|
|
self.Clear()
|
|
self.AppendText(calltip)
|
|
|
|
|
|
# TODO: Switch this to a editwindow.EditWindow
|
|
class SessionListing(wx.TextCtrl):
|
|
"""Text control containing all commands for session."""
|
|
|
|
def __init__(self, parent=None, id=-1):
|
|
style = (wx.TE_MULTILINE | wx.TE_READONLY |
|
|
wx.TE_RICH2 | wx.TE_DONTWRAP)
|
|
wx.TextCtrl.__init__(self, parent, id, style=style)
|
|
dispatcher.connect(receiver=self.addHistory, signal="Shell.addHistory")
|
|
dispatcher.connect(receiver=self.clearHistory, signal="Shell.clearHistory")
|
|
dispatcher.connect(receiver=self.loadHistory, signal="Shell.loadHistory")
|
|
|
|
df = self.GetFont()
|
|
font = wx.Font(df.GetPointSize(), wx.TELETYPE, wx.NORMAL, wx.NORMAL)
|
|
self.SetFont(font)
|
|
|
|
def loadHistory(self, history):
|
|
# preload the existing history, if any
|
|
hist = history[:]
|
|
hist.reverse()
|
|
self.SetValue('\n'.join(hist) + '\n')
|
|
self.SetInsertionPointEnd()
|
|
|
|
def addHistory(self, command):
|
|
if command:
|
|
self.SetInsertionPointEnd()
|
|
self.AppendText(command + '\n')
|
|
|
|
def clearHistory(self):
|
|
self.SetValue("")
|
|
|
|
|
|
class DispatcherListing(wx.TextCtrl):
|
|
"""Text control containing all dispatches for session."""
|
|
|
|
def __init__(self, parent=None, id=-1):
|
|
style = (wx.TE_MULTILINE | wx.TE_READONLY |
|
|
wx.TE_RICH2 | wx.TE_DONTWRAP)
|
|
wx.TextCtrl.__init__(self, parent, id, style=style)
|
|
dispatcher.connect(receiver=self.spy)
|
|
|
|
df = self.GetFont()
|
|
font = wx.Font(df.GetPointSize(), wx.TELETYPE, wx.NORMAL, wx.NORMAL)
|
|
self.SetFont(font)
|
|
|
|
def spy(self, signal, sender):
|
|
"""Receiver for Any signal from Any sender."""
|
|
text = '%r from %s' % (signal, sender)
|
|
self.SetInsertionPointEnd()
|
|
start, end = self.GetSelection()
|
|
if start != end:
|
|
self.SetSelection(0, 0)
|
|
self.AppendText(text + '\n')
|
|
|
|
|
|
|
|
class CrustFrame(frame.Frame, frame.ShellFrameMixin):
|
|
"""Frame containing all the PyCrust components."""
|
|
|
|
name = 'CrustFrame'
|
|
revision = __revision__
|
|
|
|
|
|
def __init__(self, parent=None, id=-1, title='PyCrust',
|
|
pos=wx.DefaultPosition, size=wx.DefaultSize,
|
|
style=wx.DEFAULT_FRAME_STYLE,
|
|
rootObject=None, rootLabel=None, rootIsNamespace=True,
|
|
locals=None, InterpClass=None,
|
|
config=None, dataDir=None,
|
|
*args, **kwds):
|
|
"""Create CrustFrame instance."""
|
|
frame.Frame.__init__(self, parent, id, title, pos, size, style)
|
|
frame.ShellFrameMixin.__init__(self, config, dataDir)
|
|
|
|
if size == wx.DefaultSize:
|
|
self.SetSize((800, 600))
|
|
|
|
intro = 'PyCrust %s - The Flakiest Python Shell' % VERSION
|
|
self.SetStatusText(intro.replace('\n', ', '))
|
|
self.crust = Crust(parent=self, intro=intro,
|
|
rootObject=rootObject,
|
|
rootLabel=rootLabel,
|
|
rootIsNamespace=rootIsNamespace,
|
|
locals=locals,
|
|
InterpClass=InterpClass,
|
|
startupScript=self.startupScript,
|
|
execStartupScript=self.execStartupScript,
|
|
*args, **kwds)
|
|
self.shell = self.crust.shell
|
|
|
|
# Override the filling so that status messages go to the status bar.
|
|
self.crust.filling.tree.setStatusText = self.SetStatusText
|
|
|
|
# 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."""
|
|
self.SaveSettings()
|
|
self.crust.shell.destroy()
|
|
self.Destroy()
|
|
|
|
|
|
def OnAbout(self, event):
|
|
"""Display an About window."""
|
|
title = 'About PyCrust'
|
|
text = 'PyCrust %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 ToggleTools(self):
|
|
"""Toggle the display of the filling and other tools"""
|
|
return self.crust.ToggleTools()
|
|
|
|
def ToolsShown(self):
|
|
return self.crust.ToolsShown()
|
|
|
|
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.crust.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.crust.SaveSettings(self.config)
|
|
|
|
|
|
def DoSaveSettings(self):
|
|
if self.config is not None:
|
|
self.SaveSettings(force=True)
|
|
self.config.Flush()
|
|
|
|
|
|
|