diff --git a/wxPython/CHANGES.txt b/wxPython/CHANGES.txt index e75d7ebbea..06648234fb 100644 --- a/wxPython/CHANGES.txt +++ b/wxPython/CHANGES.txt @@ -2,7 +2,7 @@ CHANGES.txt for wxPython ---------------------------------------------------------------------- -2.4.0.8 +2.4.1 ------- Added wxScrolledPanel from Wil Sadkin @@ -24,6 +24,16 @@ people can do imports without "import *" and can use names like wx.Frame instead of wx.wxFrame. This is phase 1 of a full transition to the new namespace. +Updated Scintilla to 1.51. + +Patrick O'Brien's PyCrust package has been renamed to Py and now +includes several new tools. As part of the change the location of the +pacakge has changed as well, it is now accessible as "from wxPython +import py" (or "from wx import py" using the new namespace.) There +are still some transition moudules in the wxPython.lib.PyCrust mackage +that will issue a warning and then import what is needed from the new +package. These will be removed in a future release. + diff --git a/wxPython/scripts/CreateBatchFiles.py b/wxPython/scripts/CreateBatchFiles.py index 871650d52f..d5d861801f 100644 --- a/wxPython/scripts/CreateBatchFiles.py +++ b/wxPython/scripts/CreateBatchFiles.py @@ -16,13 +16,15 @@ python = sys.executable pythonw = 'start ' + os.path.join(os.path.split(python)[0], 'pythonw.exe') scriptdir = os.getcwd() -scripts = [ ("img2png", 0), - ("img2py", 0), - ("img2xpm", 0), - ("xrced", 1), - ("pyshell", 1), - ("pycrust", 1), - ("pycwrap", 1), +scripts = [ ("img2png", 0), + ("img2py", 0), + ("img2xpm", 0), + ("xrced", 1), + ("pyshell", 1), + ("pycrust", 1), + ("pywrap", 1), + ("pyalamode", 1), + ("pyalacarte", 1), ("helpviewer", 1), ] diff --git a/wxPython/scripts/pyalacarte b/wxPython/scripts/pyalacarte new file mode 100755 index 0000000000..8ce50e1b22 --- /dev/null +++ b/wxPython/scripts/pyalacarte @@ -0,0 +1,4 @@ +#!/usr/bin/env python + +from wx.py.PyAlaCarte import main +main() diff --git a/wxPython/scripts/pyalamode b/wxPython/scripts/pyalamode new file mode 100755 index 0000000000..ac3abdc335 --- /dev/null +++ b/wxPython/scripts/pyalamode @@ -0,0 +1,4 @@ +#!/usr/bin/env python + +from wx.py.PyAlaMode import main +main() diff --git a/wxPython/scripts/pycrust b/wxPython/scripts/pycrust index e07058e8e2..2c1aec1121 100755 --- a/wxPython/scripts/pycrust +++ b/wxPython/scripts/pycrust @@ -1,4 +1,4 @@ #!/usr/bin/env python -from wxPython.lib.PyCrust.PyCrustApp import main +from wx.py.PyCrust import main main() diff --git a/wxPython/scripts/pycwrap.bat b/wxPython/scripts/pycwrap.bat deleted file mode 100755 index 40562b5629..0000000000 --- a/wxPython/scripts/pycwrap.bat +++ /dev/null @@ -1,3 +0,0 @@ -@echo off - -start e:\tools\Python22\pythonw.exe e:\PROJECTS\wx\wxPython\scripts\pycwrap %1 %2 %3 %4 %5 %6 %7 %8 %9 diff --git a/wxPython/scripts/pyshell b/wxPython/scripts/pyshell index ee9e9228b7..30972b58c4 100755 --- a/wxPython/scripts/pyshell +++ b/wxPython/scripts/pyshell @@ -1,4 +1,4 @@ #!/usr/bin/env python -from wxPython.lib.PyCrust.PyShellApp import main +from wx.py.PyShell import main main() diff --git a/wxPython/scripts/pycwrap b/wxPython/scripts/pywrap similarity index 66% rename from wxPython/scripts/pycwrap rename to wxPython/scripts/pywrap index 911c5ec778..d2ec252016 100755 --- a/wxPython/scripts/pycwrap +++ b/wxPython/scripts/pywrap @@ -1,6 +1,6 @@ #!/usr/bin/env python -from wxPython.lib.PyCrust.wrap import main +from wx.py.PyWrap import main import sys, os sys.path.insert(0, os.curdir) diff --git a/wxPython/wx/py/PyAlaCarte.py b/wxPython/wx/py/PyAlaCarte.py new file mode 100644 index 0000000000..6d60dd0eff --- /dev/null +++ b/wxPython/wx/py/PyAlaCarte.py @@ -0,0 +1,3 @@ +# Py is really located in the old namespace, but it doesn't need any +# renaming done. +from wxPython.py.PyAlaCarte import * diff --git a/wxPython/wx/py/PyAlaMode.py b/wxPython/wx/py/PyAlaMode.py new file mode 100644 index 0000000000..9417837fc3 --- /dev/null +++ b/wxPython/wx/py/PyAlaMode.py @@ -0,0 +1,3 @@ +# Py is really located in the old namespace, but it doesn't need any +# renaming done. +from wxPython.py.PyAlaMode import * diff --git a/wxPython/wx/py/PyCrust.py b/wxPython/wx/py/PyCrust.py new file mode 100644 index 0000000000..9571f7d73a --- /dev/null +++ b/wxPython/wx/py/PyCrust.py @@ -0,0 +1,3 @@ +# Py is really located in the old namespace, but it doesn't need any +# renaming done. +from wxPython.py.PyCrust import * diff --git a/wxPython/wx/py/PyFilling.py b/wxPython/wx/py/PyFilling.py new file mode 100644 index 0000000000..fa9e01db2a --- /dev/null +++ b/wxPython/wx/py/PyFilling.py @@ -0,0 +1,3 @@ +# Py is really located in the old namespace, but it doesn't need any +# renaming done. +from wxPython.py.PyFilling import * diff --git a/wxPython/wx/py/PyShell.py b/wxPython/wx/py/PyShell.py new file mode 100644 index 0000000000..19df451e84 --- /dev/null +++ b/wxPython/wx/py/PyShell.py @@ -0,0 +1,3 @@ +# Py is really located in the old namespace, but it doesn't need any +# renaming done. +from wxPython.py.PyShell import * diff --git a/wxPython/wx/py/PyWrap.py b/wxPython/wx/py/PyWrap.py new file mode 100644 index 0000000000..352a79ff6a --- /dev/null +++ b/wxPython/wx/py/PyWrap.py @@ -0,0 +1,3 @@ +# Py is really located in the old namespace, but it doesn't need any +# renaming done. +from wxPython.py.PyWrap import * diff --git a/wxPython/wx/py/__init__.py b/wxPython/wx/py/__init__.py new file mode 100644 index 0000000000..7efc5e89c1 --- /dev/null +++ b/wxPython/wx/py/__init__.py @@ -0,0 +1,3 @@ +# Py is really located in the old namespace, but it doesn't need any +# renaming done. +from wxPython.py import * diff --git a/wxPython/wxPython/lib/PyCrust/.cvsignore b/wxPython/wxPython/lib/PyCrust/.cvsignore deleted file mode 100644 index 9020783b5e..0000000000 --- a/wxPython/wxPython/lib/PyCrust/.cvsignore +++ /dev/null @@ -1,3 +0,0 @@ -*.pyc -*.BAK -*.$$$ \ No newline at end of file diff --git a/wxPython/wxPython/lib/PyCrust/PyCrustApp.py b/wxPython/wxPython/lib/PyCrust/PyCrustApp.py index 036e368002..a661288346 100755 --- a/wxPython/wxPython/lib/PyCrust/PyCrustApp.py +++ b/wxPython/wxPython/lib/PyCrust/PyCrustApp.py @@ -1,70 +1,6 @@ -"""PyCrustApp is a python shell and namespace browser application.""" +from wxPython.py.PyCrust import * -# The next two lines, and the other code below that makes use of -# ``__main__`` and ``original``, serve the purpose of cleaning up the -# main namespace to look as much as possible like the regular Python -# shell environment. -import __main__ -original = __main__.__dict__.keys() - -__author__ = "Patrick K. O'Brien " -__cvsid__ = "$Id$" -__revision__ = "$Revision$"[11:-2] - -from wxPython import wx - -try: - True -except NameError: - True = 1==1 - False = 1==0 - - -class App(wx.wxApp): - """PyCrust standalone application.""" - - def OnInit(self): - from wxPython import wx - wx.wxInitAllImageHandlers() - locals = __main__.__dict__ - from crust import CrustFrame - self.frame = CrustFrame(locals=locals) - self.frame.SetSize((800, 600)) - self.frame.Show() - self.SetTopWindow(self.frame) - # Add the application object to the sys module's namespace. - # This allows a shell user to do: - # >>> import sys - # >>> sys.app.whatever - import sys - sys.app = self - return 1 - -''' -The main() function needs to handle being imported, such as with the -pycrust script that wxPython installs: - - #!/usr/bin/env python - - from wxPython.lib.PyCrust.PyCrustApp import main - main() -''' - -def main(): - import __main__ - md = __main__.__dict__ - keepers = original - keepers.append('App') - for key in md.keys(): - if key not in keepers: - del md[key] - app = App(0) - if md.has_key('App') and md['App'] is App: - del md['App'] - if md.has_key('__main__') and md['__main__'] is __main__: - del md['__main__'] - app.MainLoop() if __name__ == '__main__': main() diff --git a/wxPython/wxPython/lib/PyCrust/PyFillingApp.py b/wxPython/wxPython/lib/PyCrust/PyFillingApp.py index b204210a80..0d8b321982 100755 --- a/wxPython/wxPython/lib/PyCrust/PyFillingApp.py +++ b/wxPython/wxPython/lib/PyCrust/PyFillingApp.py @@ -1,44 +1,5 @@ -"""PyFillingApp is a python namespace inspection application.""" - -__author__ = "Patrick K. O'Brien " -__cvsid__ = "$Id$" -__revision__ = "$Revision$"[11:-2] - -# We use this object to get more introspection when run standalone. -app = None - -import filling - -# These are imported just to have something interesting to inspect. -import crust -import interpreter -import introspect -import pseudo -import shell - -import sys -from wxPython import wx - -try: - True -except NameError: - True = 1==1 - False = 1==0 - - -class App(filling.App): - def OnInit(self): - filling.App.OnInit(self) - self.root = self.fillingFrame.filling.tree.root - return True - -def main(): - """Create and run the application.""" - global app - app = App(0) - app.fillingFrame.filling.tree.Expand(app.root) - app.MainLoop() +from wxPython.py.PyFilling import * if __name__ == '__main__': diff --git a/wxPython/wxPython/lib/PyCrust/PyShellApp.py b/wxPython/wxPython/lib/PyCrust/PyShellApp.py index 2ef7aae07f..674a451528 100755 --- a/wxPython/wxPython/lib/PyCrust/PyShellApp.py +++ b/wxPython/wxPython/lib/PyCrust/PyShellApp.py @@ -1,71 +1,6 @@ -"""PyShellApp is a python shell application.""" +from wxPython.py.PyShell import * -# The next two lines, and the other code below that makes use of -# ``__main__`` and ``original``, serve the purpose of cleaning up the -# main namespace to look as much as possible like the regular Python -# shell environment. -import __main__ -original = __main__.__dict__.keys() - -__author__ = "Patrick K. O'Brien " -__cvsid__ = "$Id$" -__revision__ = "$Revision$"[11:-2] - -from wxPython import wx - -try: - True -except NameError: - True = 1==1 - False = 1==0 - - -class App(wx.wxApp): - """PyShell standalone application.""" - - def OnInit(self): - from wxPython import wx - wx.wxInitAllImageHandlers() - locals = __main__.__dict__ - from shell import ShellFrame - self.frame = ShellFrame(locals=locals) - self.frame.SetSize((750, 525)) - self.frame.Show() - self.SetTopWindow(self.frame) - self.frame.shell.SetFocus() - # Add the application object to the sys module's namespace. - # This allows a shell user to do: - # >>> import sys - # >>> sys.app.whatever - import sys - sys.app = self - return 1 - -''' -The main() function needs to handle being imported, such as with the -pycrust script that wxPython installs: - - #!/usr/bin/env python - - from wxPython.lib.PyCrust.PyCrustApp import main - main() -''' - -def main(): - import __main__ - md = __main__.__dict__ - keepers = original - keepers.append('App') - for key in md.keys(): - if key not in keepers: - del md[key] - app = App(0) - if md.has_key('App') and md['App'] is App: - del md['App'] - if md.has_key('__main__') and md['__main__'] is __main__: - del md['__main__'] - app.MainLoop() if __name__ == '__main__': main() diff --git a/wxPython/wxPython/lib/PyCrust/__init__.py b/wxPython/wxPython/lib/PyCrust/__init__.py index db6003cf3b..cf7736acd1 100644 --- a/wxPython/wxPython/lib/PyCrust/__init__.py +++ b/wxPython/wxPython/lib/PyCrust/__init__.py @@ -1 +1,5 @@ -# Orbtech python package. \ No newline at end of file + +# Change this to an ImportError after the next release +import warnings +warnings.warn("PyCrust has been renamed to Py. Use 'from wx import py'", stacklevel=2) + diff --git a/wxPython/wxPython/lib/PyCrust/crust.py b/wxPython/wxPython/lib/PyCrust/crust.py index caeb20f6db..fc1360461d 100644 --- a/wxPython/wxPython/lib/PyCrust/crust.py +++ b/wxPython/wxPython/lib/PyCrust/crust.py @@ -1,169 +1,2 @@ -"""PyCrust Crust combines the shell and filling into one control.""" - -__author__ = "Patrick K. O'Brien " -__cvsid__ = "$Id$" -__revision__ = "$Revision$"[11:-2] - -from wxPython import wx -from filling import Filling -import os -from shell import Shell -from shellmenu import ShellMenu -from version import VERSION - -try: - True -except NameError: - True = 1==1 - False = 1==0 - - -class Crust(wx.wxSplitterWindow): - """PyCrust Crust based on wxSplitterWindow.""" - - name = 'PyCrust Crust' - revision = __revision__ - - def __init__(self, parent, id=-1, pos=wx.wxDefaultPosition, - size=wx.wxDefaultSize, style=wx.wxSP_3D, - name='Crust Window', rootObject=None, rootLabel=None, - rootIsNamespace=True, intro='', locals=None, - InterpClass=None, *args, **kwds): - """Create a PyCrust Crust instance.""" - wx.wxSplitterWindow.__init__(self, parent, id, pos, size, style, name) - self.shell = Shell(parent=self, introText=intro, - locals=locals, InterpClass=InterpClass, - *args, **kwds) - if rootObject is None: - rootObject = self.shell.interp.locals - self.notebook = wx.wxNotebook(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.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='Session') - self.dispatcherlisting = DispatcherListing(parent=self.notebook) - self.notebook.AddPage(page=self.dispatcherlisting, text='Dispatcher') - from wxd import wx_ - self.wxdocs = Filling(parent=self.notebook, - rootObject=wx_, - rootLabel='wx', - rootIsNamespace=False, - static=True) - self.notebook.AddPage(page=self.wxdocs, text='wxPython Docs') - from wxd import stc_ - self.stcdocs = Filling(parent=self.notebook, - rootObject=stc_.StyledTextCtrl, - rootLabel='StyledTextCtrl', - rootIsNamespace=False, - static=True) - self.notebook.AddPage(page=self.stcdocs, text='StyledTextCtrl Docs') - self.SplitHorizontally(self.shell, self.notebook, 300) - self.SetMinimumPaneSize(1) - - -class Calltip(wx.wxTextCtrl): - """Text control containing the most recent shell calltip.""" - - def __init__(self, parent=None, id=-1): - import dispatcher - style = wx.wxTE_MULTILINE | wx.wxTE_READONLY | wx.wxTE_RICH2 - wx.wxTextCtrl.__init__(self, parent=parent, id=id, style=style) - self.SetBackgroundColour(wx.wxColour(255, 255, 232)) - dispatcher.connect(receiver=self.display, signal='Shell.calltip') - - def display(self, calltip): - """Receiver for Shell.calltip signal.""" - self.SetValue(calltip) - - -class SessionListing(wx.wxTextCtrl): - """Text control containing all commands for session.""" - - def __init__(self, parent=None, id=-1): - import dispatcher - style = wx.wxTE_MULTILINE | wx.wxTE_READONLY | \ - wx.wxTE_RICH2 | wx.wxTE_DONTWRAP - wx.wxTextCtrl.__init__(self, parent=parent, id=id, style=style) - dispatcher.connect(receiver=self.push, signal='Interpreter.push') - - def push(self, command, more): - """Receiver for Interpreter.push signal.""" - if command and not more: - self.SetInsertionPointEnd() - start, end = self.GetSelection() - if start != end: - self.SetSelection(0, 0) - self.AppendText(command + '\n') - - -class DispatcherListing(wx.wxTextCtrl): - """Text control containing all dispatches for session.""" - - def __init__(self, parent=None, id=-1): - import dispatcher - style = wx.wxTE_MULTILINE | wx.wxTE_READONLY | \ - wx.wxTE_RICH2 | wx.wxTE_DONTWRAP - wx.wxTextCtrl.__init__(self, parent=parent, id=id, style=style) - dispatcher.connect(receiver=self.spy) - - 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(wx.wxFrame, ShellMenu): - """Frame containing all the PyCrust components.""" - - name = 'PyCrust Frame' - revision = __revision__ - - def __init__(self, parent=None, id=-1, title='PyCrust', - pos=wx.wxDefaultPosition, size=wx.wxDefaultSize, - style=wx.wxDEFAULT_FRAME_STYLE, - rootObject=None, rootLabel=None, rootIsNamespace=True, - locals=None, InterpClass=None, *args, **kwds): - """Create a PyCrust CrustFrame instance.""" - wx.wxFrame.__init__(self, parent, id, title, pos, size, style) - intro = 'PyCrust %s - The Flakiest Python Shell' % VERSION - intro += '\nSponsored by Orbtech - ' - intro += 'Your source for Python programming expertise.' - self.CreateStatusBar() - self.SetStatusText(intro.replace('\n', ', ')) - import images - self.SetIcon(images.getPyCrustIcon()) - self.crust = Crust(parent=self, intro=intro, - rootObject=rootObject, - rootLabel=rootLabel, - rootIsNamespace=rootIsNamespace, - locals=locals, - InterpClass=InterpClass, *args, **kwds) - # 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.crust.shell.setStatusText = self.SetStatusText - # Fix a problem with the sash shrinking to nothing. - self.crust.filling.SetSashPosition(200) - self.shell = self.crust.shell - self.createMenus() - wx.EVT_CLOSE(self, self.OnCloseWindow) - # Set focus to the shell editor. - self.crust.shell.SetFocus() - - def OnCloseWindow(self, event): - self.crust.shell.destroy() - self.Destroy() - +from wxPython.py.crust import * diff --git a/wxPython/wxPython/lib/PyCrust/filling.py b/wxPython/wxPython/lib/PyCrust/filling.py index 9a846b4def..a8fa098f3b 100644 --- a/wxPython/wxPython/lib/PyCrust/filling.py +++ b/wxPython/wxPython/lib/PyCrust/filling.py @@ -1,436 +1,3 @@ -"""PyCrust Filling is the gui tree control through which a user can -navigate the local namespace or any object.""" -__author__ = "Patrick K. O'Brien " -__cvsid__ = "$Id$" -__revision__ = "$Revision$"[11:-2] +from wxPython.py.filling import * -from wxPython import wx -from wxPython import stc -from version import VERSION -import dispatcher -import inspect -import introspect -import keyword -import sys -import types - -try: - True -except NameError: - True = 1==1 - False = 1==0 - -COMMONTYPES = [getattr(types, t) for t in dir(types) \ - if not t.startswith('_') \ - and t not in ('ClassType', 'InstanceType', 'ModuleType')] - -DOCTYPES = ('BuiltinFunctionType', 'BuiltinMethodType', 'ClassType', - 'FunctionType', 'GeneratorType', 'InstanceType', - 'LambdaType', 'MethodType', 'ModuleType', - 'UnboundMethodType', 'method-wrapper') - -SIMPLETYPES = [getattr(types, t) for t in dir(types) \ - if not t.startswith('_') and t not in DOCTYPES] - -try: - COMMONTYPES.append(type(''.__repr__)) # Method-wrapper in version 2.2.x. -except AttributeError: - pass - - -class FillingTree(wx.wxTreeCtrl): - """PyCrust FillingTree based on wxTreeCtrl.""" - - name = 'PyCrust Filling Tree' - revision = __revision__ - - def __init__(self, parent, id=-1, pos=wx.wxDefaultPosition, - size=wx.wxDefaultSize, style=wx.wxTR_DEFAULT_STYLE, - rootObject=None, rootLabel=None, rootIsNamespace=0, - static=False): - """Create a PyCrust FillingTree instance.""" - wx.wxTreeCtrl.__init__(self, parent, id, pos, size, style) - self.rootIsNamespace = rootIsNamespace - import __main__ - if rootObject is None: - rootObject = __main__.__dict__ - self.rootIsNamespace = 1 - if rootObject is __main__.__dict__ and rootLabel is None: - rootLabel = 'locals()' - if not rootLabel: - rootLabel = 'Ingredients' - rootData = wx.wxTreeItemData(rootObject) - self.item = self.root = self.AddRoot(rootLabel, -1, -1, rootData) - self.SetItemHasChildren(self.root, self.hasChildren(rootObject)) - wx.EVT_TREE_ITEM_EXPANDING(self, self.GetId(), self.OnItemExpanding) - wx.EVT_TREE_ITEM_COLLAPSED(self, self.GetId(), self.OnItemCollapsed) - wx.EVT_TREE_SEL_CHANGED(self, self.GetId(), self.OnSelChanged) - wx.EVT_TREE_ITEM_ACTIVATED(self, self.GetId(), self.OnItemActivated) - if not static: - dispatcher.connect(receiver=self.push, signal='Interpreter.push') - - def push(self, command, more): - """Receiver for Interpreter.push signal.""" - self.display() - - def OnItemExpanding(self, event): - """Add children to the item.""" - busy = wx.wxBusyCursor() - item = event.GetItem() - if self.IsExpanded(item): - return - self.addChildren(item) -# self.SelectItem(item) - - def OnItemCollapsed(self, event): - """Remove all children from the item.""" - busy = wx.wxBusyCursor() - item = event.GetItem() -# self.CollapseAndReset(item) -# self.DeleteChildren(item) -# self.SelectItem(item) - - def OnSelChanged(self, event): - """Display information about the item.""" - busy = wx.wxBusyCursor() - self.item = event.GetItem() - self.display() - - def OnItemActivated(self, event): - """Launch a DirFrame.""" - item = event.GetItem() - text = self.getFullName(item) - obj = self.GetPyData(item) - frame = FillingFrame(parent=self, size=(600, 100), rootObject=obj, - rootLabel=text, rootIsNamespace=False) - frame.Show() - - def hasChildren(self, obj): - """Return true if object has children.""" - if self.getChildren(obj): - return True - else: - return False - - def getChildren(self, obj): - """Return dictionary with attributes or contents of object.""" - busy = wx.wxBusyCursor() - otype = type(obj) - if otype is types.DictType \ - or str(otype)[17:23] == 'BTrees' and hasattr(obj, 'keys'): - return obj - d = {} - if otype is types.ListType or otype is types.TupleType: - for n in range(len(obj)): - key = '[' + str(n) + ']' - d[key] = obj[n] - if otype not in COMMONTYPES: - for key in introspect.getAttributeNames(obj): - # Believe it or not, some attributes can disappear, - # such as the exc_traceback attribute of the sys - # module. So this is nested in a try block. - try: - d[key] = getattr(obj, key) - except: - pass - return d - - def addChildren(self, item): - self.DeleteChildren(item) - obj = self.GetPyData(item) - children = self.getChildren(obj) - if not children: - return - keys = children.keys() - keys.sort(lambda x, y: cmp(x.lower(), y.lower())) - for key in keys: - itemtext = str(key) - # Show string dictionary items with single quotes, except - # for the first level of items, if they represent a - # namespace. - if type(obj) is types.DictType \ - and type(key) is types.StringType \ - and (item != self.root \ - or (item == self.root and not self.rootIsNamespace)): - itemtext = repr(key) - child = children[key] - data = wx.wxTreeItemData(child) - branch = self.AppendItem(parent=item, text=itemtext, data=data) - self.SetItemHasChildren(branch, self.hasChildren(child)) - - def display(self): - item = self.item - if self.IsExpanded(item): - self.addChildren(item) - self.setText('') - obj = self.GetPyData(item) - if obj is None: # Windows bug fix. - return - self.SetItemHasChildren(item, self.hasChildren(obj)) - otype = type(obj) - text = '' - text += self.getFullName(item) - text += '\n\nType: ' + str(otype) - try: - value = str(obj) - except: - value = '' - if otype is types.StringType or otype is types.UnicodeType: - value = repr(obj) - text += '\n\nValue: ' + value - if otype not in SIMPLETYPES: - try: - text += '\n\nDocstring:\n\n"""' + \ - inspect.getdoc(obj).strip() + '"""' - except: - pass - if otype is types.InstanceType: - try: - text += '\n\nClass Definition:\n\n' + \ - inspect.getsource(obj.__class__) - except: - pass - else: - try: - text += '\n\nSource Code:\n\n' + \ - inspect.getsource(obj) - except: - pass - self.setText(text) - - def getFullName(self, item, partial=''): - """Return a syntactically proper name for item.""" - name = self.GetItemText(item) - parent = None - obj = None - if item != self.root: - parent = self.GetItemParent(item) - obj = self.GetPyData(parent) - # Apply dictionary syntax to dictionary items, except the root - # and first level children of a namepace. - if (type(obj) is types.DictType \ - or str(type(obj))[17:23] == 'BTrees' \ - and hasattr(obj, 'keys')) \ - and ((item != self.root and parent != self.root) \ - or (parent == self.root and not self.rootIsNamespace)): - name = '[' + name + ']' - # Apply dot syntax to multipart names. - if partial: - if partial[0] == '[': - name += partial - else: - name += '.' + partial - # Repeat for everything but the root item - # and first level children of a namespace. - if (item != self.root and parent != self.root) \ - or (parent == self.root and not self.rootIsNamespace): - name = self.getFullName(parent, partial=name) - return name - - def setText(self, text): - """Display information about the current selection.""" - - # This method will likely be replaced by the enclosing app to - # do something more interesting, like write to a text control. - print text - - def setStatusText(self, text): - """Display status information.""" - - # This method will likely be replaced by the enclosing app to - # do something more interesting, like write to a status bar. - print text - - -if wx.wxPlatform == '__WXMSW__': - faces = { 'times' : 'Times New Roman', - 'mono' : 'Courier New', - 'helv' : 'Lucida Console', - 'lucida' : 'Lucida Console', - 'other' : 'Comic Sans MS', - 'size' : 10, - 'lnsize' : 9, - 'backcol': '#FFFFFF', - } -else: # GTK - faces = { 'times' : 'Times', - 'mono' : 'Courier', - 'helv' : 'Helvetica', - 'other' : 'new century schoolbook', - 'size' : 12, - 'lnsize' : 10, - 'backcol': '#FFFFFF', - } - - -class FillingText(stc.wxStyledTextCtrl): - """PyCrust FillingText based on wxStyledTextCtrl.""" - - name = 'PyCrust Filling Text' - revision = __revision__ - - def __init__(self, parent, id=-1, pos=wx.wxDefaultPosition, - size=wx.wxDefaultSize, style=wx.wxCLIP_CHILDREN, - static=False): - """Create a PyCrust FillingText instance.""" - stc.wxStyledTextCtrl.__init__(self, parent, id, pos, size, style) - # Configure various defaults and user preferences. - self.config() - dispatcher.connect(receiver=self.fontsizer, signal='FontIncrease') - dispatcher.connect(receiver=self.fontsizer, signal='FontDecrease') - dispatcher.connect(receiver=self.fontsizer, signal='FontDefault') - if not static: - dispatcher.connect(receiver=self.push, signal='Interpreter.push') - - def push(self, command, more): - """Receiver for Interpreter.push signal.""" - self.Refresh() - - def fontsizer(self, signal): - """Receiver for Font* signals.""" - size = self.GetZoom() - if signal == 'FontIncrease': - size += 1 - elif signal == 'FontDecrease': - size -= 1 - elif signal == 'FontDefault': - size = 0 - self.SetZoom(size) - - def config(self): - """Configure shell based on user preferences.""" - self.SetMarginWidth(1, 0) - - self.SetLexer(stc.wxSTC_LEX_PYTHON) - self.SetKeyWords(0, ' '.join(keyword.kwlist)) - - self.setStyles(faces) - self.SetViewWhiteSpace(0) - self.SetTabWidth(4) - self.SetUseTabs(0) - self.SetReadOnly(1) - try: - self.SetWrapMode(1) - except AttributeError: - pass - - def setStyles(self, faces): - """Configure font size, typeface and color for lexer.""" - - # Default style - self.StyleSetSpec(stc.wxSTC_STYLE_DEFAULT, - "face:%(mono)s,size:%(size)d" % faces) - - self.StyleClearAll() - - # Built in styles - self.StyleSetSpec(stc.wxSTC_STYLE_LINENUMBER, - "back:#C0C0C0,face:%(mono)s,size:%(lnsize)d" % faces) - self.StyleSetSpec(stc.wxSTC_STYLE_CONTROLCHAR, - "face:%(mono)s" % faces) - self.StyleSetSpec(stc.wxSTC_STYLE_BRACELIGHT, - "fore:#0000FF,back:#FFFF88") - self.StyleSetSpec(stc.wxSTC_STYLE_BRACEBAD, - "fore:#FF0000,back:#FFFF88") - - # Python styles - self.StyleSetSpec(stc.wxSTC_P_DEFAULT, - "face:%(mono)s" % faces) - self.StyleSetSpec(stc.wxSTC_P_COMMENTLINE, - "fore:#007F00,face:%(mono)s" % faces) - self.StyleSetSpec(stc.wxSTC_P_NUMBER, - "") - self.StyleSetSpec(stc.wxSTC_P_STRING, - "fore:#7F007F,face:%(mono)s" % faces) - self.StyleSetSpec(stc.wxSTC_P_CHARACTER, - "fore:#7F007F,face:%(mono)s" % faces) - self.StyleSetSpec(stc.wxSTC_P_WORD, - "fore:#00007F,bold") - self.StyleSetSpec(stc.wxSTC_P_TRIPLE, - "fore:#7F0000") - self.StyleSetSpec(stc.wxSTC_P_TRIPLEDOUBLE, - "fore:#000033,back:#FFFFE8") - self.StyleSetSpec(stc.wxSTC_P_CLASSNAME, - "fore:#0000FF,bold") - self.StyleSetSpec(stc.wxSTC_P_DEFNAME, - "fore:#007F7F,bold") - self.StyleSetSpec(stc.wxSTC_P_OPERATOR, - "") - self.StyleSetSpec(stc.wxSTC_P_IDENTIFIER, - "") - self.StyleSetSpec(stc.wxSTC_P_COMMENTBLOCK, - "fore:#7F7F7F") - self.StyleSetSpec(stc.wxSTC_P_STRINGEOL, - "fore:#000000,face:%(mono)s,back:#E0C0E0,eolfilled" % faces) - - def SetText(self, *args, **kwds): - self.SetReadOnly(0) - stc.wxStyledTextCtrl.SetText(self, *args, **kwds) - self.SetReadOnly(1) - - -class Filling(wx.wxSplitterWindow): - """PyCrust Filling based on wxSplitterWindow.""" - - name = 'PyCrust Filling' - revision = __revision__ - - def __init__(self, parent, id=-1, pos=wx.wxDefaultPosition, - size=wx.wxDefaultSize, style=wx.wxSP_3D, - name='Filling Window', rootObject=None, - rootLabel=None, rootIsNamespace=0, static=False): - """Create a PyCrust Filling instance.""" - wx.wxSplitterWindow.__init__(self, parent, id, pos, size, style, name) - self.tree = FillingTree(parent=self, rootObject=rootObject, - rootLabel=rootLabel, - rootIsNamespace=rootIsNamespace, - static=static) - self.text = FillingText(parent=self, static=static) - self.SplitVertically(self.tree, self.text, 200) - self.SetMinimumPaneSize(1) - # Override the filling so that descriptions go to FillingText. - self.tree.setText = self.text.SetText - # Display the root item. -## self.tree.SelectItem(self.tree.root) - self.tree.display() - - -class FillingFrame(wx.wxFrame): - """Frame containing the PyCrust filling, or namespace tree component.""" - - name = 'PyCrust Filling Frame' - revision = __revision__ - - def __init__(self, parent=None, id=-1, title='PyFilling', - pos=wx.wxDefaultPosition, size=wx.wxDefaultSize, - style=wx.wxDEFAULT_FRAME_STYLE, rootObject=None, - rootLabel=None, rootIsNamespace=0, static=False): - """Create a PyCrust FillingFrame instance.""" - wx.wxFrame.__init__(self, parent, id, title, pos, size, style) - intro = 'PyFilling - The Tastiest Namespace Inspector' - self.CreateStatusBar() - self.SetStatusText(intro) - import images - self.SetIcon(images.getPyCrustIcon()) - self.filling = Filling(parent=self, rootObject=rootObject, - rootLabel=rootLabel, - rootIsNamespace=rootIsNamespace, - static=static) - # Override so that status messages go to the status bar. - self.filling.tree.setStatusText = self.SetStatusText - - -class App(wx.wxApp): - """PyFilling standalone application.""" - - def OnInit(self): - wx.wxInitAllImageHandlers() - self.fillingFrame = FillingFrame() - self.fillingFrame.Show(True) - self.SetTopWindow(self.fillingFrame) - return True - - - - diff --git a/wxPython/wxPython/lib/PyCrust/shell.py b/wxPython/wxPython/lib/PyCrust/shell.py index f522356f09..2640aac7ec 100644 --- a/wxPython/wxPython/lib/PyCrust/shell.py +++ b/wxPython/wxPython/lib/PyCrust/shell.py @@ -1,1187 +1,3 @@ -"""The PyCrust 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. The latest files are always -available at the SourceForge project page at -http://sourceforge.net/projects/pycrust/. -Sponsored by Orbtech - Your source for Python programming expertise.""" - -__author__ = "Patrick K. O'Brien " -__cvsid__ = "$Id$" -__revision__ = "$Revision$"[11:-2] - -import keyword -import os -import sys -import time -from pseudo import PseudoFileIn -from pseudo import PseudoFileOut -from pseudo import PseudoFileErr -from shellmenu import ShellMenu -from version import VERSION -import dispatcher - -try: - import wxd.d_wx -except ImportError: - from wxPython import wx -else: - from wxd.d_wx import wx - -try: - import wxd.d_stc -except ImportError: - from wxPython import stc -else: - from wxd.d_stc import stc - -try: - True -except NameError: - True = 1==1 - False = 1==0 - -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) - -if wx.wxPlatform == '__WXMSW__': - faces = { 'times' : 'Times New Roman', - 'mono' : 'Courier New', - 'helv' : 'Lucida Console', - 'lucida' : 'Lucida Console', - 'other' : 'Comic Sans MS', - 'size' : 10, - 'lnsize' : 9, - 'backcol': '#FFFFFF', - } -else: # GTK - faces = { 'times' : 'Times', - 'mono' : 'Courier', - 'helv' : 'Helvetica', - 'other' : 'new century schoolbook', - 'size' : 12, - 'lnsize' : 10, - 'backcol': '#FFFFFF', - } - - -class ShellFrame(wx.wxFrame, ShellMenu): - """Frame containing the PyCrust shell component.""" - - name = 'PyCrust Shell Frame' - revision = __revision__ - - def __init__(self, parent=None, id=-1, title='PyShell', - pos=wx.wxDefaultPosition, size=wx.wxDefaultSize, - style=wx.wxDEFAULT_FRAME_STYLE, locals=None, - InterpClass=None, *args, **kwds): - """Create a PyCrust ShellFrame instance.""" - wx.wxFrame.__init__(self, parent, id, title, pos, size, style) - intro = 'PyCrust %s - The Flakiest Python Shell' % VERSION - intro += '\nSponsored by Orbtech - ' + \ - 'Your source for Python programming expertise.' - self.CreateStatusBar() - self.SetStatusText(intro.replace('\n', ', ')) - import images - self.SetIcon(images.getPyCrustIcon()) - self.shell = Shell(parent=self, id=-1, introText=intro, - locals=locals, InterpClass=InterpClass, - *args, **kwds) - # Override the shell so that status messages go to the status bar. - self.shell.setStatusText = self.SetStatusText - self.createMenus() - wx.EVT_CLOSE(self, self.OnCloseWindow) - - def OnCloseWindow(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: - event.Veto(True) - else: - self.shell.destroy() - self.Destroy() - - -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 = 'PyCrust Shell Interface' - revision = __revision__ - - def __init__(self, other): - """Create a ShellFacade instance.""" - methods = [ - 'about', - 'ask', - 'clear', - 'pause', - 'prompt', - 'quit', - 'redirectStderr', - 'redirectStdin', - 'redirectStdout', - 'run', - 'runfile', - 'wrap', - 'zoom', - ] - for method in methods: - self.__dict__[method] = getattr(other, method) - d = self.__dict__ - d['other'] = other - d['helpText'] = \ -""" -* 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. -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. -""" - - 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 = ['autoCallTip', - 'autoComplete', - 'autoCompleteCaseInsensitive', - 'autoCompleteIncludeDouble', - 'autoCompleteIncludeMagic', - 'autoCompleteIncludeSingle', - ] - list.sort() - return list - - -class Shell(stc.wxStyledTextCtrl): - """PyCrust Shell based on wxStyledTextCtrl.""" - - name = 'PyCrust Shell' - revision = __revision__ - - def __init__(self, parent, id=-1, pos=wx.wxDefaultPosition, - size=wx.wxDefaultSize, style=wx.wxCLIP_CHILDREN, - introText='', locals=None, InterpClass=None, *args, **kwds): - """Create a PyCrust Shell instance.""" - stc.wxStyledTextCtrl.__init__(self, parent, id, pos, size, style) - if locals is None: - locals = {} - # Grab these so they can be restored by self.redirect* methods. - self.stdin = sys.stdin - self.stdout = sys.stdout - self.stderr = sys.stderr - # Add the current working directory "." to the search path. - sys.path.insert(0, os.curdir) - # 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 = 0 - # 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) - # 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 = 0 - # 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 - # Assign handlers for keyboard events. - wx.EVT_KEY_DOWN(self, self.OnKeyDown) - wx.EVT_CHAR(self, self.OnChar) - # Assign handlers for wxSTC events. - stc.EVT_STC_UPDATEUI(self, id, self.OnUpdateUI) - # Assign handler for idle time. - self.waiting = False - wx.EVT_IDLE(self, self.OnIdle) - dispatcher.connect(receiver=self.fontsizer, signal='FontIncrease') - dispatcher.connect(receiver=self.fontsizer, signal='FontDecrease') - dispatcher.connect(receiver=self.fontsizer, signal='FontDefault') - # Configure various defaults and user preferences. - self.config() - # 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() - # Do this last so the user has complete control over their - # environment. They can override anything they want. - self.execStartupScript(self.interp.startupScript) - wx.wxCallAfter(self.ScrollToLine, 0) - - def fontsizer(self, signal): - """Receiver for Font* signals.""" - size = self.GetZoom() - if signal == 'FontIncrease': - size += 1 - elif signal == 'FontDecrease': - size -= 1 - elif signal == 'FontDefault': - size = 0 - self.SetZoom(size) - - def destroy(self): - del self.interp - pass - - def config(self): - """Configure shell based on user preferences.""" - self.SetMarginType(1, stc.wxSTC_MARGIN_NUMBER) - self.SetMarginWidth(1, 40) - - self.SetLexer(stc.wxSTC_LEX_PYTHON) - self.SetKeyWords(0, ' '.join(keyword.kwlist)) - - self.setStyles(faces) - self.SetViewWhiteSpace(0) - self.SetTabWidth(4) - self.SetUseTabs(0) - # Do we want to automatically pop up command completion options? - self.autoComplete = 1 - self.autoCompleteIncludeMagic = 1 - self.autoCompleteIncludeSingle = 1 - self.autoCompleteIncludeDouble = 1 - self.autoCompleteCaseInsensitive = 1 - self.AutoCompSetIgnoreCase(self.autoCompleteCaseInsensitive) - self.AutoCompSetAutoHide(False) - self.AutoCompStops(' .,;:([)]}\'"\\<>%^&+-=*/|`') - # Do we want to automatically pop up command argument help? - self.autoCallTip = 1 - self.CallTipSetBackground(wx.wxColour(255, 255, 232)) - self.wrap() - try: - self.SetEndAtLastLine(False) - except AttributeError: - pass - - 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. - """ - import __builtin__ - __builtin__.close = __builtin__.exit = __builtin__.quit = \ - 'Click on the close button to leave the application.' - - def quit(self): - """Quit the application.""" - - # XXX Good enough for now but later we want to send a close event. - - # In the close event handler we can make sure they want to - # quit. Other applications, like PythonCard, may choose to - # hide rather than quit so we should just post the event and - # let the surrounding app decide what it wants to do. - self.write('Click on the close button to leave the application.') - - def setLocalShell(self): - """Add 'shell' to locals as reference to ShellFacade instance.""" - self.interp.locals['shell'] = ShellFacade(other=self) - - def execStartupScript(self, startupScript): - """Execute the user's PYTHONSTARTUP script if they have one.""" - if startupScript and os.path.isfile(startupScript): - text = 'Startup script executed: ' + startupScript - self.push('print %r; execfile(%r)' % (text, startupScript)) - else: - self.push('') - - def setStyles(self, faces): - """Configure font size, typeface and color for lexer.""" - - # Default style - self.StyleSetSpec(stc.wxSTC_STYLE_DEFAULT, - "face:%(mono)s,size:%(size)d,back:%(backcol)s" % \ - faces) - - self.StyleClearAll() - - # Built in styles - self.StyleSetSpec(stc.wxSTC_STYLE_LINENUMBER, - "back:#C0C0C0,face:%(mono)s,size:%(lnsize)d" % faces) - self.StyleSetSpec(stc.wxSTC_STYLE_CONTROLCHAR, - "face:%(mono)s" % faces) - self.StyleSetSpec(stc.wxSTC_STYLE_BRACELIGHT, - "fore:#0000FF,back:#FFFF88") - self.StyleSetSpec(stc.wxSTC_STYLE_BRACEBAD, - "fore:#FF0000,back:#FFFF88") - - # Python styles - self.StyleSetSpec(stc.wxSTC_P_DEFAULT, - "face:%(mono)s" % faces) - self.StyleSetSpec(stc.wxSTC_P_COMMENTLINE, - "fore:#007F00,face:%(mono)s" % faces) - self.StyleSetSpec(stc.wxSTC_P_NUMBER, - "") - self.StyleSetSpec(stc.wxSTC_P_STRING, - "fore:#7F007F,face:%(mono)s" % faces) - self.StyleSetSpec(stc.wxSTC_P_CHARACTER, - "fore:#7F007F,face:%(mono)s" % faces) - self.StyleSetSpec(stc.wxSTC_P_WORD, - "fore:#00007F,bold") - self.StyleSetSpec(stc.wxSTC_P_TRIPLE, - "fore:#7F0000") - self.StyleSetSpec(stc.wxSTC_P_TRIPLEDOUBLE, - "fore:#000033,back:#FFFFE8") - self.StyleSetSpec(stc.wxSTC_P_CLASSNAME, - "fore:#0000FF,bold") - self.StyleSetSpec(stc.wxSTC_P_DEFNAME, - "fore:#007F7F,bold") - self.StyleSetSpec(stc.wxSTC_P_OPERATOR, - "") - self.StyleSetSpec(stc.wxSTC_P_IDENTIFIER, - "") - self.StyleSetSpec(stc.wxSTC_P_COMMENTBLOCK, - "fore:#7F7F7F") - self.StyleSetSpec(stc.wxSTC_P_STRINGEOL, - "fore:#000000,face:%(mono)s,back:#E0C0E0,eolfilled" % faces) - - def about(self): - """Display information about PyCrust.""" - text = """ -Author: %r -PyCrust Version: %s -Shell Revision: %s -Interpreter Revision: %s -Python Version: %s -wxPython Version: %s -Platform: %s""" % \ - (__author__, VERSION, self.revision, self.interp.revision, - sys.version.split()[0], wx.__version__, sys.platform) - self.write(text.strip()) - - def OnIdle(self, event): - """Free the CPU to do other things.""" - if self.waiting: - time.sleep(0.05) - - def OnUpdateUI(self, event): - """Check for matching braces.""" - # If the auto-complete window is up let it do its thing. - if self.AutoCompActive(): - return - braceAtCaret = -1 - braceOpposite = -1 - charBefore = None - caretPos = self.GetCurrentPos() - if caretPos > 0: - charBefore = self.GetCharAt(caretPos - 1) - styleBefore = self.GetStyleAt(caretPos - 1) - - # Check before. - if charBefore and chr(charBefore) in '[]{}()' \ - and styleBefore == stc.wxSTC_P_OPERATOR: - braceAtCaret = caretPos - 1 - - # Check after. - if braceAtCaret < 0: - charAfter = self.GetCharAt(caretPos) - styleAfter = self.GetStyleAt(caretPos) - if charAfter and chr(charAfter) in '[]{}()' \ - and styleAfter == stc.wxSTC_P_OPERATOR: - braceAtCaret = caretPos - - if braceAtCaret >= 0: - braceOpposite = self.BraceMatch(braceAtCaret) - - if braceAtCaret != -1 and braceOpposite == -1: - self.BraceBadLight(braceAtCaret) - else: - self.BraceHighlight(braceAtCaret, braceOpposite) - - def OnChar(self, event): - """Keypress event handler. - - Only receives an event if OnKeyDown calls event.Skip() for the - corresponding event.""" - - # Prevent modification of previously submitted - # commands/responses. - if not self.CanEdit(): - return - key = event.KeyCode() - currpos = self.GetCurrentPos() - stoppos = self.promptPosEnd - # Return (Enter) needs to be ignored in this handler. - if key == wx.WXK_RETURN: - pass - elif key in self.autoCompleteKeys: - # Usually the dot (period) key activates auto completion. - # Get the command between the prompt and the cursor. Add - # the autocomplete character to the end of the command. - if self.AutoCompActive(): - self.AutoCompCancel() - command = self.GetTextRange(stoppos, currpos) + chr(key) - self.write(chr(key)) - if self.autoComplete: - self.autoCompleteShow(command) - elif key == ord('('): - # The left paren activates a call tip and cancels an - # active auto completion. - if self.AutoCompActive(): - self.AutoCompCancel() - # Get the command between the prompt and the cursor. Add - # the '(' to the end of the command. - self.ReplaceSelection('') - command = self.GetTextRange(stoppos, currpos) + '(' - self.write('(') - if self.autoCallTip: - self.autoCallTipShow(command) - else: - # Allow the normal event handling to take place. - event.Skip() - - def OnKeyDown(self, event): - """Key down event handler.""" - - key = event.KeyCode() - # If the auto-complete window is up let it do its thing. - if self.AutoCompActive(): - event.Skip() - return - # Prevent modification of previously submitted - # commands/responses. - controlDown = event.ControlDown() - altDown = event.AltDown() - shiftDown = event.ShiftDown() - currpos = self.GetCurrentPos() - endpos = self.GetTextLength() - selecting = self.GetSelectionStart() != self.GetSelectionEnd() - # Return (Enter) is used to submit a command to the - # interpreter. - if not controlDown and key == wx.WXK_RETURN: - if self.CallTipActive(): - self.CallTipCancel() - self.processLine() - # Ctrl+Return (Cntrl+Enter) is used to insert a line break. - elif controlDown and key == wx.WXK_RETURN: - if self.CallTipActive(): - self.CallTipCancel() - if currpos == endpos: - self.processLine() - else: - self.insertLineBreak() - # Let Ctrl-Alt-* get handled normally. - elif controlDown and altDown: - event.Skip() - # Clear the current, unexecuted command. - elif key == wx.WXK_ESCAPE: - if self.CallTipActive(): - event.Skip() - else: - self.clearCommand() - # Increase font size. - elif controlDown and key in (ord(']'),): - dispatcher.send(signal='FontIncrease') - # Decrease font size. - elif controlDown and key in (ord('['),): - dispatcher.send(signal='FontDecrease') - # Default font size. - elif controlDown and key in (ord('='),): - dispatcher.send(signal='FontDefault') - # Cut to the clipboard. - elif (controlDown and key in (ord('X'), ord('x'))) \ - or (shiftDown and key == wx.WXK_DELETE): - self.Cut() - # Copy to the clipboard. - elif controlDown and not shiftDown \ - and key in (ord('C'), ord('c'), wx.WXK_INSERT): - self.Copy() - # Copy to the clipboard, including prompts. - elif controlDown and shiftDown \ - and key in (ord('C'), ord('c'), wx.WXK_INSERT): - self.CopyWithPrompts() - # Copy to the clipboard, including prefixed prompts. - elif altDown and not controlDown \ - and key in (ord('C'), ord('c'), wx.WXK_INSERT): - self.CopyWithPromptsPrefixed() - # Home needs to be aware of the prompt. - elif key == wx.WXK_HOME: - home = self.promptPosEnd - if currpos > home: - 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 selecting and key not in NAVKEYS and not self.CanEdit(): - pass - # Paste from the clipboard. - elif (controlDown and not shiftDown and key in (ord('V'), ord('v'))) \ - or (shiftDown and not controlDown and key == wx.WXK_INSERT): - self.Paste() - # Paste from the clipboard, run commands. - elif controlDown and shiftDown and key in (ord('V'), ord('v')): - self.PasteAndRun() - # Replace with the previous command from the history buffer. - elif (controlDown and key == wx.WXK_UP) \ - or (altDown and key in (ord('P'), ord('p'))): - self.OnHistoryReplace(step=+1) - # Replace with the next command from the history buffer. - elif (controlDown and key == wx.WXK_DOWN) \ - or (altDown and key in (ord('N'), ord('n'))): - self.OnHistoryReplace(step=-1) - # Insert the previous command from the history buffer. - elif (shiftDown and key == wx.WXK_UP) and self.CanEdit(): - self.OnHistoryInsert(step=+1) - # Insert the next command from the history buffer. - elif (shiftDown and key == wx.WXK_DOWN) and self.CanEdit(): - self.OnHistoryInsert(step=-1) - # Search up the history for the text in front of the cursor. - elif key == wx.WXK_F8: - self.OnHistorySearch() - # Don't backspace over the latest non-continuation prompt. - elif key == wx.WXK_BACK: - if selecting and self.CanEdit(): - event.Skip() - elif currpos > self.promptPosEnd: - event.Skip() - # Only allow these keys after the latest prompt. - elif key in (wx.WXK_TAB, wx.WXK_DELETE): - if self.CanEdit(): - event.Skip() - # Don't toggle between insert mode and overwrite mode. - elif key == wx.WXK_INSERT: - pass - # Don't allow line deletion. - elif controlDown and key in (ord('L'), ord('l')): - pass - # Don't allow line transposition. - elif controlDown and key in (ord('T'), ord('t')): - pass - # Basic navigation keys should work anywhere. - elif key in NAVKEYS: - event.Skip() - # Protect the readonly portion of the shell. - elif not self.CanEdit(): - pass - else: - event.Skip() - - def clearCommand(self): - """Delete the current, unexecuted command.""" - startpos = self.promptPosEnd - endpos = self.GetTextLength() - self.SetSelection(startpos, endpos) - self.ReplaceSelection('') - self.more = 0 - - def OnHistoryReplace(self, step): - """Replace with the previous/next command from the history buffer.""" - self.clearCommand() - self.replaceFromHistory(step) - - def replaceFromHistory(self, step): - """Replace selection with command from the history buffer.""" - ps2 = str(sys.ps2) - self.ReplaceSelection('') - newindex = self.historyIndex + step - if -1 <= newindex <= len(self.history): - self.historyIndex = newindex - if 0 <= newindex <= len(self.history)-1: - command = self.history[self.historyIndex] - command = command.replace('\n', os.linesep + ps2) - self.ReplaceSelection(command) - - def OnHistoryInsert(self, step): - """Insert the previous/next command from the history buffer.""" - if not self.CanEdit(): - return - startpos = self.GetCurrentPos() - self.replaceFromHistory(step) - endpos = self.GetCurrentPos() - self.SetSelection(endpos, startpos) - - def OnHistorySearch(self): - """Search up the history buffer for the text in front of the cursor.""" - if not self.CanEdit(): - return - startpos = self.GetCurrentPos() - # The text up to the cursor is what we search for. - numCharsAfterCursor = self.GetTextLength() - startpos - searchText = self.getCommand(rstrip=0) - if numCharsAfterCursor > 0: - searchText = searchText[:-numCharsAfterCursor] - if not searchText: - return - # Search upwards from the current history position and loop - # back to the beginning if we don't find anything. - if (self.historyIndex <= -1) \ - or (self.historyIndex >= len(self.history)-2): - searchOrder = range(len(self.history)) - else: - searchOrder = range(self.historyIndex+1, len(self.history)) + \ - range(self.historyIndex) - for i in searchOrder: - command = self.history[i] - if command[:len(searchText)] == searchText: - # Replace the current selection with the one we found. - self.ReplaceSelection(command[len(searchText):]) - endpos = self.GetCurrentPos() - self.SetSelection(endpos, startpos) - # We've now warped into middle of the history. - self.historyIndex = i - break - - def setStatusText(self, text): - """Display status information.""" - - # This method will likely be replaced by the enclosing app to - # do something more interesting, like write to a status bar. - print text - - def insertLineBreak(self): - """Insert a new line break.""" - if self.CanEdit(): - self.write(os.linesep) - self.more = 1 - self.prompt() - - def processLine(self): - """Process the line of text at which the user hit Enter.""" - - # The user hit ENTER and we need to decide what to do. They - # could be sitting on any line in the shell. - - thepos = self.GetCurrentPos() - startpos = self.promptPosEnd - endpos = self.GetTextLength() - ps2 = str(sys.ps2) - # If they hit RETURN inside the current command, execute the - # command. - if self.CanEdit(): - self.SetCurrentPos(endpos) - self.interp.more = 0 - command = self.GetTextRange(startpos, endpos) - lines = command.split(os.linesep + ps2) - lines = [line.rstrip() for line in lines] - command = '\n'.join(lines) - if self.reader.isreading: - if not command: - # Match the behavior of the standard Python shell - # when the user hits return without entering a - # value. - command = '\n' - self.reader.input = command - self.write(os.linesep) - else: - self.push(command) - # Or replace the current command with the other command. - else: - # If the line contains a command (even an invalid one). - if self.getCommand(rstrip=0): - command = self.getMultilineCommand() - self.clearCommand() - self.write(command) - # Otherwise, put the cursor back where we started. - else: - self.SetCurrentPos(thepos) - self.SetAnchor(thepos) - - def getMultilineCommand(self, rstrip=1): - """Extract a multi-line command from the editor. - - The command may not necessarily be valid Python syntax.""" - # XXX Need to extract real prompts here. Need to keep track of - # the prompt every time a command is issued. - ps1 = str(sys.ps1) - ps1size = len(ps1) - ps2 = str(sys.ps2) - ps2size = len(ps2) - # This is a total hack job, but it works. - text = self.GetCurLine()[0] - line = self.GetCurrentLine() - while text[:ps2size] == ps2 and line > 0: - line -= 1 - self.GotoLine(line) - text = self.GetCurLine()[0] - if text[:ps1size] == ps1: - line = self.GetCurrentLine() - self.GotoLine(line) - startpos = self.GetCurrentPos() + ps1size - line += 1 - self.GotoLine(line) - while self.GetCurLine()[0][:ps2size] == ps2: - line += 1 - self.GotoLine(line) - stoppos = self.GetCurrentPos() - command = self.GetTextRange(startpos, stoppos) - command = command.replace(os.linesep + ps2, '\n') - command = command.rstrip() - command = command.replace('\n', os.linesep + ps2) - else: - command = '' - if rstrip: - command = command.rstrip() - return command - - def getCommand(self, text=None, rstrip=1): - """Extract a command from text which may include a shell prompt. - - The command may not necessarily be valid Python syntax.""" - if not text: - text = self.GetCurLine()[0] - # Strip the prompt off the front leaving just the command. - command = self.lstripPrompt(text) - if command == text: - command = '' # Real commands have prompts. - if rstrip: - command = command.rstrip() - return command - - def lstripPrompt(self, text): - """Return text without a leading prompt.""" - ps1 = str(sys.ps1) - ps1size = len(ps1) - ps2 = str(sys.ps2) - ps2size = len(ps2) - # Strip the prompt off the front of text. - if text[:ps1size] == ps1: - text = text[ps1size:] - elif text[:ps2size] == ps2: - text = text[ps2size:] - return text - - def push(self, command): - """Send command to the interpreter for execution.""" - self.write(os.linesep) - busy = wx.wxBusyCursor() - self.waiting = True - self.more = self.interp.push(command) - self.waiting = False - del busy - if not self.more: - self.addHistory(command.rstrip()) - self.prompt() - - def addHistory(self, command): - """Add command to the command history.""" - # Reset the history position. - self.historyIndex = -1 - # Insert this command into the history, unless it's a blank - # line or the same as the last command. - if command != '' \ - and (len(self.history) == 0 or command != self.history[0]): - self.history.insert(0, command) - - def write(self, text): - """Display text in the shell. - - Replace line endings with OS-specific endings.""" - text = self.fixLineEndings(text) - self.AddText(text) - self.EnsureCaretVisible() - - def fixLineEndings(self, text): - """Return text with line endings replaced by OS-specific endings.""" - lines = text.split('\r\n') - for l in range(len(lines)): - chunks = lines[l].split('\r') - for c in range(len(chunks)): - chunks[c] = os.linesep.join(chunks[c].split('\n')) - lines[l] = os.linesep.join(chunks) - text = os.linesep.join(lines) - return text - - def prompt(self): - """Display proper prompt for the context: ps1, ps2 or ps3. - - If this is a continuation line, autoindent as necessary.""" - isreading = self.reader.isreading - skip = 0 - if isreading: - prompt = str(sys.ps3) - elif self.more: - prompt = str(sys.ps2) - else: - prompt = str(sys.ps1) - pos = self.GetCurLine()[1] - if pos > 0: - if isreading: - skip = 1 - else: - self.write(os.linesep) - if not self.more: - self.promptPosStart = self.GetCurrentPos() - if not skip: - self.write(prompt) - if not self.more: - self.promptPosEnd = self.GetCurrentPos() - # Keep the undo feature from undoing previous responses. - self.EmptyUndoBuffer() - # XXX Add some autoindent magic here if more. - if self.more: - self.write(' '*4) # Temporary hack indentation. - self.EnsureCaretVisible() - self.ScrollToColumn(0) - - def readline(self): - """Replacement for stdin.readline().""" - input = '' - reader = self.reader - reader.isreading = 1 - self.prompt() - try: - while not reader.input: - wx.wxYieldIfNeeded() - input = reader.input - finally: - reader.input = '' - reader.isreading = 0 - input = str(input) # In case of Unicode. - return input - - def readlines(self): - """Replacement for stdin.readlines().""" - lines = [] - while lines[-1:] != ['\n']: - lines.append(self.readline()) - return lines - - def raw_input(self, prompt=''): - """Return string based on user input.""" - if prompt: - self.write(prompt) - return self.readline() - - def ask(self, prompt='Please enter your response:'): - """Get response from the user using a dialog box.""" - dialog = wx.wxTextEntryDialog(None, prompt, - 'Input Dialog (Raw)', '') - try: - if dialog.ShowModal() == wx.wxID_OK: - text = dialog.GetValue() - return text - finally: - dialog.Destroy() - return '' - - def pause(self): - """Halt execution pending a response from the user.""" - self.ask('Press enter to continue:') - - def clear(self): - """Delete all text from the shell.""" - self.ClearAll() - - def run(self, command, prompt=1, verbose=1): - """Execute command as if it was typed in directly. - >>> shell.run('print "this"') - >>> print "this" - this - >>> - """ - # Go to the very bottom of the text. - endpos = self.GetTextLength() - self.SetCurrentPos(endpos) - command = command.rstrip() - if prompt: self.prompt() - if verbose: self.write(command) - self.push(command) - - def runfile(self, filename): - """Execute all commands in file as if they were typed into the - shell.""" - file = open(filename) - try: - self.prompt() - for command in file.readlines(): - if command[:6] == 'shell.': - # Run shell methods silently. - self.run(command, prompt=0, verbose=0) - else: - self.run(command, prompt=0, verbose=1) - finally: - file.close() - - def autoCompleteShow(self, command): - """Display auto-completion popup list.""" - list = self.interp.getAutoCompleteList(command, - includeMagic=self.autoCompleteIncludeMagic, - includeSingle=self.autoCompleteIncludeSingle, - includeDouble=self.autoCompleteIncludeDouble) - if list and len(list) < 2000: - options = ' '.join(list) - offset = 0 - self.AutoCompShow(offset, options) - - def autoCallTipShow(self, command): - """Display argument spec and docstring in a popup window.""" - if self.CallTipActive(): - self.CallTipCancel() - (name, argspec, tip) = self.interp.getCallTip(command) - if argspec: - startpos = self.GetCurrentPos() - self.write(argspec + ')') - endpos = self.GetCurrentPos() - self.SetSelection(endpos, startpos) - if tip: - curpos = self.GetCurrentPos() - tippos = curpos - (len(name) + 1) - fallback = curpos - self.GetColumn(curpos) - # In case there isn't enough room, only go back to the - # fallback. - tippos = max(tippos, fallback) - dispatcher.send(signal='Shell.calltip', sender=self, calltip=tip) - self.CallTipShow(tippos, tip) - - def writeOut(self, text): - """Replacement for stdout.""" - self.write(text) - - def writeErr(self, text): - """Replacement for stderr.""" - self.write(text) - - def redirectStdin(self, redirect=1): - """If redirect is true then sys.stdin will come from the shell.""" - if redirect: - sys.stdin = self.reader - else: - sys.stdin = self.stdin - - def redirectStdout(self, redirect=1): - """If redirect is true then sys.stdout will go to the shell.""" - if redirect: - sys.stdout = PseudoFileOut(self.writeOut) - else: - sys.stdout = self.stdout - - def redirectStderr(self, redirect=1): - """If redirect is true then sys.stderr will go to the shell.""" - if redirect: - sys.stderr = PseudoFileErr(self.writeErr) - else: - sys.stderr = self.stderr - - def CanCut(self): - """Return true if text is selected and can be cut.""" - if self.GetSelectionStart() != self.GetSelectionEnd() \ - and self.GetSelectionStart() >= self.promptPosEnd \ - and self.GetSelectionEnd() >= self.promptPosEnd: - return 1 - else: - return 0 - - def CanCopy(self): - """Return true if text is selected and can be copied.""" - return self.GetSelectionStart() != self.GetSelectionEnd() - - def CanPaste(self): - """Return true if a paste should succeed.""" - if self.CanEdit() and stc.wxStyledTextCtrl.CanPaste(self): - return 1 - else: - return 0 - - def CanEdit(self): - """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 - - def Cut(self): - """Remove selection and place it on the clipboard.""" - if self.CanCut() and self.CanCopy(): - if self.AutoCompActive(): - self.AutoCompCancel() - if self.CallTipActive(): - self.CallTipCancel() - self.Copy() - self.ReplaceSelection('') - - def Copy(self): - """Copy selection and place it on the clipboard.""" - if self.CanCopy(): - ps1 = str(sys.ps1) - ps2 = str(sys.ps2) - command = self.GetSelectedText() - command = command.replace(os.linesep + ps2, os.linesep) - command = command.replace(os.linesep + ps1, os.linesep) - command = self.lstripPrompt(text=command) - data = wx.wxTextDataObject(command) - self._clip(data) - - def CopyWithPrompts(self): - """Copy selection, including prompts, and place it on the clipboard.""" - if self.CanCopy(): - command = self.GetSelectedText() - data = wx.wxTextDataObject(command) - self._clip(data) - - def CopyWithPromptsPrefixed(self): - """Copy selection, including prompts prefixed with four - spaces, and place it on the clipboard.""" - if self.CanCopy(): - command = self.GetSelectedText() - spaces = ' ' * 4 - command = spaces + command.replace(os.linesep, - os.linesep + spaces) - data = wx.wxTextDataObject(command) - self._clip(data) - - def _clip(self, data): - if wx.wxTheClipboard.Open(): - wx.wxTheClipboard.UsePrimarySelection(False) - wx.wxTheClipboard.SetData(data) - wx.wxTheClipboard.Flush() - wx.wxTheClipboard.Close() - - def Paste(self): - """Replace selection with clipboard contents.""" - if self.CanPaste() and wx.wxTheClipboard.Open(): - ps2 = str(sys.ps2) - if wx.wxTheClipboard.IsSupported(wx.wxDataFormat(wx.wxDF_TEXT)): - data = wx.wxTextDataObject() - if wx.wxTheClipboard.GetData(data): - self.ReplaceSelection('') - command = data.GetText() - command = command.rstrip() - command = self.fixLineEndings(command) - command = self.lstripPrompt(text=command) - command = command.replace(os.linesep + ps2, '\n') - command = command.replace(os.linesep, '\n') - command = command.replace('\n', os.linesep + ps2) - self.write(command) - wx.wxTheClipboard.Close() - - def PasteAndRun(self): - """Replace selection with clipboard contents, run commands.""" - if wx.wxTheClipboard.Open(): - ps1 = str(sys.ps1) - ps2 = str(sys.ps2) - if wx.wxTheClipboard.IsSupported(wx.wxDataFormat(wx.wxDF_TEXT)): - data = wx.wxTextDataObject() - if wx.wxTheClipboard.GetData(data): - endpos = self.GetTextLength() - self.SetCurrentPos(endpos) - startpos = self.promptPosEnd - self.SetSelection(startpos, endpos) - self.ReplaceSelection('') - text = data.GetText() - text = text.lstrip() - text = self.fixLineEndings(text) - text = self.lstripPrompt(text) - text = text.replace(os.linesep + ps1, '\n') - text = text.replace(os.linesep + ps2, '\n') - text = text.replace(os.linesep, '\n') - lines = text.split('\n') - commands = [] - command = '' - for line in lines: - if line.strip() == ps2.strip(): - # If we are pasting from something like a - # web page that drops the trailing space - # from the ps2 prompt of a blank line. - line = '' - if line.strip() != '' and line.lstrip() == line: - # New command. - if command: - # Add the previous command to the list. - commands.append(command) - # Start a new command, which may be multiline. - command = line - else: - # Multiline command. Add to the command. - command += '\n' - command += line - commands.append(command) - for command in commands: - command = command.replace('\n', os.linesep + ps2) - self.write(command) - self.processLine() - wx.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) +from wxPython.py.shell import * diff --git a/wxPython/wxPython/lib/PyCrust/shellmenu.py b/wxPython/wxPython/lib/PyCrust/shellmenu.py deleted file mode 100644 index 1e3175a1f7..0000000000 --- a/wxPython/wxPython/lib/PyCrust/shellmenu.py +++ /dev/null @@ -1,226 +0,0 @@ -"""Shell menu mixin shared by shell and crust.""" - -__author__ = "Patrick K. O'Brien " -__cvsid__ = "$Id$" -__revision__ = "$Revision$"[11:-2] - -from wxPython import wx -import sys -from version import VERSION - -try: - True -except NameError: - True = 1==1 - False = 1==0 - -ID_AUTOCOMP = wx.wxNewId() -ID_AUTOCOMP_SHOW = wx.wxNewId() -ID_AUTOCOMP_INCLUDE_MAGIC = wx.wxNewId() -ID_AUTOCOMP_INCLUDE_SINGLE = wx.wxNewId() -ID_AUTOCOMP_INCLUDE_DOUBLE = wx.wxNewId() -ID_CALLTIPS = wx.wxNewId() -ID_CALLTIPS_SHOW = wx.wxNewId() -ID_COPY_PLUS = wx.wxNewId() -ID_PASTE_PLUS = wx.wxNewId() -ID_WRAP = wx.wxNewId() - - -class ShellMenu: - """Mixin class to add standard menu items.""" - - def createMenus(self): - m = self.fileMenu = wx.wxMenu() - m.AppendSeparator() - m.Append(wx.wxID_EXIT, 'E&xit', 'Exit PyCrust') - - m = self.editMenu = wx.wxMenu() - m.Append(wx.wxID_UNDO, '&Undo \tCtrl+Z', - 'Undo the last action') - m.Append(wx.wxID_REDO, '&Redo \tCtrl+Y', - 'Redo the last undone action') - m.AppendSeparator() - m.Append(wx.wxID_CUT, 'Cu&t \tCtrl+X', - 'Cut the selection') - m.Append(wx.wxID_COPY, '&Copy \tCtrl+C', - 'Copy the selection - removing prompts') - m.Append(ID_COPY_PLUS, 'Cop&y Plus \tCtrl+Shift+C', - 'Copy the selection - retaining prompts') - m.Append(wx.wxID_PASTE, '&Paste \tCtrl+V', 'Paste') - m.Append(ID_PASTE_PLUS, 'Past&e Plus \tCtrl+Shift+V', - 'Paste and run commands') - m.AppendSeparator() - m.Append(wx.wxID_CLEAR, 'Cle&ar', - 'Delete the selection') - m.Append(wx.wxID_SELECTALL, 'Select A&ll \tCtrl+A', - 'Select all text') - - m = self.autocompMenu = wx.wxMenu() - m.Append(ID_AUTOCOMP_SHOW, 'Show Auto Completion', - 'Show auto completion during dot syntax', 1) - m.Append(ID_AUTOCOMP_INCLUDE_MAGIC, 'Include Magic Attributes', - 'Include attributes visible to __getattr__ and __setattr__', - 1) - m.Append(ID_AUTOCOMP_INCLUDE_SINGLE, 'Include Single Underscores', - 'Include attibutes prefixed by a single underscore', 1) - m.Append(ID_AUTOCOMP_INCLUDE_DOUBLE, 'Include Double Underscores', - 'Include attibutes prefixed by a double underscore', 1) - - m = self.calltipsMenu = wx.wxMenu() - m.Append(ID_CALLTIPS_SHOW, 'Show Call Tips', - 'Show call tips with argument specifications', 1) - - m = self.optionsMenu = wx.wxMenu() - m.AppendMenu(ID_AUTOCOMP, '&Auto Completion', self.autocompMenu, - 'Auto Completion Options') - m.AppendMenu(ID_CALLTIPS, '&Call Tips', self.calltipsMenu, - 'Call Tip Options') - m.Append(ID_WRAP, '&Wrap Lines', - 'Wrap lines at right edge', 1) - - m = self.helpMenu = wx.wxMenu() - m.AppendSeparator() - m.Append(wx.wxID_ABOUT, '&About...', 'About PyCrust') - - b = self.menuBar = wx.wxMenuBar() - b.Append(self.fileMenu, '&File') - b.Append(self.editMenu, '&Edit') - b.Append(self.optionsMenu, '&Options') - b.Append(self.helpMenu, '&Help') - self.SetMenuBar(b) - - wx.EVT_MENU(self, wx.wxID_EXIT, self.OnExit) - wx.EVT_MENU(self, wx.wxID_UNDO, self.OnUndo) - wx.EVT_MENU(self, wx.wxID_REDO, self.OnRedo) - wx.EVT_MENU(self, wx.wxID_CUT, self.OnCut) - wx.EVT_MENU(self, wx.wxID_COPY, self.OnCopy) - wx.EVT_MENU(self, ID_COPY_PLUS, self.OnCopyPlus) - wx.EVT_MENU(self, wx.wxID_PASTE, self.OnPaste) - wx.EVT_MENU(self, ID_PASTE_PLUS, self.OnPastePlus) - wx.EVT_MENU(self, wx.wxID_CLEAR, self.OnClear) - wx.EVT_MENU(self, wx.wxID_SELECTALL, self.OnSelectAll) - wx.EVT_MENU(self, wx.wxID_ABOUT, self.OnAbout) - wx.EVT_MENU(self, ID_AUTOCOMP_SHOW, - self.OnAutoCompleteShow) - wx.EVT_MENU(self, ID_AUTOCOMP_INCLUDE_MAGIC, - self.OnAutoCompleteIncludeMagic) - wx.EVT_MENU(self, ID_AUTOCOMP_INCLUDE_SINGLE, - self.OnAutoCompleteIncludeSingle) - wx.EVT_MENU(self, ID_AUTOCOMP_INCLUDE_DOUBLE, - self.OnAutoCompleteIncludeDouble) - wx.EVT_MENU(self, ID_CALLTIPS_SHOW, - self.OnCallTipsShow) - wx.EVT_MENU(self, ID_WRAP, self.OnWrap) - - wx.EVT_UPDATE_UI(self, wx.wxID_UNDO, self.OnUpdateMenu) - wx.EVT_UPDATE_UI(self, wx.wxID_REDO, self.OnUpdateMenu) - wx.EVT_UPDATE_UI(self, wx.wxID_CUT, self.OnUpdateMenu) - wx.EVT_UPDATE_UI(self, wx.wxID_COPY, self.OnUpdateMenu) - wx.EVT_UPDATE_UI(self, ID_COPY_PLUS, self.OnUpdateMenu) - wx.EVT_UPDATE_UI(self, wx.wxID_PASTE, self.OnUpdateMenu) - wx.EVT_UPDATE_UI(self, ID_PASTE_PLUS, self.OnUpdateMenu) - wx.EVT_UPDATE_UI(self, wx.wxID_CLEAR, self.OnUpdateMenu) - wx.EVT_UPDATE_UI(self, ID_AUTOCOMP_SHOW, self.OnUpdateMenu) - wx.EVT_UPDATE_UI(self, ID_AUTOCOMP_INCLUDE_MAGIC, self.OnUpdateMenu) - wx.EVT_UPDATE_UI(self, ID_AUTOCOMP_INCLUDE_SINGLE, self.OnUpdateMenu) - wx.EVT_UPDATE_UI(self, ID_AUTOCOMP_INCLUDE_DOUBLE, self.OnUpdateMenu) - wx.EVT_UPDATE_UI(self, ID_CALLTIPS_SHOW, self.OnUpdateMenu) - wx.EVT_UPDATE_UI(self, ID_WRAP, self.OnUpdateMenu) - - def OnExit(self, event): - self.Close(True) - - def OnUndo(self, event): - self.shell.Undo() - - def OnRedo(self, event): - self.shell.Redo() - - def OnCut(self, event): - self.shell.Cut() - - def OnCopy(self, event): - self.shell.Copy() - - def OnCopyPlus(self, event): - self.shell.CopyWithPrompts() - - def OnPaste(self, event): - self.shell.Paste() - - def OnPastePlus(self, event): - self.shell.PasteAndRun() - - def OnClear(self, event): - self.shell.Clear() - - def OnSelectAll(self, event): - self.shell.SelectAll() - - def OnAbout(self, event): - """Display an About PyCrust 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 + \ - 'Python Version: %s\n' % sys.version.split()[0] + \ - 'wxPython Version: %s\n' % wx.__version__ + \ - 'Platform: %s\n' % sys.platform - dialog = wx.wxMessageDialog(self, text, title, - wx.wxOK | wx.wxICON_INFORMATION) - dialog.ShowModal() - dialog.Destroy() - - def OnAutoCompleteShow(self, event): - self.shell.autoComplete = event.IsChecked() - - def OnAutoCompleteIncludeMagic(self, event): - self.shell.autoCompleteIncludeMagic = event.IsChecked() - - def OnAutoCompleteIncludeSingle(self, event): - self.shell.autoCompleteIncludeSingle = event.IsChecked() - - def OnAutoCompleteIncludeDouble(self, event): - self.shell.autoCompleteIncludeDouble = event.IsChecked() - - def OnCallTipsShow(self, event): - self.shell.autoCallTip = event.IsChecked() - - def OnWrap(self, event): - self.shell.SetWrapMode(event.IsChecked()) - - def OnUpdateMenu(self, event): - """Update menu items based on current status.""" - id = event.GetId() - if id == wx.wxID_UNDO: - event.Enable(self.shell.CanUndo()) - elif id == wx.wxID_REDO: - event.Enable(self.shell.CanRedo()) - elif id == wx.wxID_CUT: - event.Enable(self.shell.CanCut()) - elif id == wx.wxID_COPY: - event.Enable(self.shell.CanCopy()) - elif id == ID_COPY_PLUS: - event.Enable(self.shell.CanCopy()) - elif id == wx.wxID_PASTE: - event.Enable(self.shell.CanPaste()) - elif id == ID_PASTE_PLUS: - event.Enable(self.shell.CanPaste()) - elif id == wx.wxID_CLEAR: - event.Enable(self.shell.CanCut()) - elif id == ID_AUTOCOMP_SHOW: - event.Check(self.shell.autoComplete) - elif id == ID_AUTOCOMP_INCLUDE_MAGIC: - event.Check(self.shell.autoCompleteIncludeMagic) - elif id == ID_AUTOCOMP_INCLUDE_SINGLE: - event.Check(self.shell.autoCompleteIncludeSingle) - elif id == ID_AUTOCOMP_INCLUDE_DOUBLE: - event.Check(self.shell.autoCompleteIncludeDouble) - elif id == ID_CALLTIPS_SHOW: - event.Check(self.shell.autoCallTip) - elif id == ID_WRAP: - event.Check(self.shell.GetWrapMode()) - diff --git a/wxPython/wxPython/lib/PyCrust/wrap.py b/wxPython/wxPython/lib/PyCrust/wrap.py index cdfbfee359..e683be8ef8 100644 --- a/wxPython/wxPython/lib/PyCrust/wrap.py +++ b/wxPython/wxPython/lib/PyCrust/wrap.py @@ -1,54 +1,5 @@ -"""Wrap is a command line utility that runs a wxPython program with -additional runtime-tools, such as PyCrust.""" - -__author__ = "Patrick K. O'Brien " -__cvsid__ = "$Id$" -__revision__ = "$Revision$"[11:-2] - -import os -import sys -from wxPython import wx -from crust import CrustFrame as Frame - -try: - True -except NameError: - True = 1==1 - False = 1==0 - - -def wrap(app): - wx.wxInitAllImageHandlers() - frame = Frame() - frame.SetSize((750, 525)) - frame.Show(True) - frame.shell.interp.locals['app'] = app - app.MainLoop() - - -def main(argv): - if len(argv) < 2: - print "Please specify a module name." - raise SystemExit - name = argv[1] - if name[-3:] == '.py': - name = name[:-3] - module = __import__(name) - # Find the App class. - App = None - d = module.__dict__ - for item in d.keys(): - try: - if issubclass(d[item], wx.wxApp): - App = d[item] - except (NameError, TypeError): - pass - if App is None: - print "No App class found." - raise SystemExit - app = App() - wrap(app) +from wxPython.py.PyWrap import * if __name__ == '__main__': diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Sizers.py b/wxPython/wxPython/lib/PyCrust/wxd/Sizers.py deleted file mode 100644 index 44446b3d51..0000000000 --- a/wxPython/wxPython/lib/PyCrust/wxd/Sizers.py +++ /dev/null @@ -1,488 +0,0 @@ -"""Decorator classes for documentation and shell scripting. -""" - -__author__ = "Patrick K. O'Brien " -__cvsid__ = "$Id$" -__revision__ = "$Revision$"[11:-2] - - -# These are not the real wxPython classes. These are Python versions -# for documentation purposes. They are also used to apply docstrings -# to the real wxPython classes, which are SWIG-generated wrappers for -# C-language classes. - - -from Base import Object -import Parameters as wx - - -class Sizer(Object): - """""" - - def Add(self): - """""" - pass - - def AddMany(self): - """""" - pass - - def AddSizer(self): - """""" - pass - - def AddSpacer(self): - """""" - pass - - def AddWindow(self): - """""" - pass - - def Clear(self): - """""" - pass - - def DeleteWindows(self): - """""" - pass - - def Destroy(self): - """""" - pass - - def Fit(self): - """""" - pass - - def FitInside(self): - """""" - pass - - def GetChildren(self): - """""" - pass - - def GetMinSize(self): - """""" - pass - - def GetMinSizeTuple(self): - """""" - pass - - def GetPosition(self): - """""" - pass - - def GetPositionTuple(self): - """""" - pass - - def GetSize(self): - """""" - pass - - def GetSizeTuple(self): - """""" - pass - - def Hide(self): - """""" - pass - - def HideSizer(self): - """""" - pass - - def HideWindow(self): - """""" - pass - - def Insert(self): - """""" - pass - - def InsertSizer(self): - """""" - pass - - def InsertSpacer(self): - """""" - pass - - def InsertWindow(self): - """""" - pass - - def IsShown(self): - """""" - pass - - def IsShownSizer(self): - """""" - pass - - def IsShownWindow(self): - """""" - pass - - def Layout(self): - """""" - pass - - def Prepend(self): - """""" - pass - - def PrependSizer(self): - """""" - pass - - def PrependSpacer(self): - """""" - pass - - def PrependWindow(self): - """""" - pass - - def Remove(self): - """""" - pass - - def RemovePos(self): - """""" - pass - - def RemoveSizer(self): - """""" - pass - - def RemoveWindow(self): - """""" - pass - - def SetDimension(self): - """""" - pass - - def SetItemMinSize(self): - """""" - pass - - def SetItemMinSizePos(self): - """""" - pass - - def SetItemMinSizeSizer(self): - """""" - pass - - def SetItemMinSizeWindow(self): - """""" - pass - - def SetMinSize(self): - """""" - pass - - def SetSizeHints(self): - """""" - pass - - def SetVirtualSizeHints(self): - """""" - pass - - def Show(self): - """""" - pass - - def ShowItems(self): - """""" - pass - - def ShowSizer(self): - """""" - pass - - def ShowWindow(self): - """""" - pass - - def __init__(self): - """""" - pass - - def _setOORInfo(self): - """""" - pass - - -class SizerItem(Object): - """""" - - def CalcMin(self): - """""" - pass - - def DeleteWindows(self): - """""" - pass - - def GetBorder(self): - """""" - pass - - def GetFlag(self): - """""" - pass - - def GetOption(self): - """""" - pass - - def GetPosition(self): - """""" - pass - - def GetRatio(self): - """""" - pass - - def GetSize(self): - """""" - pass - - def GetSizer(self): - """""" - pass - - def GetUserData(self): - """""" - pass - - def GetWindow(self): - """""" - pass - - def IsShown(self): - """""" - pass - - def IsSizer(self): - """""" - pass - - def IsSpacer(self): - """""" - pass - - def IsWindow(self): - """""" - pass - - def SetBorder(self): - """""" - pass - - def SetDimension(self): - """""" - pass - - def SetFlag(self): - """""" - pass - - def SetInitSize(self): - """""" - pass - - def SetOption(self): - """""" - pass - - def SetRatio(self): - """""" - pass - - def SetRatioSize(self): - """""" - pass - - def SetRatioWH(self): - """""" - pass - - def SetSizer(self): - """""" - pass - - def SetWindow(self): - """""" - pass - - def Show(self): - """""" - pass - - def __init__(self): - """""" - pass - - -class BoxSizer(Sizer): - """""" - - def CalcMin(self): - """""" - pass - - def GetOrientation(self): - """""" - pass - - def RecalcSizes(self): - """""" - pass - - def SetOrientation(self): - """""" - pass - - def __init__(self): - """""" - pass - - -class GridSizer(Sizer): - """""" - - def CalcMin(self): - """""" - pass - - def GetCols(self): - """""" - pass - - def GetHGap(self): - """""" - pass - - def GetRows(self): - """""" - pass - - def GetVGap(self): - """""" - pass - - def RecalcSizes(self): - """""" - pass - - def SetCols(self): - """""" - pass - - def SetHGap(self): - """""" - pass - - def SetRows(self): - """""" - pass - - def SetVGap(self): - """""" - pass - - def __init__(self): - """""" - pass - - -class FlexGridSizer(GridSizer): - """""" - - def AddGrowableCol(self): - """""" - pass - - def AddGrowableRow(self): - """""" - pass - - def CalcMin(self): - """""" - pass - - def RecalcSizes(self): - """""" - pass - - def RemoveGrowableCol(self): - """""" - pass - - def RemoveGrowableRow(self): - """""" - pass - - def __init__(self): - """""" - pass - - -class NotebookSizer(Sizer): - """""" - - def CalcMin(self): - """""" - pass - - def GetNotebook(self): - """""" - pass - - def RecalcSizes(self): - """""" - pass - - def __init__(self): - """""" - pass - - -class PySizer(Sizer): - """""" - - def __init__(self): - """""" - pass - - def _setCallbackInfo(self): - """""" - pass - - -class StaticBoxSizer(BoxSizer): - """""" - - def CalcMin(self): - """""" - pass - - def GetStaticBox(self): - """""" - pass - - def RecalcSizes(self): - """""" - pass - - def __init__(self): - """""" - pass diff --git a/wxPython/wxPython/lib/PyCrust/CHANGES.txt b/wxPython/wxPython/py/CHANGES.txt similarity index 95% rename from wxPython/wxPython/lib/PyCrust/CHANGES.txt rename to wxPython/wxPython/py/CHANGES.txt index 1e2bdafe8e..7e819962a4 100644 --- a/wxPython/wxPython/lib/PyCrust/CHANGES.txt +++ b/wxPython/wxPython/py/CHANGES.txt @@ -7,6 +7,35 @@ ----------------------------------------- +0.9.1 (3/21/2003 to //2003) +============================== + +Fixed bug in introspect.py on introspecting objects occurring +immediately after a secondary prompt, like this: + + >>> l = [1, 2, 3] + >>> for n in range(3): + ... l. <-- failed to popup autocomplete list + +Added documentation files: + +* MANUAL.txt (in PyCrust) +* wxPython.txt (in PyCrust/wxd) +* wx.txt (in wx) +* examples.txt (in wx/examples) + +Added PyAlaMode.py and PyAlaCarte code editors. + +Major refactoring to support editor and shell from the same base. + +Renamed program files: + +* PyCrustApp.py to PyCrust.py +* PyFillingApp.py to PyFilling.py +* PyShellApp.py to PyShell.py +* wrap.py to PyWrap.py + + 0.9 (2/27/2003 to 3/20/2003) ============================ diff --git a/wxPython/wxPython/py/MANUAL.txt b/wxPython/wxPython/py/MANUAL.txt new file mode 100644 index 0000000000..043d885870 --- /dev/null +++ b/wxPython/wxPython/py/MANUAL.txt @@ -0,0 +1,76 @@ +=============== + The Py Manual +=============== + +------------------------- + Py - Served Fresh Daily +------------------------- + +:Author: Patrick K. O'Brien +:Contact: pobrien@orbtech.com +:Date: $Date$ +:Revision: $Revision$ + +.. contents:: + + +Introduction +============ + +This document will show you how to make use of the Py programs and +library of modules. + + +What is Py? +=========== + +Py is really several things. Py is a set of standalone programs as +well as a library of modules that you can use in your own programs. + +First, Py contains standalone programs that provide code editors and +graphical, Python shell interfaces. Second, Py contains a collections +of modules that you can use in your own wxPython applications to +provide similar services, either for your own use during development, +or as an interface for users of your program. Third, Py containss a +wrapper utility, providing you with runtime introspection capabilities +for your wxPython programs without having to include PyCrust or +PyShell in your program, or alter one line of your code. + + +Py standalone programs +====================== + +There are several standalone applications in the Py package: + +* PyAlaCarte +* PyAlaMode +* PyCrust +* PyFilling +* PyShell +* PyWrap + + +Py modules +========== + +Py was designed to be modular. That means graphical code is kept +separate from non-graphical code, and many of the Py modules can be +used by other programs. Likewise, other programs can supply some of +the modules needed by Py. For example, you could supply a customized +interpreter module and plug it in to the PyCrust standalone +application. As long as it supports the minimum functionality +required, PyCrust will work just as well with your interpreter as with +its default interpreter. + + +Py runtime wrapper +================== + +The Py wrapper utility (``PyWrap.py``) lets you run an existing +wxPython program with a PyCrust frame at the same time. Inside the +PyCrust shell, the local variable ``app`` is assigned to your +application instance. In this way you can introspect your entire +application within the PyCrust shell and the PyFilling namespace +viewer. And through the use of the Py decorator classes, PyCrust can +display wxPython function and method signatures as well as docstrings +for the entire wxPython library. diff --git a/wxPython/wxPython/lib/PyCrust/PyCrust.ico b/wxPython/wxPython/py/Py.ico similarity index 100% rename from wxPython/wxPython/lib/PyCrust/PyCrust.ico rename to wxPython/wxPython/py/Py.ico diff --git a/wxPython/wxPython/py/PyAlaCarte.py b/wxPython/wxPython/py/PyAlaCarte.py new file mode 100755 index 0000000000..ebb134a532 --- /dev/null +++ b/wxPython/wxPython/py/PyAlaCarte.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +"""PyAlaCarte is a simple programmer's editor.""" + +__author__ = "Patrick K. O'Brien " +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +from wxPython import wx + +import os +import sys + +import editor + +try: + True +except NameError: + True = 1==1 + False = 1==0 + +class App(wx.wxApp): + """PyAlaCarte standalone application.""" + + def __init__(self, filename=None): + self.filename = filename + wx.wxApp.__init__(self, redirect=False) + + def OnInit(self): + wx.wxInitAllImageHandlers() + self.frame = editor.EditorFrame(filename=self.filename) + self.frame.Show() + self.SetTopWindow(self.frame) + return True + +def main(filename=None): + app = App(filename) + app.MainLoop() + +if __name__ == '__main__': + filename = None + if len(sys.argv) > 1: + filename = os.path.realpath(sys.argv[1]) + main(filename) diff --git a/wxPython/wxPython/py/PyAlaMode.py b/wxPython/wxPython/py/PyAlaMode.py new file mode 100755 index 0000000000..5ac5d54210 --- /dev/null +++ b/wxPython/wxPython/py/PyAlaMode.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +"""PyAlaMode is a programmer's editor.""" + +__author__ = "Patrick K. O'Brien " +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +from wxPython import wx + +import os +import sys + +import editor + +try: + True +except NameError: + True = 1==1 + False = 1==0 + +class App(wx.wxApp): + """PyAlaMode standalone application.""" + + def __init__(self, filename=None): + self.filename = filename + wx.wxApp.__init__(self, redirect=False) + + def OnInit(self): + wx.wxInitAllImageHandlers() + self.frame = editor.EditorNotebookFrame(filename=self.filename) + self.frame.Show() + self.SetTopWindow(self.frame) + return True + +def main(filename=None): + app = App(filename) + app.MainLoop() + +if __name__ == '__main__': + filename = None + if len(sys.argv) > 1: + filename = os.path.realpath(sys.argv[1]) + main(filename) diff --git a/wxPython/wxPython/py/PyCrust.py b/wxPython/wxPython/py/PyCrust.py new file mode 100755 index 0000000000..f915de1426 --- /dev/null +++ b/wxPython/wxPython/py/PyCrust.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python + +"""PyCrust is a python shell and namespace browser application.""" + +# The next two lines, and the other code below that makes use of +# ``__main__`` and ``original``, serve the purpose of cleaning up the +# main namespace to look as much as possible like the regular Python +# shell environment. +import __main__ +original = __main__.__dict__.keys() + +__author__ = "Patrick K. O'Brien " +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +from wxPython import wx + +try: + True +except NameError: + True = 1==1 + False = 1==0 + + +class App(wx.wxApp): + """PyCrust standalone application.""" + + def OnInit(self): + from wxPython import wx + wx.wxInitAllImageHandlers() + locals = __main__.__dict__ + from crust import CrustFrame + self.frame = CrustFrame(locals=locals) + self.frame.SetSize((800, 600)) + self.frame.Show() + self.SetTopWindow(self.frame) + # Add the application object to the sys module's namespace. + # This allows a shell user to do: + # >>> import sys + # >>> sys.app.whatever + import sys + sys.app = self + return True + +''' +The main() function needs to handle being imported, such as with the +pycrust script that wxPython installs: + + #!/usr/bin/env python + + from wxPython.lib.PyCrust.PyCrustApp import main + main() +''' + +def main(): + import __main__ + md = __main__.__dict__ + keepers = original + keepers.append('App') + for key in md.keys(): + if key not in keepers: + del md[key] + app = App(0) + if md.has_key('App') and md['App'] is App: + del md['App'] + if md.has_key('__main__') and md['__main__'] is __main__: + del md['__main__'] + app.MainLoop() + +if __name__ == '__main__': + main() diff --git a/wxPython/wxPython/py/PyFilling.py b/wxPython/wxPython/py/PyFilling.py new file mode 100755 index 0000000000..5ff6e3c4fd --- /dev/null +++ b/wxPython/wxPython/py/PyFilling.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +"""PyFilling is a python namespace inspection application.""" + +__author__ = "Patrick K. O'Brien " +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +# We use this object to get more introspection when run standalone. +app = None + +import filling + +# These are imported just to have something interesting to inspect. +import crust +import interpreter +import introspect +import pseudo +import shell + +import sys +from wxPython import wx + +try: + True +except NameError: + True = 1==1 + False = 1==0 + + +class App(filling.App): + def OnInit(self): + filling.App.OnInit(self) + self.root = self.fillingFrame.filling.tree.root + return True + +def main(): + """Create and run the application.""" + global app + app = App(0) + app.fillingFrame.filling.tree.Expand(app.root) + app.MainLoop() + + +if __name__ == '__main__': + main() diff --git a/wxPython/wxPython/py/PyShell.py b/wxPython/wxPython/py/PyShell.py new file mode 100755 index 0000000000..fe8a276c6b --- /dev/null +++ b/wxPython/wxPython/py/PyShell.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python + +"""PyShell is a python shell application.""" + +# The next two lines, and the other code below that makes use of +# ``__main__`` and ``original``, serve the purpose of cleaning up the +# main namespace to look as much as possible like the regular Python +# shell environment. +import __main__ +original = __main__.__dict__.keys() + +__author__ = "Patrick K. O'Brien " +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +from wxPython import wx + +try: + True +except NameError: + True = 1==1 + False = 1==0 + + +class App(wx.wxApp): + """PyShell standalone application.""" + + def OnInit(self): + from wxPython import wx + wx.wxInitAllImageHandlers() + locals = __main__.__dict__ + from shell import ShellFrame + self.frame = ShellFrame(locals=locals) + self.frame.SetSize((750, 525)) + self.frame.Show() + self.SetTopWindow(self.frame) + self.frame.shell.SetFocus() + # Add the application object to the sys module's namespace. + # This allows a shell user to do: + # >>> import sys + # >>> sys.app.whatever + import sys + sys.app = self + return 1 + +''' +The main() function needs to handle being imported, such as with the +pycrust script that wxPython installs: + + #!/usr/bin/env python + + from wxPython.lib.PyCrust.PyCrustApp import main + main() +''' + +def main(): + import __main__ + md = __main__.__dict__ + keepers = original + keepers.append('App') + for key in md.keys(): + if key not in keepers: + del md[key] + app = App(0) + if md.has_key('App') and md['App'] is App: + del md['App'] + if md.has_key('__main__') and md['__main__'] is __main__: + del md['__main__'] + app.MainLoop() + +if __name__ == '__main__': + main() diff --git a/wxPython/wxPython/py/PyWrap.py b/wxPython/wxPython/py/PyWrap.py new file mode 100755 index 0000000000..58813831aa --- /dev/null +++ b/wxPython/wxPython/py/PyWrap.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python + +"""PyWrap is a command line utility that runs a wxPython program with +additional runtime-tools, such as PyCrust.""" + +__author__ = "Patrick K. O'Brien " +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import os +import sys +from wxPython import wx +from crust import CrustFrame as Frame + +try: + True +except NameError: + True = 1==1 + False = 1==0 + + +def wrap(app): + wx.wxInitAllImageHandlers() + frame = Frame() + frame.SetSize((750, 525)) + frame.Show(True) + frame.shell.interp.locals['app'] = app + app.MainLoop() + + +def main(argv): + if len(argv) < 2: + print "Please specify a module name." + raise SystemExit + name = argv[1] + if name[-3:] == '.py': + name = name[:-3] + module = __import__(name) + # Find the App class. + App = None + d = module.__dict__ + for item in d.keys(): + try: + if issubclass(d[item], wx.wxApp): + App = d[item] + except (NameError, TypeError): + pass + if App is None: + print "No App class found." + raise SystemExit + app = App() + wrap(app) + + +if __name__ == '__main__': + sys.path.insert(0, os.curdir) + main(sys.argv) diff --git a/wxPython/wxPython/lib/PyCrust/README.txt b/wxPython/wxPython/py/README.txt similarity index 100% rename from wxPython/wxPython/lib/PyCrust/README.txt rename to wxPython/wxPython/py/README.txt diff --git a/wxPython/wxPython/py/__init__.py b/wxPython/wxPython/py/__init__.py new file mode 100644 index 0000000000..0b331a6f53 --- /dev/null +++ b/wxPython/wxPython/py/__init__.py @@ -0,0 +1,20 @@ +"""Python package.""" + +__author__ = "Patrick K. O'Brien " +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import base +import buffer +import crust +import dispatcher +import document +import editor +import filling +import frame +import images +import interpreter +import introspect +import pseudo +import shell +import version diff --git a/wxPython/wxPython/py/base.py b/wxPython/wxPython/py/base.py new file mode 100644 index 0000000000..43a0a75e7b --- /dev/null +++ b/wxPython/wxPython/py/base.py @@ -0,0 +1,195 @@ +"""Base editor.""" + +__author__ = "Patrick K. O'Brien " +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +from wxPython import wx +from wxPython import stc + +import keyword +import os +import sys +import time + +import dispatcher +from version import VERSION + +try: + True +except NameError: + True = 1==1 + False = 1==0 + +if wx.wxPlatform == '__WXMSW__': + FACES = { 'times' : 'Times New Roman', + 'mono' : 'Courier New', + 'helv' : 'Lucida Console', + 'lucida' : 'Lucida Console', + 'other' : 'Comic Sans MS', + 'size' : 10, + 'lnsize' : 9, + 'backcol': '#FFFFFF', + } +else: # GTK + FACES = { 'times' : 'Times', + 'mono' : 'Courier', + 'helv' : 'Helvetica', + 'other' : 'new century schoolbook', + 'size' : 12, + 'lnsize' : 10, + 'backcol': '#FFFFFF', + } + + +class Editor(stc.wxStyledTextCtrl): + """Editor based on StyledTextCtrl.""" + + revision = __revision__ + + def __init__(self, parent, id=-1, pos=wx.wxDefaultPosition, + size=wx.wxDefaultSize, style=wx.wxCLIP_CHILDREN | wx.wxSUNKEN_BORDER): + """Create an Editor instance.""" + stc.wxStyledTextCtrl.__init__(self, parent, id, pos, size, style) + self.__config() + stc.EVT_STC_UPDATEUI(self, id, self.OnUpdateUI) + dispatcher.connect(receiver=self._fontsizer, signal='FontIncrease') + dispatcher.connect(receiver=self._fontsizer, signal='FontDecrease') + dispatcher.connect(receiver=self._fontsizer, signal='FontDefault') + + def _fontsizer(self, signal): + """Receiver for Font* signals.""" + size = self.GetZoom() + if signal == 'FontIncrease': + size += 1 + elif signal == 'FontDecrease': + size -= 1 + elif signal == 'FontDefault': + size = 0 + self.SetZoom(size) + + def __config(self): + """Configure shell based on user preferences.""" + self.SetMarginType(1, stc.wxSTC_MARGIN_NUMBER) + self.SetMarginWidth(1, 40) + + self.SetLexer(stc.wxSTC_LEX_PYTHON) + self.SetKeyWords(0, ' '.join(keyword.kwlist)) + + self.setStyles(FACES) + self.SetViewWhiteSpace(False) + self.SetTabWidth(4) + self.SetUseTabs(False) + # Do we want to automatically pop up command completion options? + self.autoComplete = True + self.autoCompleteIncludeMagic = True + self.autoCompleteIncludeSingle = True + self.autoCompleteIncludeDouble = True + self.autoCompleteCaseInsensitive = True + self.AutoCompSetIgnoreCase(self.autoCompleteCaseInsensitive) + self.AutoCompSetAutoHide(False) + self.AutoCompStops(' .,;:([)]}\'"\\<>%^&+-=*/|`') + # Do we want to automatically pop up command argument help? + self.autoCallTip = True + self.CallTipSetBackground(wx.wxColour(255, 255, 232)) + self.SetWrapMode(False) + try: + self.SetEndAtLastLine(False) + except AttributeError: + pass + + def setStyles(self, faces): + """Configure font size, typeface and color for lexer.""" + + # Default style + self.StyleSetSpec(stc.wxSTC_STYLE_DEFAULT, + "face:%(mono)s,size:%(size)d,back:%(backcol)s" % \ + faces) + + self.StyleClearAll() + + # Built in styles + self.StyleSetSpec(stc.wxSTC_STYLE_LINENUMBER, + "back:#C0C0C0,face:%(mono)s,size:%(lnsize)d" % faces) + self.StyleSetSpec(stc.wxSTC_STYLE_CONTROLCHAR, + "face:%(mono)s" % faces) + self.StyleSetSpec(stc.wxSTC_STYLE_BRACELIGHT, + "fore:#0000FF,back:#FFFF88") + self.StyleSetSpec(stc.wxSTC_STYLE_BRACEBAD, + "fore:#FF0000,back:#FFFF88") + + # Python styles + self.StyleSetSpec(stc.wxSTC_P_DEFAULT, + "face:%(mono)s" % faces) + self.StyleSetSpec(stc.wxSTC_P_COMMENTLINE, + "fore:#007F00,face:%(mono)s" % faces) + self.StyleSetSpec(stc.wxSTC_P_NUMBER, + "") + self.StyleSetSpec(stc.wxSTC_P_STRING, + "fore:#7F007F,face:%(mono)s" % faces) + self.StyleSetSpec(stc.wxSTC_P_CHARACTER, + "fore:#7F007F,face:%(mono)s" % faces) + self.StyleSetSpec(stc.wxSTC_P_WORD, + "fore:#00007F,bold") + self.StyleSetSpec(stc.wxSTC_P_TRIPLE, + "fore:#7F0000") + self.StyleSetSpec(stc.wxSTC_P_TRIPLEDOUBLE, + "fore:#000033,back:#FFFFE8") + self.StyleSetSpec(stc.wxSTC_P_CLASSNAME, + "fore:#0000FF,bold") + self.StyleSetSpec(stc.wxSTC_P_DEFNAME, + "fore:#007F7F,bold") + self.StyleSetSpec(stc.wxSTC_P_OPERATOR, + "") + self.StyleSetSpec(stc.wxSTC_P_IDENTIFIER, + "") + self.StyleSetSpec(stc.wxSTC_P_COMMENTBLOCK, + "fore:#7F7F7F") + self.StyleSetSpec(stc.wxSTC_P_STRINGEOL, + "fore:#000000,face:%(mono)s,back:#E0C0E0,eolfilled" % faces) + + def OnUpdateUI(self, event): + """Check for matching braces.""" + # If the auto-complete window is up let it do its thing. + if self.AutoCompActive() or self.CallTipActive(): + return + braceAtCaret = -1 + braceOpposite = -1 + charBefore = None + caretPos = self.GetCurrentPos() + if caretPos > 0: + charBefore = self.GetCharAt(caretPos - 1) + styleBefore = self.GetStyleAt(caretPos - 1) + + # Check before. + if charBefore and chr(charBefore) in '[]{}()' \ + and styleBefore == stc.wxSTC_P_OPERATOR: + braceAtCaret = caretPos - 1 + + # Check after. + if braceAtCaret < 0: + charAfter = self.GetCharAt(caretPos) + styleAfter = self.GetStyleAt(caretPos) + if charAfter and chr(charAfter) in '[]{}()' \ + and styleAfter == stc.wxSTC_P_OPERATOR: + braceAtCaret = caretPos + + if braceAtCaret >= 0: + braceOpposite = self.BraceMatch(braceAtCaret) + + if braceAtCaret != -1 and braceOpposite == -1: + self.BraceBadLight(braceAtCaret) + else: + self.BraceHighlight(braceAtCaret, braceOpposite) + + def CanCut(self): + """Return true if text is selected and can be cut.""" + return self.CanCopy() + + def CanCopy(self): + """Return true if text is selected and can be copied.""" + return self.GetSelectionStart() != self.GetSelectionEnd() + + def CanEdit(self): + """Return true if editing should succeed.""" + return True diff --git a/wxPython/wxPython/py/buffer.py b/wxPython/wxPython/py/buffer.py new file mode 100644 index 0000000000..e0c520cca3 --- /dev/null +++ b/wxPython/wxPython/py/buffer.py @@ -0,0 +1,140 @@ +"""Buffer class.""" + +__author__ = "Patrick K. O'Brien " +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +from wxPython import wx + +import imp +import os +import sys + +import document + +try: + True +except NameError: + True = 1==1 + False = 1==0 + + +class Buffer: + """Buffer class.""" + + id = 0 + + def __init__(self, editor, interp, filename=None): + """Create a Buffer instance.""" + Buffer.id += 1 + self.id = Buffer.id + self.name = '' + self.editor = editor + self.interp = interp + self.modules = sys.modules.keys() + self.syspath = sys.path[:] + while True: + try: + self.syspath.remove('') + except ValueError: + break + while True: + try: + self.syspath.remove('.') + except ValueError: + break + self.open(filename) + + def getStatus(self): + """Return (filepath, line, column) status tuple.""" + editor = self.editor + pos = editor.GetCurrentPos() + line = editor.LineFromPosition(pos) + 1 + col = editor.GetColumn(pos) + 1 + status = (self.doc.filepath or self.name, line, col) + return status + + def hasChanged(self): + """Return True if text in editor has changed since last save.""" + return self.editor.GetModify() + + def new(self, filepath): + """New empty buffer.""" + if not filepath: + return + if os.path.exists(filepath): + self.confirmed = self.overwriteConfirm(filepath) + else: + self.confirmed = True + + def open(self, filename): + """Open file into buffer.""" + self.doc = document.Document(filename) + self.name = self.doc.filename or ('Untitled:' + str(self.id)) + self.modulename = self.doc.filebase + if self.doc.filepath and os.path.exists(self.doc.filepath): + self.editor.ClearAll() + self.editor.SetText(self.doc.read()) + self.editor.EmptyUndoBuffer() + self.editor.SetSavePoint() + self.confirmed = True + if self.doc.filedir and self.doc.filedir not in self.syspath: + self.syspath.insert(0, self.doc.filedir) + + def overwriteConfirm(filepath): + """Confirm overwriting an existing file.""" + return False + + def save(self): + """Save buffer.""" + filepath = self.doc.filepath + if not filepath: + return # XXX Get filename + if not os.path.exists(filepath): + self.confirmed = True + if not self.confirmed: + self.confirmed = self.overwriteConfirm(filepath) + if self.confirmed: + self.doc.write(self.editor.GetText()) + self.editor.SetSavePoint() + + def saveAs(self, filename): + """Save buffer.""" + self.doc = document.Document(filename) + self.name = self.doc.filename + self.modulename = self.doc.filebase + filepath = self.doc.filepath + if not filepath: + return # XXX Get filename +## if not os.path.exists(filepath): + self.confirmed = True + if not self.confirmed: + self.confirmed = self.overwriteConfirm(filepath) + if self.confirmed: + self.doc.write(self.editor.GetText()) + self.editor.SetSavePoint() + + def updateNamespace(self): + """Update the namespace for autocompletion and calltips. + + Return True if updated, False if there was an error.""" + backup = self.interp.locals + syspath = sys.path + sys.path = self.syspath + code = self.editor.GetText() + module = imp.new_module(str(self.modulename)) + namespace = module.__dict__.copy() + try: + try: + exec code in namespace + except: + self.interp.locals = backup + return False + else: + self.interp.locals = namespace + return True + finally: + sys.path = syspath + for m in sys.modules.keys(): + if m not in self.modules: + del sys.modules[m] diff --git a/wxPython/wxPython/py/crust.py b/wxPython/wxPython/py/crust.py new file mode 100644 index 0000000000..b5b7894886 --- /dev/null +++ b/wxPython/wxPython/py/crust.py @@ -0,0 +1,182 @@ +"""PyCrust Crust combines the shell and filling into one control.""" + +__author__ = "Patrick K. O'Brien " +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +from wxPython import wx + +import os +import sys + +import dispatcher +from filling import Filling +import frame +from shell import Shell +from version import VERSION + +try: + True +except NameError: + True = 1==1 + False = 1==0 + + +class Crust(wx.wxSplitterWindow): + """PyCrust Crust based on wxSplitterWindow.""" + + name = 'PyCrust Crust' + revision = __revision__ + + def __init__(self, parent, id=-1, pos=wx.wxDefaultPosition, + size=wx.wxDefaultSize, style=wx.wxSP_3D, + name='Crust Window', rootObject=None, rootLabel=None, + rootIsNamespace=True, intro='', locals=None, + InterpClass=None, *args, **kwds): + """Create a PyCrust Crust instance.""" + wx.wxSplitterWindow.__init__(self, parent, id, pos, size, style, name) + self.shell = Shell(parent=self, introText=intro, + locals=locals, InterpClass=InterpClass, + *args, **kwds) + self.buffer = self.shell.buffer + if rootObject is None: + rootObject = self.shell.interp.locals + self.notebook = wx.wxNotebook(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.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='Session') + self.dispatcherlisting = DispatcherListing(parent=self.notebook) + self.notebook.AddPage(page=self.dispatcherlisting, text='Dispatcher') + from wxd import wx_ + self.wxdocs = Filling(parent=self.notebook, + rootObject=wx_, + rootLabel='wx', + rootIsNamespace=False, + static=True) + self.notebook.AddPage(page=self.wxdocs, text='wxPython Docs') + from wxd import stc_ + self.stcdocs = Filling(parent=self.notebook, + rootObject=stc_.StyledTextCtrl, + rootLabel='StyledTextCtrl', + rootIsNamespace=False, + static=True) + self.notebook.AddPage(page=self.stcdocs, text='StyledTextCtrl Docs') + self.SplitHorizontally(self.shell, self.notebook, 300) + self.SetMinimumPaneSize(1) + + +class Calltip(wx.wxTextCtrl): + """Text control containing the most recent shell calltip.""" + + def __init__(self, parent=None, id=-1): + style = wx.wxTE_MULTILINE | wx.wxTE_READONLY | wx.wxTE_RICH2 + wx.wxTextCtrl.__init__(self, parent=parent, id=id, style=style) + self.SetBackgroundColour(wx.wxColour(255, 255, 232)) + dispatcher.connect(receiver=self.display, signal='Shell.calltip') + + def display(self, calltip): + """Receiver for Shell.calltip signal.""" + self.SetValue(calltip) + + +class SessionListing(wx.wxTextCtrl): + """Text control containing all commands for session.""" + + def __init__(self, parent=None, id=-1): + style = wx.wxTE_MULTILINE | wx.wxTE_READONLY | \ + wx.wxTE_RICH2 | wx.wxTE_DONTWRAP + wx.wxTextCtrl.__init__(self, parent=parent, id=id, style=style) + dispatcher.connect(receiver=self.push, signal='Interpreter.push') + + def push(self, command, more): + """Receiver for Interpreter.push signal.""" + if command and not more: + self.SetInsertionPointEnd() + start, end = self.GetSelection() + if start != end: + self.SetSelection(0, 0) + self.AppendText(command + '\n') + + +class DispatcherListing(wx.wxTextCtrl): + """Text control containing all dispatches for session.""" + + def __init__(self, parent=None, id=-1): + style = wx.wxTE_MULTILINE | wx.wxTE_READONLY | \ + wx.wxTE_RICH2 | wx.wxTE_DONTWRAP + wx.wxTextCtrl.__init__(self, parent=parent, id=id, style=style) + dispatcher.connect(receiver=self.spy) + + 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 containing all the PyCrust components.""" + + name = 'PyCrust Frame' + revision = __revision__ + + def __init__(self, parent=None, id=-1, title='PyCrust', + pos=wx.wxDefaultPosition, size=wx.wxDefaultSize, + style=wx.wxDEFAULT_FRAME_STYLE, + rootObject=None, rootLabel=None, rootIsNamespace=True, + locals=None, InterpClass=None, *args, **kwds): + """Create a PyCrust CrustFrame instance.""" + frame.Frame.__init__(self, parent, id, title, pos, size, style) + intro = 'PyCrust %s - The Flakiest Python Shell' % VERSION + intro += '\nSponsored by Orbtech - ' + intro += 'Your source for Python programming expertise.' + self.SetStatusText(intro.replace('\n', ', ')) + self.crust = Crust(parent=self, intro=intro, + rootObject=rootObject, + rootLabel=rootLabel, + rootIsNamespace=rootIsNamespace, + locals=locals, + InterpClass=InterpClass, *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 + # Fix a problem with the sash shrinking to nothing. + self.crust.filling.SetSashPosition(200) + # Set focus to the shell editor. + self.shell.SetFocus() + + def OnClose(self, event): + """Event handler for closing.""" + 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 + \ + 'Python Version: %s\n' % sys.version.split()[0] + \ + 'wxPython Version: %s\n' % wx.__version__ + \ + 'Platform: %s\n' % sys.platform + dialog = wx.wxMessageDialog(self, text, title, + wx.wxOK | wx.wxICON_INFORMATION) + dialog.ShowModal() + dialog.Destroy() diff --git a/wxPython/wxPython/py/default.css b/wxPython/wxPython/py/default.css new file mode 100644 index 0000000000..7343b12bc9 --- /dev/null +++ b/wxPython/wxPython/py/default.css @@ -0,0 +1,208 @@ +/* +:Author: David Goodger +:Contact: goodger@users.sourceforge.net +:date: $Date$ +:version: $Revision$ +:copyright: This stylesheet has been placed in the public domain. + +Default cascading style sheet for the HTML output of Docutils. +*/ + +.first { + margin-top: 0 } + +.last { + margin-bottom: 0 } + +a.toc-backref { + text-decoration: none ; + color: black } + +dd { + margin-bottom: 0.5em } + +div.abstract { + margin: 2em 5em } + +div.abstract p.topic-title { + font-weight: bold ; + text-align: center } + +div.attention, div.caution, div.danger, div.error, div.hint, +div.important, div.note, div.tip, div.warning { + margin: 2em ; + border: medium outset ; + padding: 1em } + +div.attention p.admonition-title, div.caution p.admonition-title, +div.danger p.admonition-title, div.error p.admonition-title, +div.warning p.admonition-title { + color: red ; + font-weight: bold ; + font-family: sans-serif } + +div.hint p.admonition-title, div.important p.admonition-title, +div.note p.admonition-title, div.tip p.admonition-title { + font-weight: bold ; + font-family: sans-serif } + +div.dedication { + margin: 2em 5em ; + text-align: center ; + font-style: italic } + +div.dedication p.topic-title { + font-weight: bold ; + font-style: normal } + +div.figure { + margin-left: 2em } + +div.footer, div.header { + font-size: smaller } + +div.sidebar { + margin-left: 1em ; + border: medium outset ; + padding: 0em 1em ; + background-color: #ffffee ; + width: 40% ; + float: right } + +div.system-messages { + margin: 5em } + +div.system-messages h1 { + color: red } + +div.system-message { + border: medium outset ; + padding: 1em } + +div.system-message p.system-message-title { + color: red ; + font-weight: bold } + +div.topic { + margin: 2em } + +h1.title { + text-align: center } + +h2.subtitle { + text-align: center } + +hr { + width: 75% } + +ol.simple, ul.simple { + margin-bottom: 1em } + +ol.arabic { + list-style: decimal } + +ol.loweralpha { + list-style: lower-alpha } + +ol.upperalpha { + list-style: upper-alpha } + +ol.lowerroman { + list-style: lower-roman } + +ol.upperroman { + list-style: upper-roman } + +p.caption { + font-style: italic } + +p.credits { + font-style: italic ; + font-size: smaller } + +p.label { + white-space: nowrap } + +p.sidebar-title { + font-family: sans-serif ; + font-weight: bold ; + font-size: larger } + +p.sidebar-subtitle { + font-family: sans-serif ; + font-weight: bold } + +p.topic-title { + font-weight: bold } + +pre.address { + margin-bottom: 0 ; + margin-top: 0 ; + font-family: serif ; + font-size: 100% } + +pre.line-block { + font-family: serif ; + font-size: 100% } + +pre.literal-block, pre.doctest-block { + margin-left: 2em ; + margin-right: 2em ; + background-color: #eeeeee } + +span.classifier { + font-family: sans-serif ; + font-style: oblique } + +span.classifier-delimiter { + font-family: sans-serif ; + font-weight: bold } + +span.interpreted { + font-family: sans-serif } + +span.option { + white-space: nowrap } + +span.option-argument { + font-style: italic } + +span.pre { + white-space: pre } + +span.problematic { + color: red } + +table { + margin-top: 0.5em ; + margin-bottom: 0.5em } + +table.citation { + border-left: solid thin gray ; + padding-left: 0.5ex } + +table.docinfo { + margin: 2em 4em } + +table.footnote { + border-left: solid thin black ; + padding-left: 0.5ex } + +td, th { + padding-left: 0.5em ; + padding-right: 0.5em ; + vertical-align: top } + +th.docinfo-name, th.field-name { + font-weight: bold ; + text-align: left ; + white-space: nowrap } + +h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { + font-size: 100% } + +tt { + background-color: #eeeeee } + +ul.auto-toc { + list-style-type: none } diff --git a/wxPython/wxPython/lib/PyCrust/dispatcher.py b/wxPython/wxPython/py/dispatcher.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/dispatcher.py rename to wxPython/wxPython/py/dispatcher.py diff --git a/wxPython/wxPython/py/document.py b/wxPython/wxPython/py/document.py new file mode 100644 index 0000000000..6e7b5001a0 --- /dev/null +++ b/wxPython/wxPython/py/document.py @@ -0,0 +1,46 @@ +"""Document class.""" + +__author__ = "Patrick K. O'Brien " +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import os + +try: + True +except NameError: + True = 1==1 + False = 1==0 + + +class Document: + """Document class.""" + + def __init__(self, filename=None): + """Create a Document instance.""" + self.filename = filename + self.filepath = None + self.filedir = None + self.filebase = None + self.fileext = None + if self.filename: + self.filepath = os.path.abspath(self.filename) + self.filedir, self.filename = os.path.split(self.filepath) + self.filebase, self.fileext = os.path.splitext(self.filename) + + def read(self): + """Return contents of file.""" + f = file(self.filepath, 'rb') + try: + return f.read() + finally: + f.close() + + def write(self, text): + """Write text to file.""" + try: + f = file(self.filepath, 'wb') + f.write(text) + finally: + if f: + f.close() diff --git a/wxPython/wxPython/py/editor.py b/wxPython/wxPython/py/editor.py new file mode 100644 index 0000000000..4b2ad1be00 --- /dev/null +++ b/wxPython/wxPython/py/editor.py @@ -0,0 +1,733 @@ +"""PyAlaCarte and PyAlaMode editors.""" + +__author__ = "Patrick K. O'Brien " +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +from wxPython import wx + +import base +import buffer +import crust +import dispatcher +import frame +import interpreter +import shell +import version + +try: + True +except NameError: + True = 1==1 + False = 1==0 + + +class EditorFrame(frame.Frame): + """Frame containing one editor.""" + + def __init__(self, parent=None, id=-1, title='PyAlaCarte', + pos=wx.wxDefaultPosition, size=(800, 600), + style=wx.wxDEFAULT_FRAME_STYLE, filename=None): + """Create an EditorFrame instance.""" + frame.Frame.__init__(self, parent, id, title, pos, size, style) + self._buffers = {} + self._buffer = None # Current buffer. + self.editor = None + self._statusText = title + ' - the tastiest Python editor.' + self.SetStatusText(self._statusText) + wx.EVT_IDLE(self, self.OnIdle) + self._setup() + if filename: + self.bufferCreate(filename) + + def _setup(self): + """Setup prior to first buffer creation. + + Useful for subclasses.""" + pass + + def OnAbout(self, event): + """Display an About window.""" + title = 'About PyAlaCarte' + text = 'Another fine, flaky program.' + dialog = wx.wxMessageDialog(self, text, title, + wx.wxOK | wx.wxICON_INFORMATION) + dialog.ShowModal() + dialog.Destroy() + + def OnClose(self, event): + """Event handler for closing.""" + for buffer in self._buffers.values(): + self._buffer = buffer + if buffer.hasChanged(): + cancel = self.bufferSuggestSave() + if cancel and event.CanVeto(): + event.Veto() + return + self.Destroy() + + def OnIdle(self, event): + """Event handler for idle time.""" + self._updateStatus() + self._updateTitle() + event.Skip() + + def _updateStatus(self): + """Show current status information.""" + if self._buffer: + status = self._buffer.getStatus() + text = 'File: %s | Line: %d | Column: %d' % status + if text != self._statusText: + self.SetStatusText(text) + self._statusText = text + + def _updateTitle(self): + """Show current title information.""" + title = self.GetTitle() + if self.bufferHasChanged(): + if title.startswith('* '): + pass + else: + self.SetTitle('* ' + title) + else: + if title.startswith('* '): + self.SetTitle(title[2:]) + + def hasBuffer(self): + """Return True if there is a current buffer.""" + if self._buffer: + return True + else: + return False + + def bufferClose(self): + """Close buffer.""" + if self.bufferHasChanged(): + cancel = self.bufferSuggestSave() + if cancel: + return cancel + self.bufferDestroy() + cancel = False + return cancel + + def bufferCreate(self, filename=None): + """Create new buffer.""" + self.bufferDestroy() + interp = interpreter.Interpreter(locals={}) + self.editor = Editor(interp=interp, parent=self, filename=filename) + self._buffer = self.editor.buffer + self._buffers[self._buffer.id] = self._buffer + self._buffer.editor.SetFocus() + + def bufferDestroy(self): + """Destroy the current buffer.""" + if self._buffer: + del self._buffers[self._buffer.id] + self._buffer = None + if self.editor: + self.editor.Destroy() + self.editor = None + + def bufferHasChanged(self): + """Return True if buffer has changed since last save.""" + if self._buffer: + return self._buffer.hasChanged() + else: + return False + + def bufferNew(self): + """Create new buffer.""" + if self.bufferHasChanged(): + cancel = self.bufferSuggestSave() + if cancel: + return cancel + self.bufferCreate() + cancel = False + return cancel + + def bufferOpen(self): + """Open file in buffer.""" + if self.bufferHasChanged(): + cancel = self.bufferSuggestSave() + if cancel: + return cancel + filedir = '' + if self._buffer and self._buffer.doc.filedir: + filedir = self._buffer.doc.filedir + result = openSingle(directory=filedir) + if result.path: + self.bufferCreate(result.path) + cancel = False + return cancel + +## def bufferPrint(self): +## """Print buffer.""" +## pass + +## def bufferRevert(self): +## """Revert buffer to version of file on disk.""" +## pass + + def bufferSave(self): + """Save buffer to its file.""" + if self._buffer.doc.filepath: + self._buffer.save() + cancel = False + else: + cancel = self.bufferSaveAs() + return cancel + + def bufferSaveAs(self): + """Save buffer to a new filename.""" + if self.bufferHasChanged() and self._buffer.doc.filepath: + cancel = self.bufferSuggestSave() + if cancel: + return cancel + filedir = '' + if self._buffer and self._buffer.doc.filedir: + filedir = self._buffer.doc.filedir + result = saveSingle(directory=filedir) + if result.path: + self._buffer.saveAs(result.path) + cancel = False + else: + cancel = True + return cancel + + def bufferSuggestSave(self): + """Suggest saving changes. Return True if user selected Cancel.""" + result = messageDialog(parent=None, + message='%s has changed.\n' + 'Would you like to save it first' + '?' % self._buffer.name, + title='Save current file?') + if result.positive: + cancel = self.bufferSave() + else: + cancel = result.text == 'Cancel' + return cancel + + def updateNamespace(self): + """Update the buffer namespace for autocompletion and calltips.""" + if self._buffer.updateNamespace(): + self.SetStatusText('Namespace updated') + else: + self.SetStatusText('Error executing, unable to update namespace') + + +class EditorNotebookFrame(EditorFrame): + """Frame containing one or more editors in a notebook.""" + + def __init__(self, parent=None, id=-1, title='PyAlaMode', + pos=wx.wxDefaultPosition, size=(800, 600), + style=wx.wxDEFAULT_FRAME_STYLE, filename=None): + """Create an EditorNotebookFrame instance.""" + EditorFrame.__init__(self, parent, id, title, pos, + size, style, filename) + + def _setup(self): + """Setup prior to first buffer creation. + + Useful for subclasses.""" + self._notebook = BufferNotebook(parent=self) + dispatcher.connect(receiver=self._bufferChange, + signal='BufferChange', sender=self._notebook) + intro = 'PyCrust %s' % version.VERSION + import imp + module = imp.new_module('__main__') + import __builtin__ + module.__dict__['__builtins__'] = __builtin__ + namespace = module.__dict__.copy() + self.crust = crust.Crust(parent=self._notebook, intro=intro, locals=namespace) + 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 + # Fix a problem with the sash shrinking to nothing. + self.crust.filling.SetSashPosition(200) + self._notebook.AddPage(page=self.crust, text='PyCrust', select=True) + self._buffer = self.crust.buffer + self._buffers[self._buffer.id] = self._buffer + self._buffer.editor.SetFocus() + + def _bufferChange(self, buffer): + """Buffer change signal receiver.""" + self._buffer = buffer + + def OnAbout(self, event): + """Display an About window.""" + title = 'About PyAlaMode' + text = 'Another fine, flaky program.' + dialog = wx.wxMessageDialog(self, text, title, + wx.wxOK | wx.wxICON_INFORMATION) + dialog.ShowModal() + dialog.Destroy() + + def _updateTitle(self): + """Show current title information.""" + title = self.GetTitle() + if self.bufferHasChanged(): + if title.startswith('* '): + pass + else: + self.SetTitle('* ' + title) + else: + if title.startswith('* '): + self.SetTitle(title[2:]) + + def bufferCreate(self, filename=None): + """Create new buffer.""" + interp = interpreter.Interpreter(locals={}) + editor = Editor(interp=interp, parent=self._notebook, + filename=filename) + self._buffer = editor.buffer + self._buffers[self._buffer.id] = self._buffer + self._notebook.AddPage(page=editor, text=self._buffer.name, + select=True) + self._buffer.editor.SetFocus() + + def bufferDestroy(self): + """Destroy the current buffer.""" + selection = self._notebook.GetSelection() +## print "Destroy Selection:", selection + if selection > 0: # Don't destroy the PyCrust tab. + if self._buffer: + del self._buffers[self._buffer.id] + self._buffer = None # Do this before DeletePage(). + self._notebook.DeletePage(selection) + + def bufferNew(self): + """Create new buffer.""" + self.bufferCreate() + cancel = False + return cancel + + def bufferOpen(self): + """Open file in buffer.""" + filedir = '' + if self._buffer and self._buffer.doc.filedir: + filedir = self._buffer.doc.filedir + result = openMultiple(directory=filedir) + for path in result.paths: + self.bufferCreate(path) + cancel = False + return cancel + + +class BufferNotebook(wx.wxNotebook): + """A notebook containing a page for each buffer.""" + + def __init__(self, parent): + """Create a BufferNotebook instance.""" + wx.wxNotebook.__init__(self, parent, id=-1) + wx.EVT_NOTEBOOK_PAGE_CHANGING(self, self.GetId(), self.OnPageChanging) + wx.EVT_NOTEBOOK_PAGE_CHANGED(self, self.GetId(), self.OnPageChanged) + + def OnPageChanging(self, event): + """Page changing event handler.""" +## old = event.GetOldSelection() +## print "Changing from old:", old +## new = event.GetOldSelection() +## print "Changing to new:", new + event.Skip() + + def OnPageChanged(self, event): + """Page changed event handler.""" +## old = event.GetOldSelection() +## print "Changed from:", old + new = event.GetSelection() +## print "Changed to new:", new + page = self.GetPage(new) + buffer = page.buffer + buffer.editor.SetFocus() + dispatcher.send(signal='BufferChange', sender=self, buffer=buffer) + event.Skip() + + +class BufferEditorShellNotebookFrame(EditorFrame): + """Frame containing one or more editor notebooks.""" + + def __init__(self, parent=None, id=-1, title='PyAlaMode', + pos=wx.wxDefaultPosition, size=(600, 400), + style=wx.wxDEFAULT_FRAME_STYLE, + filename=None, singlefile=False): + """Create a BufferEditorShellNotebookFrame instance.""" + self._singlefile = singlefile + EditorFrame.__init__(self, parent, id, title, pos, + size, style, filename) + + def _setup(self): + """Setup prior to first buffer creation. + + Useful for subclasses.""" + if not self._singlefile: + self._notebook = BufferNotebook(parent=self) + dispatcher.connect(receiver=self._bufferChange, + signal='BufferChange', sender=self._notebook) + + def _bufferChange(self, buffer): + """Buffer change signal receiver.""" + self._buffer = buffer + + def OnAbout(self, event): + """Display an About window.""" + title = 'About PyAlaMode' + text = 'Another fine, flaky program.' + dialog = wx.wxMessageDialog(self, text, title, + wx.wxOK | wx.wxICON_INFORMATION) + dialog.ShowModal() + dialog.Destroy() + + def _updateTitle(self): + """Show current title information.""" + title = self.GetTitle() + if self.bufferHasChanged(): + if title.startswith('* '): + pass + else: + self.SetTitle('* ' + title) + else: + if title.startswith('* '): + self.SetTitle(title[2:]) + + def bufferCreate(self, filename=None): + """Create new buffer.""" + if self._singlefile: + self.bufferDestroy() + notebook = self._notebook = EditorShellNotebook(parent=self, + filename=filename) + else: + notebook = EditorShellNotebook(parent=self._notebook, + filename=filename) + self._buffer = notebook.buffer + if not self._singlefile: + self._notebook.AddPage(page=notebook, text=self._buffer.name, + select=True) + self._buffers[self._buffer.id] = self._buffer + self._buffer.editor.SetFocus() + + def bufferDestroy(self): + """Destroy the current buffer.""" + if self._buffer: + del self._buffers[self._buffer.id] + self._buffer = None # Do this before DeletePage(). + if self._singlefile: + self._notebook.Destroy() + self._notebook = None + else: + selection = self._notebook.GetSelection() + print "Destroy Selection:", selection + self._notebook.DeletePage(selection) + + def bufferNew(self): + """Create new buffer.""" + if self._singlefile and self.bufferHasChanged(): + cancel = self.bufferSuggestSave() + if cancel: + return cancel + self.bufferCreate() + cancel = False + return cancel + + def bufferOpen(self): + """Open file in buffer.""" + if self._singlefile and self.bufferHasChanged(): + cancel = self.bufferSuggestSave() + if cancel: + return cancel + filedir = '' + if self._buffer and self._buffer.doc.filedir: + filedir = self._buffer.doc.filedir + if self._singlefile: + result = openSingle(directory=filedir) + if result.path: + self.bufferCreate(result.path) + else: + result = openMultiple(directory=filedir) + for path in result.paths: + self.bufferCreate(path) + cancel = False + return cancel + + +class BufferEditorShellNotebook(wx.wxNotebook): + """A notebook containing a page for each buffer.""" + + def __init__(self, parent): + """Create a BufferEditorShellNotebook instance.""" + wx.wxNotebook.__init__(self, parent, id=-1) + wx.EVT_NOTEBOOK_PAGE_CHANGED(self, self.GetId(), self.OnPageChanged) + + def OnPageChanged(self, event): + """Page changed event handler.""" +## old = event.GetOldSelection() +## print "Changed from old:", old + new = event.GetSelection() +## print "Changed to new:", new + page = self.GetPage(new) + buffer = page.buffer + subselection = page.GetSelection() + page.focus(subselection) + dispatcher.send(signal='BufferChange', sender=self, buffer=buffer) + event.Skip() + + +class EditorShellNotebook(wx.wxNotebook): + """A notebook containing an editor page and a shell page.""" + + def __init__(self, parent, filename=None): + """Create an EditorShellNotebook instance.""" + wx.wxNotebook.__init__(self, parent, id=-1) + usePanels = True + if usePanels: + shellparent = shellpanel = wx.wxPanel(self, -1) + editorparent = editorpanel = wx.wxPanel(self, -1) + else: + shellparent = self + editorparent = self + self.shell = shell.Shell(parent=shellparent, + style=wx.wxCLIP_CHILDREN | wx.wxSUNKEN_BORDER) + self.editor = Editor(interp=self.shell.interp, parent=editorparent, + filename=filename) + if usePanels: + self.AddPage(page=editorpanel, text='File', select=True) + self.AddPage(page=shellpanel, text='Shell') + # Setup sizers + shellsizer = wx.wxBoxSizer(wx.wxVERTICAL) + shellsizer.Add(self.shell, 1, wx.wxEXPAND) + shellpanel.SetSizer(shellsizer) + shellpanel.SetAutoLayout(True) + editorsizer = wx.wxBoxSizer(wx.wxVERTICAL) + editorsizer.Add(self.editor, 1, wx.wxEXPAND) + editorpanel.SetSizer(editorsizer) + editorpanel.SetAutoLayout(True) + else: + self.AddPage(page=self.editor, text='File', select=True) + self.AddPage(page=self.shell, text='Shell') + self.buffer = self.editor.buffer + self.editor.SetFocus() + wx.EVT_NOTEBOOK_PAGE_CHANGED(self, self.GetId(), self.OnPageChanged) + + def OnPageChanged(self, event): + """Page changed event handler.""" + selection = event.GetSelection() + self.focus(selection) + event.Skip() + + def focus(self, selection): + if selection == 0: + self.editor.SetFocus() + else: + self.shell.SetFocus() + + +class Editor(base.Editor): + """Editor based on StyledTextCtrl.""" + + def __init__(self, interp, parent, id=-1, pos=wx.wxDefaultPosition, + size=wx.wxDefaultSize, + style=wx.wxCLIP_CHILDREN | wx.wxSUNKEN_BORDER, + filename=None): + """Create a Editor instance.""" + base.Editor.__init__(self, parent, id, pos, size, style) + self.interp = interp + # Find out for which keycodes the interpreter will autocomplete. + self.autoCompleteKeys = self.interp.getAutoCompleteKeys() + # Assign handlers for keyboard events. + wx.EVT_CHAR(self, self.OnChar) + wx.EVT_KEY_DOWN(self, self.OnKeyDown) + self.buffer = buffer.Buffer(editor=self, interp=self.interp, + filename=filename) + + def OnChar(self, event): + """Keypress event handler. + + Only receives an event if OnKeyDown calls event.Skip() for the + corresponding event.""" + + key = event.KeyCode() + if key in self.autoCompleteKeys: + # Usually the dot (period) key activates auto completion. + if self.AutoCompActive(): + self.AutoCompCancel() + self.ReplaceSelection('') + self.AddText(chr(key)) + text, pos = self.GetCurLine() + text = text[:pos] + if self.autoComplete: + self.autoCompleteShow(text) + elif key == ord('('): + # The left paren activates a call tip and cancels an + # active auto completion. + if self.AutoCompActive(): + self.AutoCompCancel() + self.ReplaceSelection('') + self.AddText('(') + text, pos = self.GetCurLine() + text = text[:pos] + self.autoCallTipShow(text) + else: + # Allow the normal event handling to take place. + event.Skip() + + def OnKeyDown(self, event): + """Key down event handler.""" + + key = event.KeyCode() + # If the auto-complete window is up let it do its thing. + if self.AutoCompActive(): + event.Skip() + return + controlDown = event.ControlDown() + altDown = event.AltDown() + shiftDown = event.ShiftDown() + # Let Ctrl-Alt-* get handled normally. + if controlDown and altDown: + event.Skip() + # Increase font size. + elif controlDown and key in (ord(']'),): + dispatcher.send(signal='FontIncrease') + # Decrease font size. + elif controlDown and key in (ord('['),): + dispatcher.send(signal='FontDecrease') + # Default font size. + elif controlDown and key in (ord('='),): + dispatcher.send(signal='FontDefault') + else: + event.Skip() + + def autoCompleteShow(self, command): + """Display auto-completion popup list.""" + list = self.interp.getAutoCompleteList(command, + includeMagic=self.autoCompleteIncludeMagic, + includeSingle=self.autoCompleteIncludeSingle, + includeDouble=self.autoCompleteIncludeDouble) + if list and len(list) < 2000: + options = ' '.join(list) + offset = 0 + self.AutoCompShow(offset, options) + + def autoCallTipShow(self, command): + """Display argument spec and docstring in a popup window.""" + if self.CallTipActive(): + self.CallTipCancel() + (name, argspec, tip) = self.interp.getCallTip(command) + if tip: + dispatcher.send(signal='Shell.calltip', sender=self, calltip=tip) + if not self.autoCallTip: + return + if argspec: + startpos = self.GetCurrentPos() + self.AddText(argspec + ')') + endpos = self.GetCurrentPos() + self.SetSelection(endpos, startpos) + if tip: + curpos = self.GetCurrentPos() + size = len(name) + tippos = curpos - (size + 1) + fallback = curpos - self.GetColumn(curpos) + # In case there isn't enough room, only go back to the + # fallback. + tippos = max(tippos, fallback) + self.CallTipShow(tippos, tip) + self.CallTipSetHighlight(0, size) + + +class DialogResults: + """DialogResults class.""" + + def __init__(self, returned): + """Create a wrapper for the results returned by a dialog.""" + self.returned = returned + self.positive = returned in (wx.wxID_OK, wx.wxID_YES) + self.text = self._asString() + + + def __repr__(self): + return str(self.__dict__) + + def _asString(self): + returned = self.returned + if returned == wx.wxID_OK: + return "Ok" + elif returned == wx.wxID_CANCEL: + return "Cancel" + elif returned == wx.wxID_YES: + return "Yes" + elif returned == wx.wxID_NO: + return "No" + + +def fileDialog(parent=None, title='Open', directory='', filename='', + wildcard='All Files (*.*)|*.*', + style=wx.wxOPEN | wx.wxMULTIPLE): + """File dialog wrapper function.""" + dialog = wx.wxFileDialog(parent, title, directory, filename, + wildcard, style) + result = DialogResults(dialog.ShowModal()) + if result.positive: + result.paths = dialog.GetPaths() + else: + result.paths = [] + dialog.Destroy() + return result + + +def openSingle(parent=None, title='Open', directory='', filename='', + wildcard='All Files (*.*)|*.*', style=wx.wxOPEN): + """File dialog wrapper function.""" + dialog = wx.wxFileDialog(parent, title, directory, filename, + wildcard, style) + result = DialogResults(dialog.ShowModal()) + if result.positive: + result.path = dialog.GetPath() + else: + result.path = None + dialog.Destroy() + return result + + +def openMultiple(parent=None, title='Open', directory='', filename='', + wildcard='All Files (*.*)|*.*', + style=wx.wxOPEN | wx.wxMULTIPLE): + """File dialog wrapper function.""" + return fileDialog(parent, title, directory, filename, wildcard, style) + + +def saveSingle(parent=None, title='Save', directory='', filename='', + wildcard='All Files (*.*)|*.*', + style=wx.wxSAVE | wx.wxHIDE_READONLY | wx.wxOVERWRITE_PROMPT): + """File dialog wrapper function.""" + dialog = wx.wxFileDialog(parent, title, directory, filename, + wildcard, style) + result = DialogResults(dialog.ShowModal()) + if result.positive: + result.path = dialog.GetPath() + else: + result.path = None + dialog.Destroy() + return result + + +def directory(parent=None, message='Choose a directory', path='', style=0, + pos=wx.wxDefaultPosition, size=wx.wxDefaultSize): + """Dir dialog wrapper function.""" + dialog = wx.wxDirDialog(parent, message, path, style, pos, size) + result = DialogResults(dialog.ShowModal()) + if result.positive: + result.path = dialog.GetPath() + else: + result.path = None + dialog.Destroy() + return result + + +def messageDialog(parent=None, message='', title='Message box', + style=wx.wxYES_NO | wx.wxCANCEL | wx.wxCENTRE | wx.wxICON_QUESTION, + pos=wx.wxDefaultPosition): + """Message dialog wrapper function.""" + dialog = wx.wxMessageDialog(parent, message, title, style, pos) + result = DialogResults(dialog.ShowModal()) + dialog.Destroy() + return result diff --git a/wxPython/wxPython/py/filling.py b/wxPython/wxPython/py/filling.py new file mode 100644 index 0000000000..99ce5358b6 --- /dev/null +++ b/wxPython/wxPython/py/filling.py @@ -0,0 +1,335 @@ +"""PyCrust Filling is the gui tree control through which a user can +navigate the local namespace or any object.""" + +__author__ = "Patrick K. O'Brien " +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +from wxPython import wx + +import base +import dispatcher +import inspect +import introspect +import keyword +import sys +import types +from version import VERSION + +try: + True +except NameError: + True = 1==1 + False = 1==0 + +COMMONTYPES = [getattr(types, t) for t in dir(types) \ + if not t.startswith('_') \ + and t not in ('ClassType', 'InstanceType', 'ModuleType')] + +DOCTYPES = ('BuiltinFunctionType', 'BuiltinMethodType', 'ClassType', + 'FunctionType', 'GeneratorType', 'InstanceType', + 'LambdaType', 'MethodType', 'ModuleType', + 'UnboundMethodType', 'method-wrapper') + +SIMPLETYPES = [getattr(types, t) for t in dir(types) \ + if not t.startswith('_') and t not in DOCTYPES] + +try: + COMMONTYPES.append(type(''.__repr__)) # Method-wrapper in version 2.2.x. +except AttributeError: + pass + + +class FillingTree(wx.wxTreeCtrl): + """PyCrust FillingTree based on wxTreeCtrl.""" + + name = 'PyCrust Filling Tree' + revision = __revision__ + + def __init__(self, parent, id=-1, pos=wx.wxDefaultPosition, + size=wx.wxDefaultSize, style=wx.wxTR_DEFAULT_STYLE, + rootObject=None, rootLabel=None, rootIsNamespace=False, + static=False): + """Create a PyCrust FillingTree instance.""" + wx.wxTreeCtrl.__init__(self, parent, id, pos, size, style) + self.rootIsNamespace = rootIsNamespace + import __main__ + if rootObject is None: + rootObject = __main__.__dict__ + self.rootIsNamespace = True + if rootObject is __main__.__dict__ and rootLabel is None: + rootLabel = 'locals()' + if not rootLabel: + rootLabel = 'Ingredients' + rootData = wx.wxTreeItemData(rootObject) + self.item = self.root = self.AddRoot(rootLabel, -1, -1, rootData) + self.SetItemHasChildren(self.root, self.objHasChildren(rootObject)) + wx.EVT_TREE_ITEM_EXPANDING(self, self.GetId(), self.OnItemExpanding) + wx.EVT_TREE_ITEM_COLLAPSED(self, self.GetId(), self.OnItemCollapsed) + wx.EVT_TREE_SEL_CHANGED(self, self.GetId(), self.OnSelChanged) + wx.EVT_TREE_ITEM_ACTIVATED(self, self.GetId(), self.OnItemActivated) + if not static: + dispatcher.connect(receiver=self.push, signal='Interpreter.push') + + def push(self, command, more): + """Receiver for Interpreter.push signal.""" + self.display() + + def OnItemExpanding(self, event): + """Add children to the item.""" + busy = wx.wxBusyCursor() + item = event.GetItem() + if self.IsExpanded(item): + return + self.addChildren(item) +# self.SelectItem(item) + + def OnItemCollapsed(self, event): + """Remove all children from the item.""" + busy = wx.wxBusyCursor() + item = event.GetItem() +# self.CollapseAndReset(item) +# self.DeleteChildren(item) +# self.SelectItem(item) + + def OnSelChanged(self, event): + """Display information about the item.""" + busy = wx.wxBusyCursor() + self.item = event.GetItem() + self.display() + + def OnItemActivated(self, event): + """Launch a DirFrame.""" + item = event.GetItem() + text = self.getFullName(item) + obj = self.GetPyData(item) + frame = FillingFrame(parent=self, size=(600, 100), rootObject=obj, + rootLabel=text, rootIsNamespace=False) + frame.Show() + + def objHasChildren(self, obj): + """Return true if object has children.""" + if self.objGetChildren(obj): + return True + else: + return False + + def objGetChildren(self, obj): + """Return dictionary with attributes or contents of object.""" + busy = wx.wxBusyCursor() + otype = type(obj) + if otype is types.DictType \ + or str(otype)[17:23] == 'BTrees' and hasattr(obj, 'keys'): + return obj + d = {} + if otype is types.ListType or otype is types.TupleType: + for n in range(len(obj)): + key = '[' + str(n) + ']' + d[key] = obj[n] + if otype not in COMMONTYPES: + for key in introspect.getAttributeNames(obj): + # Believe it or not, some attributes can disappear, + # such as the exc_traceback attribute of the sys + # module. So this is nested in a try block. + try: + d[key] = getattr(obj, key) + except: + pass + return d + + def addChildren(self, item): + self.DeleteChildren(item) + obj = self.GetPyData(item) + children = self.objGetChildren(obj) + if not children: + return + keys = children.keys() + keys.sort(lambda x, y: cmp(x.lower(), y.lower())) + for key in keys: + itemtext = str(key) + # Show string dictionary items with single quotes, except + # for the first level of items, if they represent a + # namespace. + if type(obj) is types.DictType \ + and type(key) is types.StringType \ + and (item != self.root \ + or (item == self.root and not self.rootIsNamespace)): + itemtext = repr(key) + child = children[key] + data = wx.wxTreeItemData(child) + branch = self.AppendItem(parent=item, text=itemtext, data=data) + self.SetItemHasChildren(branch, self.objHasChildren(child)) + + def display(self): + item = self.item + if self.IsExpanded(item): + self.addChildren(item) + self.setText('') + obj = self.GetPyData(item) + if wx.wxPlatform == '__WXMSW__': + if obj is None: # Windows bug fix. + return + self.SetItemHasChildren(item, self.objHasChildren(obj)) + otype = type(obj) + text = '' + text += self.getFullName(item) + text += '\n\nType: ' + str(otype) + try: + value = str(obj) + except: + value = '' + if otype is types.StringType or otype is types.UnicodeType: + value = repr(obj) + text += '\n\nValue: ' + value + if otype not in SIMPLETYPES: + try: + text += '\n\nDocstring:\n\n"""' + \ + inspect.getdoc(obj).strip() + '"""' + except: + pass + if otype is types.InstanceType: + try: + text += '\n\nClass Definition:\n\n' + \ + inspect.getsource(obj.__class__) + except: + pass + else: + try: + text += '\n\nSource Code:\n\n' + \ + inspect.getsource(obj) + except: + pass + self.setText(text) + + def getFullName(self, item, partial=''): + """Return a syntactically proper name for item.""" + name = self.GetItemText(item) + parent = None + obj = None + if item != self.root: + parent = self.GetItemParent(item) + obj = self.GetPyData(parent) + # Apply dictionary syntax to dictionary items, except the root + # and first level children of a namepace. + if (type(obj) is types.DictType \ + or str(type(obj))[17:23] == 'BTrees' \ + and hasattr(obj, 'keys')) \ + and ((item != self.root and parent != self.root) \ + or (parent == self.root and not self.rootIsNamespace)): + name = '[' + name + ']' + # Apply dot syntax to multipart names. + if partial: + if partial[0] == '[': + name += partial + else: + name += '.' + partial + # Repeat for everything but the root item + # and first level children of a namespace. + if (item != self.root and parent != self.root) \ + or (parent == self.root and not self.rootIsNamespace): + name = self.getFullName(parent, partial=name) + return name + + def setText(self, text): + """Display information about the current selection.""" + + # This method will likely be replaced by the enclosing app to + # do something more interesting, like write to a text control. + print text + + def setStatusText(self, text): + """Display status information.""" + + # This method will likely be replaced by the enclosing app to + # do something more interesting, like write to a status bar. + print text + + +class FillingText(base.Editor): + """FillingText based on StyledTextCtrl.""" + + name = 'PyFilling Text' + revision = __revision__ + + def __init__(self, parent, id=-1, pos=wx.wxDefaultPosition, + size=wx.wxDefaultSize, style=wx.wxCLIP_CHILDREN, + static=False): + """Create a FillingText instance.""" + base.Editor.__init__(self, parent, id, pos, size, style) + # Configure various defaults and user preferences. + self.SetReadOnly(True) + self.SetWrapMode(True) + self.SetMarginWidth(1, 0) + if not static: + dispatcher.connect(receiver=self.push, signal='Interpreter.push') + + def push(self, command, more): + """Receiver for Interpreter.push signal.""" + self.Refresh() + + def SetText(self, *args, **kwds): + self.SetReadOnly(False) + base.Editor.SetText(self, *args, **kwds) + self.SetReadOnly(True) + + +class Filling(wx.wxSplitterWindow): + """Filling based on wxSplitterWindow.""" + + name = 'PyFilling' + revision = __revision__ + + def __init__(self, parent, id=-1, pos=wx.wxDefaultPosition, + size=wx.wxDefaultSize, style=wx.wxSP_3D, + name='Filling Window', rootObject=None, + rootLabel=None, rootIsNamespace=False, static=False): + """Create a Filling instance.""" + wx.wxSplitterWindow.__init__(self, parent, id, pos, size, style, name) + self.tree = FillingTree(parent=self, rootObject=rootObject, + rootLabel=rootLabel, + rootIsNamespace=rootIsNamespace, + static=static) + self.text = FillingText(parent=self, static=static) + self.SplitVertically(self.tree, self.text, 130) + self.SetMinimumPaneSize(1) + # Override the filling so that descriptions go to FillingText. + self.tree.setText = self.text.SetText + # Display the root item. +## self.tree.SelectItem(self.tree.root) + self.tree.display() + + +class FillingFrame(wx.wxFrame): + """Frame containing the namespace tree component.""" + + name = 'PyFilling Frame' + revision = __revision__ + + def __init__(self, parent=None, id=-1, title='PyFilling', + pos=wx.wxDefaultPosition, size=(600, 400), + style=wx.wxDEFAULT_FRAME_STYLE, rootObject=None, + rootLabel=None, rootIsNamespace=False, static=False): + """Create a FillingFrame instance.""" + wx.wxFrame.__init__(self, parent, id, title, pos, size, style) + intro = 'PyFilling - The Tastiest Namespace Inspector' + self.CreateStatusBar() + self.SetStatusText(intro) + import images + self.SetIcon(images.getPyCrustIcon()) + self.filling = Filling(parent=self, rootObject=rootObject, + rootLabel=rootLabel, + rootIsNamespace=rootIsNamespace, + static=static) + # Override so that status messages go to the status bar. + self.filling.tree.setStatusText = self.SetStatusText + + +class App(wx.wxApp): + """PyFilling standalone application.""" + + def OnInit(self): + wx.wxInitAllImageHandlers() + self.fillingFrame = FillingFrame() + self.fillingFrame.Show(True) + self.SetTopWindow(self.fillingFrame) + return True diff --git a/wxPython/wxPython/py/frame.py b/wxPython/wxPython/py/frame.py new file mode 100644 index 0000000000..2291004fd0 --- /dev/null +++ b/wxPython/wxPython/py/frame.py @@ -0,0 +1,348 @@ +"""Base frame with menu.""" + +__author__ = "Patrick K. O'Brien " +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +from wxPython import wx +from version import VERSION + +try: + True +except NameError: + True = 1==1 + False = 1==0 + +ID_NEW = wx.wxID_NEW +ID_OPEN = wx.wxID_OPEN +ID_REVERT = wx.wxID_REVERT +ID_CLOSE = wx.wxID_CLOSE +ID_SAVE = wx.wxID_SAVE +ID_SAVEAS = wx.wxID_SAVEAS +ID_PRINT = wx.wxID_PRINT +ID_EXIT = wx.wxID_EXIT +ID_UNDO = wx.wxID_UNDO +ID_REDO = wx.wxID_REDO +ID_CUT = wx.wxID_CUT +ID_COPY = wx.wxID_COPY +ID_PASTE = wx.wxID_PASTE +ID_CLEAR = wx.wxID_CLEAR +ID_SELECTALL = wx.wxID_SELECTALL +ID_ABOUT = wx.wxID_ABOUT +ID_AUTOCOMP = wx.wxNewId() +ID_AUTOCOMP_SHOW = wx.wxNewId() +ID_AUTOCOMP_MAGIC = wx.wxNewId() +ID_AUTOCOMP_SINGLE = wx.wxNewId() +ID_AUTOCOMP_DOUBLE = wx.wxNewId() +ID_CALLTIPS = wx.wxNewId() +ID_CALLTIPS_SHOW = wx.wxNewId() +ID_COPY_PLUS = wx.wxNewId() +ID_NAMESPACE = wx.wxNewId() +ID_PASTE_PLUS = wx.wxNewId() +ID_WRAP = wx.wxNewId() + + +class Frame(wx.wxFrame): + """Frame with standard menu items.""" + + revision = __revision__ + + def __init__(self, parent=None, id=-1, title='Editor', + pos=wx.wxDefaultPosition, size=wx.wxDefaultSize, + style=wx.wxDEFAULT_FRAME_STYLE): + """Create a Frame instance.""" + wx.wxFrame.__init__(self, parent, id, title, pos, size, style) + self.CreateStatusBar() + self.SetStatusText('Frame') + import images + self.SetIcon(images.getPyCrustIcon()) + self.__createMenus() + wx.EVT_CLOSE(self, self.OnClose) + + def OnClose(self, event): + """Event handler for closing.""" + self.Destroy() + + def __createMenus(self): + m = self.fileMenu = wx.wxMenu() + m.Append(ID_NEW, '&New \tCtrl+N', + 'New file') + m.Append(ID_OPEN, '&Open... \tCtrl+O', + 'Open file') + m.AppendSeparator() + m.Append(ID_REVERT, '&Revert \tCtrl+R', + 'Revert to last saved version') + m.Append(ID_CLOSE, '&Close \tCtrl+W', + 'Close file') + m.AppendSeparator() + m.Append(ID_SAVE, '&Save... \tCtrl+S', + 'Save file') + m.Append(ID_SAVEAS, 'Save &As \tShift+Ctrl+S', + 'Save file with new name') + m.AppendSeparator() + m.Append(ID_PRINT, '&Print... \tCtrl+P', + 'Print file') + m.AppendSeparator() + m.Append(ID_NAMESPACE, '&Update Namespace \tShift+Ctrl+N', + 'Update namespace for autocompletion and calltips') + m.AppendSeparator() + m.Append(ID_EXIT, 'E&xit', 'Exit Program') + + m = self.editMenu = wx.wxMenu() + m.Append(ID_UNDO, '&Undo \tCtrl+Z', + 'Undo the last action') + m.Append(ID_REDO, '&Redo \tCtrl+Y', + 'Redo the last undone action') + m.AppendSeparator() + m.Append(ID_CUT, 'Cu&t \tCtrl+X', + 'Cut the selection') + m.Append(ID_COPY, '&Copy \tCtrl+C', + 'Copy the selection') + m.Append(ID_COPY_PLUS, 'Cop&y Plus \tShift+Ctrl+C', + 'Copy the selection - retaining prompts') + m.Append(ID_PASTE, '&Paste \tCtrl+V', 'Paste from clipboard') + m.Append(ID_PASTE_PLUS, 'Past&e Plus \tShift+Ctrl+V', + 'Paste and run commands') + m.AppendSeparator() + m.Append(ID_CLEAR, 'Cle&ar', + 'Delete the selection') + m.Append(ID_SELECTALL, 'Select A&ll \tCtrl+A', + 'Select all text') + + m = self.autocompMenu = wx.wxMenu() + m.Append(ID_AUTOCOMP_SHOW, 'Show Auto Completion', + 'Show auto completion list', 1) + m.Append(ID_AUTOCOMP_MAGIC, 'Include Magic Attributes', + 'Include attributes visible to __getattr__ and __setattr__', + 1) + m.Append(ID_AUTOCOMP_SINGLE, 'Include Single Underscores', + 'Include attibutes prefixed by a single underscore', 1) + m.Append(ID_AUTOCOMP_DOUBLE, 'Include Double Underscores', + 'Include attibutes prefixed by a double underscore', 1) + + m = self.calltipsMenu = wx.wxMenu() + m.Append(ID_CALLTIPS_SHOW, 'Show Call Tips', + 'Show call tips with argument signature and docstring', 1) + + m = self.optionsMenu = wx.wxMenu() + m.AppendMenu(ID_AUTOCOMP, '&Auto Completion', self.autocompMenu, + 'Auto Completion Options') + m.AppendMenu(ID_CALLTIPS, '&Call Tips', self.calltipsMenu, + 'Call Tip Options') + m.Append(ID_WRAP, '&Wrap Lines', + 'Wrap lines at right edge', 1) + + m = self.helpMenu = wx.wxMenu() + m.AppendSeparator() + m.Append(ID_ABOUT, '&About...', 'About this program') + + b = self.menuBar = wx.wxMenuBar() + b.Append(self.fileMenu, '&File') + b.Append(self.editMenu, '&Edit') + b.Append(self.optionsMenu, '&Options') + b.Append(self.helpMenu, '&Help') + self.SetMenuBar(b) + + wx.EVT_MENU(self, ID_NEW, self.OnFileNew) + wx.EVT_MENU(self, ID_OPEN, self.OnFileOpen) + wx.EVT_MENU(self, ID_REVERT, self.OnFileRevert) + wx.EVT_MENU(self, ID_CLOSE, self.OnFileClose) + wx.EVT_MENU(self, ID_SAVE, self.OnFileSave) + wx.EVT_MENU(self, ID_SAVEAS, self.OnFileSaveAs) + wx.EVT_MENU(self, ID_NAMESPACE, self.OnFileUpdateNamespace) + wx.EVT_MENU(self, ID_PRINT, self.OnFilePrint) + wx.EVT_MENU(self, ID_EXIT, self.OnExit) + wx.EVT_MENU(self, ID_UNDO, self.OnUndo) + wx.EVT_MENU(self, ID_REDO, self.OnRedo) + wx.EVT_MENU(self, ID_CUT, self.OnCut) + wx.EVT_MENU(self, ID_COPY, self.OnCopy) + wx.EVT_MENU(self, ID_COPY_PLUS, self.OnCopyPlus) + wx.EVT_MENU(self, ID_PASTE, self.OnPaste) + wx.EVT_MENU(self, ID_PASTE_PLUS, self.OnPastePlus) + wx.EVT_MENU(self, ID_CLEAR, self.OnClear) + wx.EVT_MENU(self, ID_SELECTALL, self.OnSelectAll) + wx.EVT_MENU(self, ID_ABOUT, self.OnAbout) + wx.EVT_MENU(self, ID_AUTOCOMP_SHOW, self.OnAutoCompleteShow) + wx.EVT_MENU(self, ID_AUTOCOMP_MAGIC, self.OnAutoCompleteMagic) + wx.EVT_MENU(self, ID_AUTOCOMP_SINGLE, self.OnAutoCompleteSingle) + wx.EVT_MENU(self, ID_AUTOCOMP_DOUBLE, self.OnAutoCompleteDouble) + wx.EVT_MENU(self, ID_CALLTIPS_SHOW, self.OnCallTipsShow) + wx.EVT_MENU(self, ID_WRAP, self.OnWrap) + + wx.EVT_UPDATE_UI(self, ID_NEW, self.OnUpdateMenu) + wx.EVT_UPDATE_UI(self, ID_OPEN, self.OnUpdateMenu) + wx.EVT_UPDATE_UI(self, ID_REVERT, self.OnUpdateMenu) + wx.EVT_UPDATE_UI(self, ID_CLOSE, self.OnUpdateMenu) + wx.EVT_UPDATE_UI(self, ID_SAVE, self.OnUpdateMenu) + wx.EVT_UPDATE_UI(self, ID_SAVEAS, self.OnUpdateMenu) + wx.EVT_UPDATE_UI(self, ID_NAMESPACE, self.OnUpdateMenu) + wx.EVT_UPDATE_UI(self, ID_PRINT, self.OnUpdateMenu) + wx.EVT_UPDATE_UI(self, ID_UNDO, self.OnUpdateMenu) + wx.EVT_UPDATE_UI(self, ID_REDO, self.OnUpdateMenu) + wx.EVT_UPDATE_UI(self, ID_CUT, self.OnUpdateMenu) + wx.EVT_UPDATE_UI(self, ID_COPY, self.OnUpdateMenu) + wx.EVT_UPDATE_UI(self, ID_COPY_PLUS, self.OnUpdateMenu) + wx.EVT_UPDATE_UI(self, ID_PASTE, self.OnUpdateMenu) + wx.EVT_UPDATE_UI(self, ID_PASTE_PLUS, self.OnUpdateMenu) + wx.EVT_UPDATE_UI(self, ID_CLEAR, self.OnUpdateMenu) + wx.EVT_UPDATE_UI(self, ID_SELECTALL, self.OnUpdateMenu) + wx.EVT_UPDATE_UI(self, ID_AUTOCOMP_SHOW, self.OnUpdateMenu) + wx.EVT_UPDATE_UI(self, ID_AUTOCOMP_MAGIC, self.OnUpdateMenu) + wx.EVT_UPDATE_UI(self, ID_AUTOCOMP_SINGLE, self.OnUpdateMenu) + wx.EVT_UPDATE_UI(self, ID_AUTOCOMP_DOUBLE, self.OnUpdateMenu) + wx.EVT_UPDATE_UI(self, ID_CALLTIPS_SHOW, self.OnUpdateMenu) + wx.EVT_UPDATE_UI(self, ID_WRAP, self.OnUpdateMenu) + + def OnFileNew(self, event): + self.bufferNew() + + def OnFileOpen(self, event): + self.bufferOpen() + + def OnFileRevert(self, event): + self.bufferRevert() + + def OnFileClose(self, event): + self.bufferClose() + + def OnFileSave(self, event): + self.bufferSave() + + def OnFileSaveAs(self, event): + self.bufferSaveAs() + + def OnFileUpdateNamespace(self, event): + self.updateNamespace() + + def OnFilePrint(self, event): + self.bufferPrint() + + def OnExit(self, event): + self.Close(False) + + def OnUndo(self, event): + win = wx.wxWindow_FindFocus() + win.Undo() + + def OnRedo(self, event): + win = wx.wxWindow_FindFocus() + win.Redo() + + def OnCut(self, event): + win = wx.wxWindow_FindFocus() + win.Cut() + + def OnCopy(self, event): + win = wx.wxWindow_FindFocus() + win.Copy() + + def OnCopyPlus(self, event): + win = wx.wxWindow_FindFocus() + win.CopyWithPrompts() + + def OnPaste(self, event): + win = wx.wxWindow_FindFocus() + win.Paste() + + def OnPastePlus(self, event): + win = wx.wxWindow_FindFocus() + win.PasteAndRun() + + def OnClear(self, event): + win = wx.wxWindow_FindFocus() + win.Clear() + + def OnSelectAll(self, event): + win = wx.wxWindow_FindFocus() + win.SelectAll() + + def OnAbout(self, event): + """Display an About window.""" + title = 'About' + text = 'Your message here.' + dialog = wx.wxMessageDialog(self, text, title, + wx.wxOK | wx.wxICON_INFORMATION) + dialog.ShowModal() + dialog.Destroy() + + def OnAutoCompleteShow(self, event): + win = wx.wxWindow_FindFocus() + win.autoComplete = event.IsChecked() + + def OnAutoCompleteMagic(self, event): + win = wx.wxWindow_FindFocus() + win.autoCompleteIncludeMagic = event.IsChecked() + + def OnAutoCompleteSingle(self, event): + win = wx.wxWindow_FindFocus() + win.autoCompleteIncludeSingle = event.IsChecked() + + def OnAutoCompleteDouble(self, event): + win = wx.wxWindow_FindFocus() + win.autoCompleteIncludeDouble = event.IsChecked() + + def OnCallTipsShow(self, event): + win = wx.wxWindow_FindFocus() + win.autoCallTip = event.IsChecked() + + def OnWrap(self, event): + win = wx.wxWindow_FindFocus() + win.SetWrapMode(event.IsChecked()) + + def OnUpdateMenu(self, event): + """Update menu items based on current status and context.""" + win = wx.wxWindow_FindFocus() + id = event.GetId() + event.Enable(True) + try: + if id == ID_NEW: + event.Enable(hasattr(self, 'bufferNew')) + elif id == ID_OPEN: + event.Enable(hasattr(self, 'bufferOpen')) + elif id == ID_REVERT: + event.Enable(hasattr(self, 'bufferRevert') and self.hasBuffer()) + elif id == ID_CLOSE: + event.Enable(hasattr(self, 'bufferClose') and self.hasBuffer()) + elif id == ID_SAVE: + event.Enable(hasattr(self, 'bufferSave') and self.bufferHasChanged()) + elif id == ID_SAVEAS: + event.Enable(hasattr(self, 'bufferSaveAs') and self.hasBuffer()) + elif id == ID_NAMESPACE: + event.Enable(hasattr(self, 'updateNamespace') and self.hasBuffer()) + elif id == ID_PRINT: + event.Enable(hasattr(self, 'bufferPrint') and self.hasBuffer()) + elif id == ID_UNDO: + event.Enable(win.CanUndo()) + elif id == ID_REDO: + event.Enable(win.CanRedo()) + elif id == ID_CUT: + event.Enable(win.CanCut()) + elif id == ID_COPY: + event.Enable(win.CanCopy()) + elif id == ID_COPY_PLUS: + event.Enable(win.CanCopy() and hasattr(win, 'CopyWithPrompts')) + elif id == ID_PASTE: + event.Enable(win.CanPaste()) + elif id == ID_PASTE_PLUS: + event.Enable(win.CanPaste() and hasattr(win, 'PasteAndRun')) + elif id == ID_CLEAR: + event.Enable(win.CanCut()) + elif id == ID_SELECTALL: + event.Enable(hasattr(win, 'SelectAll')) + elif id == ID_AUTOCOMP_SHOW: + event.Check(win.autoComplete) + elif id == ID_AUTOCOMP_MAGIC: + event.Check(win.autoCompleteIncludeMagic) + elif id == ID_AUTOCOMP_SINGLE: + event.Check(win.autoCompleteIncludeSingle) + elif id == ID_AUTOCOMP_DOUBLE: + event.Check(win.autoCompleteIncludeDouble) + elif id == ID_CALLTIPS_SHOW: + event.Check(win.autoCallTip) + elif id == ID_WRAP: + event.Check(win.GetWrapMode()) + else: + event.Enable(False) + except AttributeError: + # This menu option is not supported in the current context. + event.Enable(False) diff --git a/wxPython/wxPython/lib/PyCrust/images.py b/wxPython/wxPython/py/images.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/images.py rename to wxPython/wxPython/py/images.py diff --git a/wxPython/wxPython/lib/PyCrust/interpreter.py b/wxPython/wxPython/py/interpreter.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/interpreter.py rename to wxPython/wxPython/py/interpreter.py diff --git a/wxPython/wxPython/lib/PyCrust/introspect.py b/wxPython/wxPython/py/introspect.py similarity index 97% rename from wxPython/wxPython/lib/PyCrust/introspect.py rename to wxPython/wxPython/py/introspect.py index 482464735d..5c02dc624c 100644 --- a/wxPython/wxPython/lib/PyCrust/introspect.py +++ b/wxPython/wxPython/py/introspect.py @@ -9,6 +9,7 @@ from __future__ import nested_scopes import cStringIO import inspect +import sys import tokenize import types @@ -200,6 +201,10 @@ def getRoot(command, terminator=None): effects. The command would normally terminate with a '(' or '.'. The terminator and anything after the terminator will be dropped.""" + command = command.split('\n')[-1] + if command.startswith(sys.ps2): + command = command[len(sys.ps2):] + command = command.lstrip() command = rtrimTerminus(command, terminator) tokens = getTokens(command) if not tokens: @@ -207,13 +212,17 @@ def getRoot(command, terminator=None): if tokens[-1][0] is tokenize.ENDMARKER: # Remove the end marker. del tokens[-1] + if not tokens: + return '' if terminator == '.' and \ (tokens[-1][1] <> '.' or tokens[-1][0] is not tokenize.OP): # Trap decimals in numbers, versus the dot operator. return '' else: # Strip off the terminator. - command = command[:-1] + if terminator and command.endswith(terminator): + size = 0 - len(terminator) + command = command[:size] command = command.rstrip() tokens = getTokens(command) tokens.reverse() @@ -293,7 +302,7 @@ def getTokens(command): return tokens def rtrimTerminus(command, terminator=None): - """Return command minus anything that fillows the final terminator.""" + """Return command minus anything that follows the final terminator.""" if terminator: pieces = command.split(terminator) if len(pieces) > 1: diff --git a/wxPython/wxPython/lib/PyCrust/pseudo.py b/wxPython/wxPython/py/pseudo.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/pseudo.py rename to wxPython/wxPython/py/pseudo.py diff --git a/wxPython/wxPython/py/shell.py b/wxPython/wxPython/py/shell.py new file mode 100644 index 0000000000..028cf29061 --- /dev/null +++ b/wxPython/wxPython/py/shell.py @@ -0,0 +1,1021 @@ +"""The PyCrust 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. The latest files are always +available at the SourceForge project page at +http://sourceforge.net/projects/pycrust/. + +Sponsored by Orbtech - Your source for Python programming expertise.""" + +__author__ = "Patrick K. O'Brien " +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +from wxd.d_wx import wx +from wxd.d_stc import stc + +import keyword +import os +import sys +import time + +import base +import buffer +import dispatcher +import frame +from pseudo import PseudoFileIn +from pseudo import PseudoFileOut +from pseudo import PseudoFileErr +from version import VERSION + +try: + True +except NameError: + True = 1==1 + False = 1==0 + +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 containing the PyCrust shell component.""" + + name = 'PyCrust Shell Frame' + revision = __revision__ + + def __init__(self, parent=None, id=-1, title='PyShell', + pos=wx.wxDefaultPosition, size=wx.wxDefaultSize, + style=wx.wxDEFAULT_FRAME_STYLE, locals=None, + InterpClass=None, *args, **kwds): + """Create a PyCrust ShellFrame instance.""" + frame.Frame.__init__(self, parent, id, title, pos, size, style) + intro = 'PyCrust %s - The Flakiest Python Shell' % VERSION + intro += '\nSponsored by Orbtech - ' + \ + 'Your source for Python programming expertise.' + self.SetStatusText(intro.replace('\n', ', ')) + self.shell = Shell(parent=self, id=-1, introText=intro, + locals=locals, InterpClass=InterpClass, + *args, **kwds) + # Override the shell so that status messages go to the status bar. + self.shell.setStatusText = self.SetStatusText + + 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.shell.destroy() + self.Destroy() + + +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 = 'PyCrust Shell Interface' + revision = __revision__ + + def __init__(self, other): + """Create a ShellFacade instance.""" + d = self.__dict__ + d['other'] = other + d['helpText'] = \ +""" +* 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. +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. +""" + + 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', + 'autoCompleteCaseInsensitive', + 'autoCompleteIncludeDouble', + 'autoCompleteIncludeMagic', + 'autoCompleteIncludeSingle', + 'clear', + 'pause', + 'prompt', + 'quit', + 'redirectStderr', + 'redirectStdin', + 'redirectStdout', + 'run', + 'runfile', + 'wrap', + 'zoom', + ] + list.sort() + return list + + +class Shell(base.Editor): + """PyCrust Shell based on StyledTextCtrl.""" + + name = 'PyCrust Shell' + revision = __revision__ + + def __init__(self, parent, id=-1, pos=wx.wxDefaultPosition, + size=wx.wxDefaultSize, style=wx.wxCLIP_CHILDREN, + introText='', locals=None, InterpClass=None, *args, **kwds): + """Create a PyCrust Shell instance.""" + base.Editor.__init__(self, parent, id, pos, size, style) + self.wrap() + if locals is None: + locals = {} + # Grab these so they can be restored by self.redirect* methods. + self.stdin = sys.stdin + self.stdout = sys.stdout + self.stderr = sys.stderr + # Add the current working directory "." to the search path. + sys.path.insert(0, os.curdir) + # 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.Buffer(editor=self, interp=self.interp, + filename=None) + # 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 + # Assign handlers for keyboard events. + wx.EVT_CHAR(self, self.OnChar) + wx.EVT_KEY_DOWN(self, self.OnKeyDown) + # Assign handler for idle time. + self.waiting = False + wx.EVT_IDLE(self, 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() + # Do this last so the user has complete control over their + # environment. They can override anything they want. + self.execStartupScript(self.interp.startupScript) + wx.wxCallAfter(self.ScrollToLine, 0) + + def destroy(self): + del self.interp + + 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. + """ + import __builtin__ + __builtin__.close = __builtin__.exit = __builtin__.quit = \ + 'Click on the close button to leave the application.' + + def quit(self): + """Quit the application.""" + + # XXX Good enough for now but later we want to send a close event. + + # In the close event handler we can make sure they want to + # quit. Other applications, like PythonCard, may choose to + # hide rather than quit so we should just post the event and + # let the surrounding app decide what it wants to do. + self.write('Click on the close button to leave the application.') + + def setLocalShell(self): + """Add 'shell' to locals as reference to ShellFacade instance.""" + self.interp.locals['shell'] = ShellFacade(other=self) + + def execStartupScript(self, startupScript): + """Execute the user's PYTHONSTARTUP script if they have one.""" + if startupScript and os.path.isfile(startupScript): + text = 'Startup script executed: ' + startupScript + self.push('print %r; execfile(%r)' % (text, startupScript)) + else: + self.push('') + + def about(self): + """Display information about PyCrust.""" + text = """ +Author: %r +PyCrust Version: %s +Shell Revision: %s +Interpreter Revision: %s +Python Version: %s +wxPython Version: %s +Platform: %s""" % \ + (__author__, VERSION, self.revision, self.interp.revision, + sys.version.split()[0], wx.__version__, sys.platform) + self.write(text.strip()) + + def OnChar(self, event): + """Keypress event handler. + + Only receives an event if OnKeyDown calls event.Skip() for the + corresponding event.""" + + # Prevent modification of previously submitted + # commands/responses. + if not self.CanEdit(): + return + key = event.KeyCode() + currpos = self.GetCurrentPos() + stoppos = self.promptPosEnd + # Return (Enter) needs to be ignored in this handler. + if key == wx.WXK_RETURN: + pass + elif key in self.autoCompleteKeys: + # Usually the dot (period) key activates auto completion. + # Get the command between the prompt and the cursor. Add + # the autocomplete character to the end of the command. + if self.AutoCompActive(): + self.AutoCompCancel() + command = self.GetTextRange(stoppos, currpos) + chr(key) + self.write(chr(key)) + if self.autoComplete: + self.autoCompleteShow(command) + elif key == ord('('): + # The left paren activates a call tip and cancels an + # active auto completion. + if self.AutoCompActive(): + self.AutoCompCancel() + # Get the command between the prompt and the cursor. Add + # the '(' to the end of the command. + self.ReplaceSelection('') + command = self.GetTextRange(stoppos, currpos) + '(' + self.write('(') + self.autoCallTipShow(command) + else: + # Allow the normal event handling to take place. + event.Skip() + + def OnKeyDown(self, event): + """Key down event handler.""" + + key = event.KeyCode() + # If the auto-complete window is up let it do its thing. + if self.AutoCompActive(): + event.Skip() + return + # Prevent modification of previously submitted + # commands/responses. + controlDown = event.ControlDown() + altDown = event.AltDown() + shiftDown = event.ShiftDown() + currpos = self.GetCurrentPos() + endpos = self.GetTextLength() + selecting = self.GetSelectionStart() != self.GetSelectionEnd() + # Return (Enter) is used to submit a command to the + # interpreter. + if not controlDown and key == wx.WXK_RETURN: + if self.CallTipActive(): + self.CallTipCancel() + self.processLine() + # Ctrl+Return (Cntrl+Enter) is used to insert a line break. + elif controlDown and key == wx.WXK_RETURN: + if self.CallTipActive(): + self.CallTipCancel() + if currpos == endpos: + self.processLine() + else: + self.insertLineBreak() + # Let Ctrl-Alt-* get handled normally. + elif controlDown and altDown: + event.Skip() + # Clear the current, unexecuted command. + elif key == wx.WXK_ESCAPE: + if self.CallTipActive(): + event.Skip() + else: + self.clearCommand() + # Increase font size. + elif controlDown and key in (ord(']'),): + dispatcher.send(signal='FontIncrease') + # Decrease font size. + elif controlDown and key in (ord('['),): + dispatcher.send(signal='FontDecrease') + # Default font size. + elif controlDown and key in (ord('='),): + dispatcher.send(signal='FontDefault') + # Cut to the clipboard. + elif (controlDown and key in (ord('X'), ord('x'))) \ + or (shiftDown and key == wx.WXK_DELETE): + self.Cut() + # Copy to the clipboard. + elif controlDown and not shiftDown \ + and key in (ord('C'), ord('c'), wx.WXK_INSERT): + self.Copy() + # Copy to the clipboard, including prompts. + elif controlDown and shiftDown \ + and key in (ord('C'), ord('c'), wx.WXK_INSERT): + self.CopyWithPrompts() + # Copy to the clipboard, including prefixed prompts. + elif altDown and not controlDown \ + and key in (ord('C'), ord('c'), wx.WXK_INSERT): + self.CopyWithPromptsPrefixed() + # Home needs to be aware of the prompt. + elif key == wx.WXK_HOME: + home = self.promptPosEnd + if currpos > home: + 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 selecting and key not in NAVKEYS and not self.CanEdit(): + pass + # Paste from the clipboard. + elif (controlDown and not shiftDown and key in (ord('V'), ord('v'))) \ + or (shiftDown and not controlDown and key == wx.WXK_INSERT): + self.Paste() + # Paste from the clipboard, run commands. + elif controlDown and shiftDown and key in (ord('V'), ord('v')): + self.PasteAndRun() + # Replace with the previous command from the history buffer. + elif (controlDown and key == wx.WXK_UP) \ + or (altDown and key in (ord('P'), ord('p'))): + self.OnHistoryReplace(step=+1) + # Replace with the next command from the history buffer. + elif (controlDown and key == wx.WXK_DOWN) \ + or (altDown and key in (ord('N'), ord('n'))): + self.OnHistoryReplace(step=-1) + # Insert the previous command from the history buffer. + elif (shiftDown and key == wx.WXK_UP) and self.CanEdit(): + self.OnHistoryInsert(step=+1) + # Insert the next command from the history buffer. + elif (shiftDown and key == wx.WXK_DOWN) and self.CanEdit(): + self.OnHistoryInsert(step=-1) + # Search up the history for the text in front of the cursor. + elif key == wx.WXK_F8: + self.OnHistorySearch() + # Don't backspace over the latest non-continuation prompt. + elif key == wx.WXK_BACK: + if selecting and self.CanEdit(): + event.Skip() + elif currpos > self.promptPosEnd: + event.Skip() + # Only allow these keys after the latest prompt. + elif key in (wx.WXK_TAB, wx.WXK_DELETE): + if self.CanEdit(): + event.Skip() + # Don't toggle between insert mode and overwrite mode. + elif key == wx.WXK_INSERT: + pass + # Don't allow line deletion. + elif controlDown and key in (ord('L'), ord('l')): + pass + # Don't allow line transposition. + elif controlDown and key in (ord('T'), ord('t')): + pass + # Basic navigation keys should work anywhere. + elif key in NAVKEYS: + event.Skip() + # Protect the readonly portion of the shell. + elif not self.CanEdit(): + pass + else: + event.Skip() + + def clearCommand(self): + """Delete the current, unexecuted command.""" + startpos = self.promptPosEnd + endpos = self.GetTextLength() + self.SetSelection(startpos, endpos) + self.ReplaceSelection('') + self.more = False + + def OnHistoryReplace(self, step): + """Replace with the previous/next command from the history buffer.""" + self.clearCommand() + self.replaceFromHistory(step) + + def replaceFromHistory(self, step): + """Replace selection with command from the history buffer.""" + ps2 = str(sys.ps2) + self.ReplaceSelection('') + newindex = self.historyIndex + step + if -1 <= newindex <= len(self.history): + self.historyIndex = newindex + if 0 <= newindex <= len(self.history)-1: + command = self.history[self.historyIndex] + command = command.replace('\n', os.linesep + ps2) + self.ReplaceSelection(command) + + def OnHistoryInsert(self, step): + """Insert the previous/next command from the history buffer.""" + if not self.CanEdit(): + return + startpos = self.GetCurrentPos() + self.replaceFromHistory(step) + endpos = self.GetCurrentPos() + self.SetSelection(endpos, startpos) + + def OnHistorySearch(self): + """Search up the history buffer for the text in front of the cursor.""" + if not self.CanEdit(): + return + startpos = self.GetCurrentPos() + # The text up to the cursor is what we search for. + numCharsAfterCursor = self.GetTextLength() - startpos + searchText = self.getCommand(rstrip=False) + if numCharsAfterCursor > 0: + searchText = searchText[:-numCharsAfterCursor] + if not searchText: + return + # Search upwards from the current history position and loop + # back to the beginning if we don't find anything. + if (self.historyIndex <= -1) \ + or (self.historyIndex >= len(self.history)-2): + searchOrder = range(len(self.history)) + else: + searchOrder = range(self.historyIndex+1, len(self.history)) + \ + range(self.historyIndex) + for i in searchOrder: + command = self.history[i] + if command[:len(searchText)] == searchText: + # Replace the current selection with the one we found. + self.ReplaceSelection(command[len(searchText):]) + endpos = self.GetCurrentPos() + self.SetSelection(endpos, startpos) + # We've now warped into middle of the history. + self.historyIndex = i + break + + def setStatusText(self, text): + """Display status information.""" + + # This method will likely be replaced by the enclosing app to + # do something more interesting, like write to a status bar. + print text + + def insertLineBreak(self): + """Insert a new line break.""" + if self.CanEdit(): + self.write(os.linesep) + self.more = True + self.prompt() + + def processLine(self): + """Process the line of text at which the user hit Enter.""" + + # The user hit ENTER and we need to decide what to do. They + # could be sitting on any line in the shell. + + thepos = self.GetCurrentPos() + startpos = self.promptPosEnd + endpos = self.GetTextLength() + ps2 = str(sys.ps2) + # If they hit RETURN inside the current command, execute the + # command. + if self.CanEdit(): + self.SetCurrentPos(endpos) + self.interp.more = False + command = self.GetTextRange(startpos, endpos) + lines = command.split(os.linesep + ps2) + lines = [line.rstrip() for line in lines] + command = '\n'.join(lines) + if self.reader.isreading: + if not command: + # Match the behavior of the standard Python shell + # when the user hits return without entering a + # value. + command = '\n' + self.reader.input = command + self.write(os.linesep) + else: + self.push(command) + # Or replace the current command with the other command. + else: + # If the line contains a command (even an invalid one). + if self.getCommand(rstrip=False): + command = self.getMultilineCommand() + self.clearCommand() + self.write(command) + # Otherwise, put the cursor back where we started. + else: + self.SetCurrentPos(thepos) + self.SetAnchor(thepos) + + def getMultilineCommand(self, rstrip=True): + """Extract a multi-line command from the editor. + + The command may not necessarily be valid Python syntax.""" + # XXX Need to extract real prompts here. Need to keep track of + # the prompt every time a command is issued. + ps1 = str(sys.ps1) + ps1size = len(ps1) + ps2 = str(sys.ps2) + ps2size = len(ps2) + # This is a total hack job, but it works. + text = self.GetCurLine()[0] + line = self.GetCurrentLine() + while text[:ps2size] == ps2 and line > 0: + line -= 1 + self.GotoLine(line) + text = self.GetCurLine()[0] + if text[:ps1size] == ps1: + line = self.GetCurrentLine() + self.GotoLine(line) + startpos = self.GetCurrentPos() + ps1size + line += 1 + self.GotoLine(line) + while self.GetCurLine()[0][:ps2size] == ps2: + line += 1 + self.GotoLine(line) + stoppos = self.GetCurrentPos() + command = self.GetTextRange(startpos, stoppos) + command = command.replace(os.linesep + ps2, '\n') + command = command.rstrip() + command = command.replace('\n', os.linesep + ps2) + else: + command = '' + if rstrip: + command = command.rstrip() + return command + + def getCommand(self, text=None, rstrip=True): + """Extract a command from text which may include a shell prompt. + + The command may not necessarily be valid Python syntax.""" + if not text: + text = self.GetCurLine()[0] + # Strip the prompt off the front leaving just the command. + command = self.lstripPrompt(text) + if command == text: + command = '' # Real commands have prompts. + if rstrip: + command = command.rstrip() + return command + + def lstripPrompt(self, text): + """Return text without a leading prompt.""" + ps1 = str(sys.ps1) + ps1size = len(ps1) + ps2 = str(sys.ps2) + ps2size = len(ps2) + # Strip the prompt off the front of text. + if text[:ps1size] == ps1: + text = text[ps1size:] + elif text[:ps2size] == ps2: + text = text[ps2size:] + return text + + def push(self, command): + """Send command to the interpreter for execution.""" + self.write(os.linesep) + busy = wx.wxBusyCursor() + self.waiting = True + self.more = self.interp.push(command) + self.waiting = False + del busy + if not self.more: + self.addHistory(command.rstrip()) + self.prompt() + + def addHistory(self, command): + """Add command to the command history.""" + # Reset the history position. + self.historyIndex = -1 + # Insert this command into the history, unless it's a blank + # line or the same as the last command. + if command != '' \ + and (len(self.history) == 0 or command != self.history[0]): + self.history.insert(0, command) + + def write(self, text): + """Display text in the shell. + + Replace line endings with OS-specific endings.""" + text = self.fixLineEndings(text) + self.AddText(text) + self.EnsureCaretVisible() + + def fixLineEndings(self, text): + """Return text with line endings replaced by OS-specific endings.""" + lines = text.split('\r\n') + for l in range(len(lines)): + chunks = lines[l].split('\r') + for c in range(len(chunks)): + chunks[c] = os.linesep.join(chunks[c].split('\n')) + lines[l] = os.linesep.join(chunks) + text = os.linesep.join(lines) + return text + + def prompt(self): + """Display proper prompt for the context: ps1, ps2 or ps3. + + If this is a continuation line, autoindent as necessary.""" + isreading = self.reader.isreading + skip = False + if isreading: + prompt = str(sys.ps3) + elif self.more: + prompt = str(sys.ps2) + else: + prompt = str(sys.ps1) + pos = self.GetCurLine()[1] + if pos > 0: + if isreading: + skip = True + else: + self.write(os.linesep) + if not self.more: + self.promptPosStart = self.GetCurrentPos() + if not skip: + self.write(prompt) + if not self.more: + self.promptPosEnd = self.GetCurrentPos() + # Keep the undo feature from undoing previous responses. + self.EmptyUndoBuffer() + # XXX Add some autoindent magic here if more. + if self.more: + self.write(' '*4) # Temporary hack indentation. + self.EnsureCaretVisible() + self.ScrollToColumn(0) + + def readline(self): + """Replacement for stdin.readline().""" + input = '' + reader = self.reader + reader.isreading = True + self.prompt() + try: + while not reader.input: + wx.wxYieldIfNeeded() + input = reader.input + finally: + reader.input = '' + reader.isreading = False + input = str(input) # In case of Unicode. + return input + + def readlines(self): + """Replacement for stdin.readlines().""" + lines = [] + while lines[-1:] != ['\n']: + lines.append(self.readline()) + return lines + + def raw_input(self, prompt=''): + """Return string based on user input.""" + if prompt: + self.write(prompt) + return self.readline() + + def ask(self, prompt='Please enter your response:'): + """Get response from the user using a dialog box.""" + dialog = wx.wxTextEntryDialog(None, prompt, + 'Input Dialog (Raw)', '') + try: + if dialog.ShowModal() == wx.wxID_OK: + text = dialog.GetValue() + return text + finally: + dialog.Destroy() + return '' + + def pause(self): + """Halt execution pending a response from the user.""" + self.ask('Press enter to continue:') + + def clear(self): + """Delete all text from the shell.""" + self.ClearAll() + + def run(self, command, prompt=True, verbose=True): + """Execute command as if it was typed in directly. + >>> shell.run('print "this"') + >>> print "this" + this + >>> + """ + # Go to the very bottom of the text. + endpos = self.GetTextLength() + self.SetCurrentPos(endpos) + command = command.rstrip() + if prompt: self.prompt() + if verbose: self.write(command) + self.push(command) + + def runfile(self, filename): + """Execute all commands in file as if they were typed into the + shell.""" + file = open(filename) + try: + self.prompt() + for command in file.readlines(): + if command[:6] == 'shell.': + # Run shell methods silently. + self.run(command, prompt=False, verbose=False) + else: + self.run(command, prompt=False, verbose=True) + finally: + file.close() + + def autoCompleteShow(self, command): + """Display auto-completion popup list.""" + list = self.interp.getAutoCompleteList(command, + includeMagic=self.autoCompleteIncludeMagic, + includeSingle=self.autoCompleteIncludeSingle, + includeDouble=self.autoCompleteIncludeDouble) + if list: + options = ' '.join(list) + offset = 0 + self.AutoCompShow(offset, options) + + def autoCallTipShow(self, command): + """Display argument spec and docstring in a popup window.""" + if self.CallTipActive(): + self.CallTipCancel() + (name, argspec, tip) = self.interp.getCallTip(command) + if tip: + dispatcher.send(signal='Shell.calltip', sender=self, calltip=tip) + if not self.autoCallTip: + return + if argspec: + startpos = self.GetCurrentPos() + self.write(argspec + ')') + endpos = self.GetCurrentPos() + self.SetSelection(endpos, startpos) + if tip: + curpos = self.GetCurrentPos() + tippos = curpos - (len(name) + 1) + fallback = curpos - self.GetColumn(curpos) + # In case there isn't enough room, only go back to the + # fallback. + tippos = max(tippos, fallback) + self.CallTipShow(tippos, tip) + + def writeOut(self, text): + """Replacement for stdout.""" + self.write(text) + + def writeErr(self, text): + """Replacement for stderr.""" + self.write(text) + + def redirectStdin(self, redirect=True): + """If redirect is true then sys.stdin will come from the shell.""" + if redirect: + sys.stdin = self.reader + else: + sys.stdin = self.stdin + + def redirectStdout(self, redirect=True): + """If redirect is true then sys.stdout will go to the shell.""" + if redirect: + sys.stdout = PseudoFileOut(self.writeOut) + else: + sys.stdout = self.stdout + + def redirectStderr(self, redirect=True): + """If redirect is true then sys.stderr will go to the shell.""" + if redirect: + sys.stderr = PseudoFileErr(self.writeErr) + else: + sys.stderr = self.stderr + + def CanCut(self): + """Return true if text is selected and can be cut.""" + if self.GetSelectionStart() != self.GetSelectionEnd() \ + and self.GetSelectionStart() >= self.promptPosEnd \ + and self.GetSelectionEnd() >= self.promptPosEnd: + return True + else: + return False + + def CanPaste(self): + """Return true if a paste should succeed.""" + if self.CanEdit() and base.Editor.CanPaste(self): + return True + else: + return False + + def CanEdit(self): + """Return true if editing should succeed.""" + if self.GetSelectionStart() != self.GetSelectionEnd(): + if self.GetSelectionStart() >= self.promptPosEnd \ + and self.GetSelectionEnd() >= self.promptPosEnd: + return True + else: + return False + else: + return self.GetCurrentPos() >= self.promptPosEnd + + def Cut(self): + """Remove selection and place it on the clipboard.""" + if self.CanCut() and self.CanCopy(): + if self.AutoCompActive(): + self.AutoCompCancel() + if self.CallTipActive(): + self.CallTipCancel() + self.Copy() + self.ReplaceSelection('') + + def Copy(self): + """Copy selection and place it on the clipboard.""" + if self.CanCopy(): + ps1 = str(sys.ps1) + ps2 = str(sys.ps2) + command = self.GetSelectedText() + command = command.replace(os.linesep + ps2, os.linesep) + command = command.replace(os.linesep + ps1, os.linesep) + command = self.lstripPrompt(text=command) + data = wx.wxTextDataObject(command) + self._clip(data) + + def CopyWithPrompts(self): + """Copy selection, including prompts, and place it on the clipboard.""" + if self.CanCopy(): + command = self.GetSelectedText() + data = wx.wxTextDataObject(command) + self._clip(data) + + def CopyWithPromptsPrefixed(self): + """Copy selection, including prompts prefixed with four + spaces, and place it on the clipboard.""" + if self.CanCopy(): + command = self.GetSelectedText() + spaces = ' ' * 4 + command = spaces + command.replace(os.linesep, + os.linesep + spaces) + data = wx.wxTextDataObject(command) + self._clip(data) + + def _clip(self, data): + if wx.wxTheClipboard.Open(): + wx.wxTheClipboard.UsePrimarySelection(False) + wx.wxTheClipboard.SetData(data) + wx.wxTheClipboard.Flush() + wx.wxTheClipboard.Close() + + def Paste(self): + """Replace selection with clipboard contents.""" + if self.CanPaste() and wx.wxTheClipboard.Open(): + ps2 = str(sys.ps2) + if wx.wxTheClipboard.IsSupported(wx.wxDataFormat(wx.wxDF_TEXT)): + data = wx.wxTextDataObject() + if wx.wxTheClipboard.GetData(data): + self.ReplaceSelection('') + command = data.GetText() + command = command.rstrip() + command = self.fixLineEndings(command) + command = self.lstripPrompt(text=command) + command = command.replace(os.linesep + ps2, '\n') + command = command.replace(os.linesep, '\n') + command = command.replace('\n', os.linesep + ps2) + self.write(command) + wx.wxTheClipboard.Close() + + def PasteAndRun(self): + """Replace selection with clipboard contents, run commands.""" + if wx.wxTheClipboard.Open(): + ps1 = str(sys.ps1) + ps2 = str(sys.ps2) + if wx.wxTheClipboard.IsSupported(wx.wxDataFormat(wx.wxDF_TEXT)): + data = wx.wxTextDataObject() + if wx.wxTheClipboard.GetData(data): + endpos = self.GetTextLength() + self.SetCurrentPos(endpos) + startpos = self.promptPosEnd + self.SetSelection(startpos, endpos) + self.ReplaceSelection('') + text = data.GetText() + text = text.lstrip() + text = self.fixLineEndings(text) + text = self.lstripPrompt(text) + text = text.replace(os.linesep + ps1, '\n') + text = text.replace(os.linesep + ps2, '\n') + text = text.replace(os.linesep, '\n') + lines = text.split('\n') + commands = [] + command = '' + for line in lines: + if line.strip() == ps2.strip(): + # If we are pasting from something like a + # web page that drops the trailing space + # from the ps2 prompt of a blank line. + line = '' + if line.strip() != '' and line.lstrip() == line: + # New command. + if command: + # Add the previous command to the list. + commands.append(command) + # Start a new command, which may be multiline. + command = line + else: + # Multiline command. Add to the command. + command += '\n' + command += line + commands.append(command) + for command in commands: + command = command.replace('\n', os.linesep + ps2) + self.write(command) + self.processLine() + wx.wxTheClipboard.Close() + + def wrap(self, wrap=True): + """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) diff --git a/wxPython/wxPython/py/tests/test_interpreter.py b/wxPython/wxPython/py/tests/test_interpreter.py new file mode 100644 index 0000000000..9ab26a091f --- /dev/null +++ b/wxPython/wxPython/py/tests/test_interpreter.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python + +__author__ = "Patrick K. O'Brien " +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import unittest + +# Import from this module's parent directory. +import os +import sys +sys.path.insert(0, os.pardir) +import interpreter +del sys.path[0] +del sys +del os + + +""" +These unittest methods are preferred: +------------------------------------- +self.assert_(expr, msg=None) +self.assertEqual(first, second, msg=None) +self.assertRaises(excClass, callableObj, *args, **kwargs) +self.fail(msg=None) +self.failIf(expr, msg=None) +""" + + +class ModuleTestCase(unittest.TestCase): + + def test_module(self): + module = interpreter + self.assert_(module.__author__) + self.assert_(module.__cvsid__) + self.assert_(module.__revision__) + self.assert_(module.Interpreter) + self.assert_(module.Interpreter.push) + self.assert_(module.Interpreter.runsource) + self.assert_(module.Interpreter.getAutoCompleteList) + self.assert_(module.Interpreter.getCallTip) + self.assert_(module.InterpreterAlaCarte) + + +class InterpreterTestCase(unittest.TestCase): + + def setUp(self): + self.output = '' + self.i = interpreter.Interpreter(stdout=self) + + def write(self, text): + """Capture output from self.i.push().""" + self.output += text + + def tearDown(self): + self.output = '' + self.i = None + del self.i + + def test_more(self): + self.assertEqual(self.i.push('dir()'), 0) + self.assertEqual(self.i.push('for n in range(3):'), 1) + + def test_push(self): + values = ( + ('dir', ''), + ('dir()', "['__builtins__', '__doc__', '__name__']"), + ('2 + 2', '4'), + ('d = {}', ''), + ('d', '{}'), + ('del d', ''), + ('len([4,5,6])', '3'), + ) + for input, output in values: + if output: output += '\n' + self.i.push(input) + self.assertEqual(self.output, output) + self.output = '' + + +if __name__ == '__main__': + unittest.main() diff --git a/wxPython/wxPython/py/tests/test_introspect.py b/wxPython/wxPython/py/tests/test_introspect.py new file mode 100644 index 0000000000..887b40c39e --- /dev/null +++ b/wxPython/wxPython/py/tests/test_introspect.py @@ -0,0 +1,862 @@ +#!/usr/bin/env python + +__author__ = "Patrick K. O'Brien " +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import unittest + +# Import from this module's parent directory. +import os +import sys +sys.path.insert(0, os.pardir) +import introspect +del sys.path[0] +del sys +del os + + +""" +These unittest methods are preferred: +------------------------------------- +self.assert_(expr, msg=None) +self.assertEqual(first, second, msg=None) +self.assertRaises(excClass, callableObj, *args, **kwargs) +self.fail(msg=None) +self.failIf(expr, msg=None) +""" + + +class ModuleTestCase(unittest.TestCase): + + def test_module(self): + module = introspect + self.assert_(module.__author__) + self.assert_(module.__cvsid__) + self.assert_(module.__revision__) + self.assert_(module.getAllAttributeNames) + self.assert_(module.getAttributeNames) + self.assert_(module.getAutoCompleteList) + self.assert_(module.getBaseObject) + self.assert_(module.getCallTip) + self.assert_(module.getConstructor) + self.assert_(module.getRoot) + self.assert_(module.rtrimTerminus) + + +class RtrimTerminusTestCase(unittest.TestCase): + + def test_rtrimTerminus(self): + values = ( + ('', '', ''), + ('', None, ''), + ('', '.', ''), + ('', '(', ''), + + ('.', '', '.'), + ('.', None, '.'), + ('.', '.', '.'), + ('.', '(', '.'), + + ('(', '', '('), + ('(', None, '('), + ('(', '.', '('), + ('(', '(', '('), + + ('spam', '', 'spam'), + ('spam', None, 'spam'), + ('spam', '.', 'spam'), + ('spam', '(', 'spam'), + + ('spam.', '', 'spam.'), + ('spam.', None, 'spam.'), + ('spam.', '.', 'spam.'), + ('spam.', '(', 'spam.'), + + ('spam(', '', 'spam('), + ('spam(', None, 'spam('), + ('spam(', '.', 'spam('), + ('spam(', '(', 'spam('), + + ('spam.eggs', '.', 'spam.'), + ('spam.eggs.', '.', 'spam.eggs.'), + ('spam.eggs(', '(', 'spam.eggs('), + ('spam.eggs.', '(', 'spam.eggs.'), + ('spam.eggs(', '.', 'spam.'), + + ('x = spam.', '.', 'x = spam.'), + ('x = spam.eggs', '.', 'x = spam.'), + ('x = spam.eggs.', '.', 'x = spam.eggs.'), + ('x = spam.eggs(', '(', 'x = spam.eggs('), + ) + for input, terminator, output in values: + result = introspect.rtrimTerminus(input, terminator) + self.assertEqual(result, output, + ':in: %r :t: %r :out: %r :result: %r' % + (input, terminator, output, result)) + + +class GetRootTestCase(unittest.TestCase): + + def _checkRoot(self, input, terminator, output): + root = introspect.getRoot(command=input, terminator=terminator) + self.assertEqual(root, output, + ':in: %r :t: %r :out: %r :root: %r' % + (input, terminator, output, root)) + + def test_getRoot(self): + values = ( + ('', '', ''), + ('', None, ''), + ('', '.', ''), + ('', '(', ''), + + ('.', '', '.'), + ('.', None, '.'), + ('.', '.', ''), + ('.', '(', '.'), + + ('(', '', ''), + ('(', None, ''), + ('(', '.', ''), + ('(', '(', ''), + + ('spam', '', 'spam'), + ('spam', None, 'spam'), + ('spam', '.', ''), + ('spam', '(', 'spam'), + + ('spam.', '', 'spam.'), + ('spam.', None, 'spam.'), + ('spam.', '.', 'spam'), + ('spam.', '(', 'spam.'), + + ('spam(', '', ''), + ('spam(', None, ''), + ('spam(', '.', ''), + ('spam(', '(', 'spam'), + + ('spam.eggs', '.', 'spam'), + ('spam.eggs.', '.', 'spam.eggs'), + ('spam.eggs(', '(', 'spam.eggs'), + ('spam.eggs.', '(', 'spam.eggs.'), + ('spam.eggs(', '.', 'spam'), + + ('x = spam.', '.', 'spam'), + ('x = spam.eggs', '.', 'spam'), + ('x = spam.eggs.', '.', 'spam.eggs'), + ('x = spam.eggs(', '(', 'spam.eggs'), + + ('for n in range(3):\n d.', '.', 'd'), + ('for n in range(3):\n... d.', '.', 'd'), + ) + for input, terminator, output in values: + self._checkRoot(input, terminator, output) + + def test_getRoot_Advanced(self): + values = ( + ('spam_', '', 'spam_'), + ('spam_', None, 'spam_'), + ('spam_', '.', ''), + ('spam_', '(', 'spam_'), + + ('_spam', '', '_spam'), + ('_spam', None, '_spam'), + ('_spam', '.', ''), + ('_spam', '(', '_spam'), + + ('spam_eggs', '', 'spam_eggs'), + ('spam_eggs', None, 'spam_eggs'), + ('spam_eggs', '.', ''), + ('spam_eggs', '(', 'spam_eggs'), + + ('spam123', '', 'spam123'), + ('spam123', None, 'spam123'), + ('spam123', '.', ''), + ('spam123', '(', 'spam123'), + + ('spam_123', '', 'spam_123'), + ('spam_123', None, 'spam_123'), + ('spam_123', '.', ''), + ('spam_123', '(', 'spam_123'), + ) + for input, terminator, output in values: + self._checkRoot(input, terminator, output) + +## The original intent was to detect when we were inside a string. +## That has proven to be very difficult, for little benefit. +## The fact that autocomplete or calltips might be triggered inside +## a string is not a big deal. Sometimes it is even helpful. + +## def test_getRoot_InsideStrings(self): +## values = ( +## ('x = ".', '.', ''), +## ("x = '.", '.', ''), +## ('x = """.', '.', ''), +## ("x = '''.", '.', ''), +## +## ('x = "(', '(', ''), +## ("x = '(", '(', ''), +## ('x = """(', '(', ''), +## ("x = '''(", '(', ''), +## +## ('x = "spam', '.', ''), +## ('x = "spam.', '.', ''), +## ("x = 'spam.", '.', ''), +## ('x = """spam.', '.', ''), +## ("x = '''spam.", '.', ''), +## +## ('x = "spam', '(', ''), +## ('x = "spam(', '(', ''), +## ("x = 'spam(", '(', ''), +## ('x = """spam(', '(', ''), +## ("x = '''spam(", '(', ''), +## +## ('x = "spam.eggs.', '.', ''), +## ("x = 'spam.eggs.", '.', ''), +## ('x = """spam.eggs.', '.', ''), +## ("x = '''spam.eggs.", '.', ''), +## +## ('x = "spam.eggs(', '(', ''), +## ("x = 'spam.eggs(", '(', ''), +## ('x = """spam.eggs(', '(', ''), +## ("x = '''spam.eggs(", '(', ''), +## ) +## for input, terminator, output in values: +## self._checkRoot(input, terminator, output) + + def test_getRoot_EmptyTypes(self): + values = ( + ("''.", '.', "''"), + ('"".', '.', '""'), + ('"""""".', '.', '""""""'), + ("''''''.", '.', "''''''"), + + ('[].', '.', '[]'), + ('().', '.', '()'), + ('{}.', '.', '{}'), + + ('[](', '(', '[]'), + ('()(', '(', '()'), + ('{}(', '(', '{}'), + + ("x = ''.", '.', "''"), + ('x = "".', '.', '""'), + ('x = """""".', '.', '""""""'), + ("x = ''''''.", '.', "''''''"), + + ('x = [].', '.', '[]'), + ('x = ().', '.', '()'), + ('x = {}.', '.', '{}'), + + ('x = [](', '(', '[]'), + ('x = ()(', '(', '()'), + ('x = {}(', '(', '{}'), + + ('print [].', '.', '[]'), + ('print ().', '.', '()'), + ('print {}.', '.', '{}'), + + ('print [](', '(', '[]'), + ('print ()(', '(', '()'), + ('print {}(', '(', '{}'), + + ("''.attr.", '.', "''.attr"), + ('"".attr.', '.', '"".attr'), + ('"""""".attr.', '.', '"""""".attr'), + ("''''''.attr.", '.', "''''''.attr"), + + ('[].attr.', '.', '[].attr'), + ('().attr.', '.', '().attr'), + ('{}.attr.', '.', '{}.attr'), + + ('[].attr(', '(', '[].attr'), + ('().attr(', '(', '().attr'), + ('{}.attr(', '(', '{}.attr'), + + ('spam().', '.', ''), + ('spam_().', '.', ''), + ('spam5().', '.', ''), + ('spam[]().', '.', ''), + ('spam()[].', '.', ''), + ('spam[]{}.', '.', ''), + + ("spam(''.", '.', "''"), + ('spam("".', '.', '""'), + ('spam("""""".', '.', '""""""'), + ("spam(''''''.", '.', "''''''"), + + ('spam([].', '.', '[]'), + ('spam(().', '.', '()'), + ('spam({}.', '.', '{}'), + ('spam[[].', '.', '[]'), + ('spam[().', '.', '()'), + ('spam[{}.', '.', '{}'), + ('x = {[].', '.', '[]'), + ('x = {().', '.', '()'), + ('x = {{}.', '.', '{}'), + + ('spam,[].', '.', '[]'), + ('spam+[].', '.', '[]'), + ('spam-[].', '.', '[]'), + ('spam*[].', '.', '[]'), + ('spam/[].', '.', '[]'), + ('spam=[].', '.', '[]'), + ('spam%[].', '.', '[]'), + ('spam<[].', '.', '[]'), + ('spam>[].', '.', '[]'), + ('spam&[].', '.', '[]'), + ('spam|[].', '.', '[]'), + ('spam^[].', '.', '[]'), + ('spam~[].', '.', '[]'), + ('spam:[].', '.', '[]'), + + ('spam,().', '.', '()'), + ('spam+().', '.', '()'), + ('spam-().', '.', '()'), + ('spam*().', '.', '()'), + ('spam/().', '.', '()'), + ('spam=().', '.', '()'), + ('spam%().', '.', '()'), + ('spam<().', '.', '()'), + ('spam>().', '.', '()'), + ('spam&().', '.', '()'), + ('spam|().', '.', '()'), + ('spam^().', '.', '()'), + ('spam~().', '.', '()'), + ('spam:().', '.', '()'), + + ('spam,{}.', '.', '{}'), + ('spam+{}.', '.', '{}'), + ('spam-{}.', '.', '{}'), + ('spam*{}.', '.', '{}'), + ('spam/{}.', '.', '{}'), + ('spam={}.', '.', '{}'), + ('spam%{}.', '.', '{}'), + ('spam<{}.', '.', '{}'), + ('spam>{}.', '.', '{}'), + ('spam&{}.', '.', '{}'), + ('spam|{}.', '.', '{}'), + ('spam^{}.', '.', '{}'), + ('spam~{}.', '.', '{}'), + ('spam:{}.', '.', '{}'), + ) + for input, terminator, output in values: + self._checkRoot(input, terminator, output) + + +# Support for GetBaseObjectTestCase and GetAttributeNamesTestCase. + +class Foo: + def __init__(self): + pass + + def __del__(self): + pass + + def _private(self): + pass + +class Bar: + pass + +class Spam: + def __call__(self): + pass + + def foo(self): + pass + + def bar(spam): + # It shouldn't matter what we call "self". + pass + + def eggs(self): + pass + +def ham(eggs): + pass + +class GetBaseObjectTestCase(unittest.TestCase): + + def test_getBaseObject(self): + spam = Spam() + eggs = Spam.eggs + listappend = [].append + spamda = lambda: None + values = ( + ('spam', 'spam', 0), + (123, 123, 0), + (12.3, 12.3, 0), + ([], [], 0), + ((), (), 0), + ({}, {}, 0), + # Builtin function. + (len, len, 0), + # Builtin method. + (listappend, listappend, 0), + # User function. + (ham, ham, 0), + # Byte-compiled code. + (ham.func_code, ham.func_code, 0), + # Lambda. + (spamda, spamda, 0), + # Class with init. + (Foo, Foo.__init__.im_func, 1), + # Class with no init. + (Bar, Bar, 0), + # Bound method. + (spam.foo, spam.foo.im_func, 1), + # Bound method with self named something else (spam). + (spam.bar, spam.bar.im_func, 1), + # Unbound method. (Do not drop the self argument.) + (eggs, eggs.im_func, 0), + # Callable instance. + (spam, spam.__call__.im_func, 1), + ) + for object, baseObject, dropSelf in values: + result = introspect.getBaseObject(object) + self.assertEqual(result, (baseObject, dropSelf)) + + +class GetAttributeTestCase(unittest.TestCase): + """Base class for other test case classes.""" + + def setUp(self): + self.values = ( + '__abs__', + '__add__', + '__and__', + '__base__', + '__bases__', + '__basicsize__', + '__builtins__', + '__call__', + '__class__', + '__cmp__', + '__coerce__', + '__contains__', + '__del__', + '__delattr__', + '__delitem__', + '__delslice__', + '__dict__', + '__dictoffset__', + '__div__', + '__divmod__', + '__doc__', + '__eq__', + '__file__', + '__flags__', + '__float__', + '__floordiv__', + '__ge__', + '__get__', + '__getattr__', + '__getattribute__', + '__getitem__', + '__getslice__', + '__gt__', + '__hash__', + '__hex__', + '__iadd__', + '__imul__', + '__init__', + '__int__', + '__invert__', + '__itemsize__', + '__iter__', + '__le__', + '__len__', + '__long__', + '__lshift__', + '__lt__', + '__mod__', + '__module__', + '__mro__', + '__mul__', + '__name__', + '__ne__', + '__neg__', + '__new__', + '__nonzero__', + '__oct__', + '__or__', + '__path__', + '__pos__', + '__pow__', + '__radd__', + '__rand__', + '__rdiv__', + '__rdivmod__', + '__reduce__', + '__repr__', + '__rfloordiv__', + '__rlshift__', + '__rmod__', + '__rmul__', + '__ror__', + '__rpow__', + '__rrshift__', + '__rshift__', + '__rsub__', + '__rtruediv__', + '__rxor__', + '__self__', + '__setattr__', + '__setitem__', + '__setslice__', + '__str__', + '__sub__', + '__subclasses__', + '__truediv__', + '__warningregistry__', + '__weakrefoffset__', + '__xor__', + 'append', + 'capitalize', + 'center', + 'clear', + 'close', + 'closed', + 'co_argcount', + 'co_cellvars', + 'co_code', + 'co_consts', + 'co_filename', + 'co_firstlineno', + 'co_flags', + 'co_freevars', + 'co_lnotab', + 'co_name', + 'co_names', + 'co_nlocals', + 'co_stacksize', + 'co_varnames', + 'conjugate', + 'copy', + 'count', + 'decode', + 'encode', + 'endswith', + 'expandtabs', + 'extend', + 'fileno', + 'find', + 'flush', + 'func_closure', + 'func_code', + 'func_defaults', + 'func_dict', + 'func_doc', + 'func_globals', + 'func_name', + 'get', + 'has_key', + 'im_class', + 'im_func', + 'im_self', + 'imag', + 'index', + 'insert', + 'isalnum', + 'isalpha', + 'isatty', + 'isdigit', + 'islower', + 'isspace', + 'istitle', + 'isupper', + 'items', + 'iteritems', + 'iterkeys', + 'itervalues', + 'join', + 'keys', + 'ljust', + 'lower', + 'lstrip', + 'mode', + 'mro', + 'name', + 'pop', + 'popitem', + 'real', + 'read', + 'readinto', + 'readline', + 'readlines', + 'remove', + 'replace', + 'reverse', + 'rfind', + 'rindex', + 'rjust', + 'rstrip', + 'seek', + 'setdefault', + 'softspace', + 'sort', + 'split', + 'splitlines', + 'start', + 'startswith', + 'step', + 'stop', + 'strip', + 'swapcase', + 'tell', + 'title', + 'tolist', + 'translate', + 'truncate', + 'update', + 'upper', + 'values', + 'write', + 'writelines', + 'xreadlines', + ) + +# Since getAllAttributeNames() calls str(object), +# we need to test for a broken __str__ method. +class BrokenStr: + def __str__(self): + raise Exception + +brokenStr = BrokenStr() + +class GetAttributeNamesTestCase(GetAttributeTestCase): + + def setUp(self): + GetAttributeTestCase.setUp(self) + import PyCrust + spam = Spam() + self.f = open('test_introspect.py') + self.items = ( + None, + int(123), + long(123), + float(123), + complex(123), + "", + unicode(""), + [], + (), + xrange(0), + {}, + # Builtin function. + len, + # Builtin method. + [].append, + # User function. + ham, + # Byte-compiled code. + ham.func_code, + # Lambda. + lambda: None, + # Class with no init. + Bar, + # Instance with no init. + Bar(), + # Class with init and del. + Foo, + # Instance with init and del. + Foo(), + # Bound method. + spam.foo, + # Unbound method. + Spam.eggs, + # Callable instance. + spam, + # Module. + introspect, + # Package. + PyCrust, + # Buffer. + buffer(''), + # File. + self.f, + # Slice. + slice(0), + # Ellipsis. + Ellipsis, + # BrokenStr class. + BrokenStr, + # BrokenStr instance. + brokenStr, + ) + + def tearDown(self): + self.items = None + self.f.close() + + def test_getAttributeNames(self): + for item in self.items: + self._checkAttributeNames(item) + if __builtins__.has_key('object'): + self._checkAttributeNames(object) + + def test_getAttributeNames_NoSingle(self): + for item in self.items: + result = introspect.getAttributeNames(item, includeSingle=0) + attributes = [attribute for attribute in result \ + if attribute[0] != '_' or attribute[:2] == '__'] + self.assertEqual(result, attributes, + ':item: %r' % (item,)) + + def test_getAttributeNames_NoDouble(self): + for item in self.items: + result = introspect.getAttributeNames(item, includeDouble=0) + attributes = [attribute for attribute in result \ + if attribute[:2] != '__'] + self.assertEqual(result, attributes, + ':item: %r' % (item,)) + + def test_getAttributeNames_NoSingleOrDouble(self): + for item in self.items: + result = introspect.getAttributeNames(item, includeSingle=0, + includeDouble=0) + attributes = [attribute for attribute in result \ + if attribute[0] != '_'] + self.assertEqual(result, attributes, + ':item: %r' % (item,)) + + def _checkAttributeNames(self, item): + result = introspect.getAttributeNames(item) + attributes = [attribute for attribute in self.values \ + if hasattr(item, attribute)] + for attribute in attributes: + self.assert_(attribute in result, + ':item: %r :attribute: %r' % (item, attribute)) + + +class GetAutoCompleteListTestCase(GetAttributeTestCase): + + def setUp(self): + GetAttributeTestCase.setUp(self) + self.items = ( + 'None.', + '123 .', + '"".', + '[].', + '().', + '{}.', + # Builtin function. + 'len.', + # Builtin method. + '[].append.', + ) + + def test_getAutoCompleteList(self): + for item in self.items: + result = introspect.getAutoCompleteList(item) + object = eval(item[:-1]) + attributes = [attribute for attribute in self.values \ + if hasattr(object, attribute)] + for attribute in attributes: + self.assert_(attribute in result, + ':item: %r :attribute: %r' % (item, attribute)) + + def test_getAutoCompleteList_NoSingle(self): + for item in self.items: + result = introspect.getAutoCompleteList(item, includeSingle=0) + attributes = [attribute for attribute in result \ + if attribute[0] != '_' or attribute[:2] == '__'] + self.assertEqual(result, attributes, + ':item: %r' % (item,)) + + def test_getAutoCompleteList_NoDouble(self): + for item in self.items: + result = introspect.getAutoCompleteList(item, includeDouble=0) + attributes = [attribute for attribute in result \ + if attribute[:2] != '__'] + self.assertEqual(result, attributes, + ':item: %r' % (item,)) + + def test_getAutoCompleteList_NoSingleOrDouble(self): + for item in self.items: + result = introspect.getAutoCompleteList(item, includeSingle=0, + includeDouble=0) + attributes = [attribute for attribute in result \ + if attribute[0] != '_'] + self.assertEqual(result, attributes, + ':item: %r' % (item,)) + + +# Support for GetConstructorTestCase. + +class A1: + def __init__(self, a): + self.a = a + +class B1(A1): + def __init__(self, b): + self.b = b + +class C1(A1): + pass + +class D1(C1, B1): + pass + +if __builtins__.has_key('object'): + class A2(object): + def __init__(self, a): + self.a = a + + class B2(A2): + def __init__(self, b): + self.b = b + + class C2(A2): + pass + + class D2(C2, B2): + pass + +class N: + pass + +class O: + def __init__(self, a, b=2, *args, **kwargs): + pass + +class P(O): + pass + +class Q(P): + def __init__(self, c, d=4): + pass + +class GetConstructorTestCase(unittest.TestCase): + + def test_getConstructor(self): + args = ('self', 'a', 'b', 'args', 'kwargs') + varnames = introspect.getConstructor(O).func_code.co_varnames + self.assertEqual(varnames, args) + varnames = introspect.getConstructor(P).func_code.co_varnames + self.assertEqual(varnames, args) + args = ('self', 'c', 'd') + varnames = introspect.getConstructor(Q).func_code.co_varnames + self.assertEqual(varnames, args) + + def test_getConstructor_None(self): + values = (N, 1, 'spam', {}, [], (), dir) + for value in values: + self.assertEqual(introspect.getConstructor(N), None) + + def test_getConstructor_MultipleInheritance(self): + # Test old style inheritance rules. + args = ('self', 'a') + varnames = introspect.getConstructor(D1).func_code.co_varnames + self.assertEqual(varnames, args) + if __builtins__.has_key('object'): + # Test new style inheritance rules as well. + args = ('self', 'b') + varnames = introspect.getConstructor(D2).func_code.co_varnames + self.assertEqual(varnames, args) + + +if __name__ == '__main__': + unittest.main() diff --git a/wxPython/wxPython/py/tests/test_pseudo.py b/wxPython/wxPython/py/tests/test_pseudo.py new file mode 100644 index 0000000000..b6064b283e --- /dev/null +++ b/wxPython/wxPython/py/tests/test_pseudo.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python + +__author__ = "Patrick K. O'Brien " +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import unittest + +# Import from this module's parent directory. +import os +import sys +sys.path.insert(0, os.pardir) +import pseudo +del sys.path[0] +del sys +del os + + +""" +These unittest methods are preferred: +------------------------------------- +self.assert_(expr, msg=None) +self.assertEqual(first, second, msg=None) +self.assertRaises(excClass, callableObj, *args, **kwargs) +self.fail(msg=None) +self.failIf(expr, msg=None) +""" + + +class ModuleTestCase(unittest.TestCase): + + def test_module(self): + module = pseudo + self.assert_(module.__author__) + self.assert_(module.__cvsid__) + self.assert_(module.__revision__) + self.assert_(module.PseudoFile) + self.assert_(module.PseudoFileErr) + self.assert_(module.PseudoFileIn) + self.assert_(module.PseudoFileOut) + self.assert_(module.PseudoKeyword) + + +class PseudoTestCase(unittest.TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + +class PseudoFileTestCase(unittest.TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + +class PseudoFileOutTestCase(unittest.TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + def _write(self): + pass + + def test_PseudoFileOut_goodInit(self): + self.assert_(pseudo.PseudoFileOut(write=self._write)) + + def test_PseudoFileOut_badInit(self): + self.assertRaises(ValueError, pseudo.PseudoFileOut, write='bad') + + +if __name__ == '__main__': + unittest.main() diff --git a/wxPython/wxPython/py/tests/test_version.py b/wxPython/wxPython/py/tests/test_version.py new file mode 100644 index 0000000000..9eb40fb7cd --- /dev/null +++ b/wxPython/wxPython/py/tests/test_version.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python + +__author__ = "Patrick K. O'Brien " +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + +import unittest + +import types + +# Import from this module's parent directory. +import os +import sys +sys.path.insert(0, os.pardir) +import version +del sys.path[0] +del sys +del os + + +""" +These unittest methods are preferred: +------------------------------------- +self.assert_(expr, msg=None) +self.assertEqual(first, second, msg=None) +self.assertRaises(excClass, callableObj, *args, **kwargs) +self.fail(msg=None) +self.failIf(expr, msg=None) +""" + + +class ModuleTestCase(unittest.TestCase): + + def test_module(self): + module = version + self.assert_(module.__author__) + self.assert_(module.__cvsid__) + self.assert_(module.__revision__) + self.assert_(module.VERSION) + + +class VersionTestCase(unittest.TestCase): + + def test_VERSION(self): + self.assert_(type(version.VERSION) is types.StringType) + + +if __name__ == '__main__': + unittest.main() diff --git a/wxPython/wxPython/py/tests/testall.py b/wxPython/wxPython/py/tests/testall.py new file mode 100644 index 0000000000..22669e777d --- /dev/null +++ b/wxPython/wxPython/py/tests/testall.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +__author__ = "Patrick K. O'Brien " +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + + +import unittest +import glob +import os + + +def suite(): + """Return a test suite containing all test cases in all test modules. + Searches the current directory for any modules matching test_*.py.""" + suite = unittest.TestSuite() + for filename in glob.glob('test_*.py'): + module = __import__(os.path.splitext(filename)[0]) + suite.addTest(unittest.defaultTestLoader.loadTestsFromModule(module)) + return suite + + +if __name__ == '__main__': + unittest.main(defaultTest='suite') + diff --git a/wxPython/wxPython/lib/PyCrust/version.py b/wxPython/wxPython/py/version.py similarity index 94% rename from wxPython/wxPython/lib/PyCrust/version.py rename to wxPython/wxPython/py/version.py index 3950c4a402..01cb2f291f 100644 --- a/wxPython/wxPython/lib/PyCrust/version.py +++ b/wxPython/wxPython/py/version.py @@ -7,5 +7,5 @@ __author__ = "Patrick K. O'Brien " __cvsid__ = "$Id$" __revision__ = "$Revision$"[11:-2] -VERSION = '0.9' +VERSION = '0.9.1' diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Accelerators.py b/wxPython/wxPython/py/wxd/Accelerators.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/Accelerators.py rename to wxPython/wxPython/py/wxd/Accelerators.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/App.py b/wxPython/wxPython/py/wxd/App.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/App.py rename to wxPython/wxPython/py/wxd/App.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Base.py b/wxPython/wxPython/py/wxd/Base.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/Base.py rename to wxPython/wxPython/py/wxd/Base.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/ClipDragDrop.py b/wxPython/wxPython/py/wxd/ClipDragDrop.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/ClipDragDrop.py rename to wxPython/wxPython/py/wxd/ClipDragDrop.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Config.py b/wxPython/wxPython/py/wxd/Config.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/Config.py rename to wxPython/wxPython/py/wxd/Config.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Controls.py b/wxPython/wxPython/py/wxd/Controls.py similarity index 98% rename from wxPython/wxPython/lib/PyCrust/wxd/Controls.py rename to wxPython/wxPython/py/wxd/Controls.py index 1d1fad0210..f2870fa5c2 100644 --- a/wxPython/wxPython/lib/PyCrust/wxd/Controls.py +++ b/wxPython/wxPython/py/wxd/Controls.py @@ -1126,11 +1126,11 @@ class Notebook(Control): """""" pass - def AddPage(self, pPage, strText, bSelect=False, imageId=-1): + def AddPage(self, page, text, select=False, imageId=-1): """""" pass - def AdvanceSelection(self, bForward=True): + def AdvanceSelection(self, forward=True): """""" pass @@ -1147,7 +1147,7 @@ class Notebook(Control): """""" pass - def DeletePage(self, nPage): + def DeletePage(self, page): """""" pass @@ -1155,7 +1155,7 @@ class Notebook(Control): """""" pass - def GetPage(self, nPage): + def GetPage(self, page): """""" pass @@ -1163,11 +1163,11 @@ class Notebook(Control): """""" pass - def GetPageImage(self, nPage): + def GetPageImage(self, page): """""" pass - def GetPageText(self, nPage): + def GetPageText(self, page): """""" pass @@ -1179,11 +1179,11 @@ class Notebook(Control): """""" pass - def InsertPage(self, nPage, pPage, strText, bSelect=False, imageId=-1): + def InsertPage(self, index, page, text, select=False, imageId=-1): """""" pass - def RemovePage(self, nPage): + def RemovePage(self, page): """""" pass @@ -1199,7 +1199,7 @@ class Notebook(Control): """""" pass - def SetPageImage(self, nPage, nImage): + def SetPageImage(self, page, image): """""" pass @@ -1207,11 +1207,11 @@ class Notebook(Control): """""" pass - def SetPageText(self, nPage, strText): + def SetPageText(self, page, text): """""" pass - def SetSelection(self, nPage): + def SetSelection(self, page): """""" pass diff --git a/wxPython/wxPython/lib/PyCrust/wxd/DataStructures.py b/wxPython/wxPython/py/wxd/DataStructures.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/DataStructures.py rename to wxPython/wxPython/py/wxd/DataStructures.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/DateTime.py b/wxPython/wxPython/py/wxd/DateTime.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/DateTime.py rename to wxPython/wxPython/py/wxd/DateTime.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Dialogs.py b/wxPython/wxPython/py/wxd/Dialogs.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/Dialogs.py rename to wxPython/wxPython/py/wxd/Dialogs.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Drawing.py b/wxPython/wxPython/py/wxd/Drawing.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/Drawing.py rename to wxPython/wxPython/py/wxd/Drawing.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Errors.py b/wxPython/wxPython/py/wxd/Errors.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/Errors.py rename to wxPython/wxPython/py/wxd/Errors.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/EventFunctions.py b/wxPython/wxPython/py/wxd/EventFunctions.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/EventFunctions.py rename to wxPython/wxPython/py/wxd/EventFunctions.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Events.py b/wxPython/wxPython/py/wxd/Events.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/Events.py rename to wxPython/wxPython/py/wxd/Events.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/FileSystem.py b/wxPython/wxPython/py/wxd/FileSystem.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/FileSystem.py rename to wxPython/wxPython/py/wxd/FileSystem.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Frames.py b/wxPython/wxPython/py/wxd/Frames.py similarity index 67% rename from wxPython/wxPython/lib/PyCrust/wxd/Frames.py rename to wxPython/wxPython/py/wxd/Frames.py index 94c0e72074..49b5b21306 100644 --- a/wxPython/wxPython/lib/PyCrust/wxd/Frames.py +++ b/wxPython/wxPython/py/wxd/Frames.py @@ -37,9 +37,9 @@ class Frame(TopLevelWindow): name=wx.PyFrameNameStr): """Create a Frame instance. - parent - The window parent. This may be NULL. If it is - non-NULL, the frame will always be displayed on top of the - parent window on Windows. + parent - The window parent. This may be None. If it is not + None, the frame will always be displayed on top of the parent + window on Windows. id - The window identifier. It may take a value of -1 to indicate a default value. @@ -93,8 +93,8 @@ class Frame(TopLevelWindow): By default, the status bar is an instance of wx.StatusBar.""" pass - def CreateToolBar(self, style=wx.NO_BORDER|wx.TB_HORIZONTAL, id=-1, - name=wx.PyToolBarNameStr): + def CreateToolBar(self, style=wx.NO_BORDER | wx.TB_HORIZONTAL, + id=-1, name=wx.PyToolBarNameStr): """Create a toolbar at the top or left of frame. style - The toolbar style. See wxToolBar for a list of valid @@ -141,8 +141,7 @@ class Frame(TopLevelWindow): pass def GetStatusBarPane(self): - """Return status bar pane used to display menu and toolbar - help.""" + """Return status bar pane used to display menu/toolbar help.""" pass def GetToolBar(self): @@ -237,22 +236,102 @@ class Frame(TopLevelWindow): class LayoutAlgorithm(Object): - """""" + """LayoutAlgorithm implements layout of subwindows in MDI or SDI + frames. It sends a wx.CalculateLayoutEvent event to children of + the frame, asking them for information about their size. For MDI + parent frames, the algorithm allocates the remaining space to the + MDI client window (which contains the MDI child frames). For SDI + (normal) frames, a 'main' window is specified as taking up the + remaining space. + + Because the event system is used, this technique can be applied to + any windows, which are not necessarily 'aware' of the layout + classes. However, you may wish to use wx.SashLayoutWindow for + your subwindows since this class provides handlers for the + required events, and accessors to specify the desired size of the + window. The sash behaviour in the base class can be used, + optionally, to make the windows user-resizable. + + LayoutAlgorithm is typically used in IDE (integrated development + environment) applications, where there are several resizable + windows in addition to the MDI client window, or other primary + editing window. Resizable windows might include toolbars, a + project window, and a window for displaying error and warning + messages. + + When a window receives an OnCalculateLayout event, it should call + SetRect in the given event object, to be the old supplied + rectangle minus whatever space the window takes up. It should + also set its own size accordingly. + SashLayoutWindow.OnCalculateLayout generates an OnQueryLayoutInfo + event which it sends to itself to determine the orientation, + alignment and size of the window, which it gets from internal + member variables set by the application. + + The algorithm works by starting off with a rectangle equal to the + whole frame client area. It iterates through the frame children, + generating OnCalculateLayout events which subtract the window size + and return the remaining rectangle for the next window to process. + It is assumed (by SashLayoutWindow.OnCalculateLayout) that a + window stretches the full dimension of the frame client, according + to the orientation it specifies. For example, a horizontal window + will stretch the full width of the remaining portion of the frame + client area. In the other orientation, the window will be fixed + to whatever size was specified by OnQueryLayoutInfo. An alignment + setting will make the window 'stick' to the left, top, right or + bottom of the remaining client area. This scheme implies that + order of window creation is important. Say you wish to have an + extra toolbar at the top of the frame, a project window to the + left of the MDI client window, and an output window above the + status bar. You should therefore create the windows in this + order: toolbar, output window, project window. This ensures that + the toolbar and output window take up space at the top and bottom, + and then the remaining height in-between is used for the project + window. + + LayoutAlgorithm is quite independent of the way in which + OnCalculateLayout chooses to interpret a window's size and + alignment. Therefore you could implement a different window class + with a new OnCalculateLayout event handler, that has a more + sophisticated way of laying out the windows. It might allow + specification of whether stretching occurs in the specified + orientation, for example, rather than always assuming + stretching. (This could, and probably should, be added to the + existing implementation). + + The algorithm object does not respond to events, but itself + generates the following events in order to calculate window sizes: + EVT_QUERY_LAYOUT_INFO(func), EVT_CALCULATE_LAYOUT(func).""" def __init__(self): - """""" + """Create a LayoutAlgorithm instance.""" pass - def LayoutFrame(self): - """""" + def LayoutFrame(self, frame, mainWindow=wx.NULL): + """Lay out the children of a normal frame. + + mainWindow is set to occupy the remaining space. This + function simply calls LayoutWindow().""" pass - def LayoutMDIFrame(self): - """""" + def LayoutMDIFrame(self, frame, rect=wx.NULL): + """Lay out the children of an MDI parent frame. + + If rect is non-NULL, the given rectangle will be used as a + starting point instead of the frame's client area. + + The MDI client window is set to occupy the remaining space.""" pass - def LayoutWindow(self): - """""" + def LayoutWindow(self, parent, mainWindow=wx.NULL): + """Lay out the children of a normal frame or other window. + + mainWindow is set to occupy the remaining space. If this is + not specified, then the last window that responds to a + calculate layout event in query mode will get the remaining + space (that is, a non-query OnCalculateLayout event will not + be sent to this window and the window will be set to the + remaining size).""" pass diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Functions.py b/wxPython/wxPython/py/wxd/Functions.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/Functions.py rename to wxPython/wxPython/py/wxd/Functions.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Help.py b/wxPython/wxPython/py/wxd/Help.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/Help.py rename to wxPython/wxPython/py/wxd/Help.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/ImageHandlers.py b/wxPython/wxPython/py/wxd/ImageHandlers.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/ImageHandlers.py rename to wxPython/wxPython/py/wxd/ImageHandlers.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Joystick.py b/wxPython/wxPython/py/wxd/Joystick.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/Joystick.py rename to wxPython/wxPython/py/wxd/Joystick.py diff --git a/wxPython/wxPython/py/wxd/LICENSE.txt b/wxPython/wxPython/py/wxd/LICENSE.txt new file mode 100644 index 0000000000..5bfa143812 --- /dev/null +++ b/wxPython/wxPython/py/wxd/LICENSE.txt @@ -0,0 +1,60 @@ + wxWindows Free Documentation Licence, Version 3 + =============================================== + + Copyright (c) 1998 Julian Smart, Robert Roebling et al + + Everyone is permitted to copy and distribute verbatim copies + of this licence document, but changing it is not allowed. + + WXWINDOWS FREE DOCUMENTATION LICENCE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 1. Permission is granted to make and distribute verbatim copies of this + manual or piece of documentation provided any copyright notice and this + permission notice are preserved on all copies. + + 2. Permission is granted to process this file or document through a + document processing system and, at your option and the option of any third + party, print the results, provided a printed document carries a copying + permission notice identical to this one. + + 3. Permission is granted to copy and distribute modified versions of this + manual or piece of documentation under the conditions for verbatim + copying, provided also that any sections describing licensing conditions + for this manual, such as, in particular, the GNU General Public Licence, + the GNU Library General Public Licence, and any wxWindows Licence are + included exactly as in the original, and provided that the entire + resulting derived work is distributed under the terms of a permission + notice identical to this one. + + 4. Permission is granted to copy and distribute translations of this + manual or piece of documentation into another language, under the above + conditions for modified versions, except that sections related to + licensing, including this paragraph, may also be included in translations + approved by the copyright holders of the respective licence documents in + addition to the original English. + + WARRANTY DISCLAIMER + + 5. BECAUSE THIS MANUAL OR PIECE OF DOCUMENTATION IS LICENSED FREE OF CHARGE, + THERE IS NO WARRANTY FOR IT, TO THE EXTENT PERMITTED BY APPLICABLE LAW. + EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER + PARTIES PROVIDE THIS MANUAL OR PIECE OF DOCUMENTATION "AS IS" WITHOUT + WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF + THE MANUAL OR PIECE OF DOCUMENTATION IS WITH YOU. SHOULD THE MANUAL OR + PIECE OF DOCUMENTATION PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL + NECESSARY SERVICING, REPAIR OR CORRECTION. + + 6. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL + ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR + REDISTRIBUTE THE MANUAL OR PIECE OF DOCUMENTATION AS PERMITTED ABOVE, BE + LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR + CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE + MANUAL OR PIECE OF DOCUMENTATION (INCLUDING BUT NOT LIMITED TO LOSS OF + DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD + PARTIES OR A FAILURE OF A PROGRAM BASED ON THE MANUAL OR PIECE OF + DOCUMENTATION TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR + OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + diff --git a/wxPython/wxPython/lib/PyCrust/wxd/LayoutConstraints.py b/wxPython/wxPython/py/wxd/LayoutConstraints.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/LayoutConstraints.py rename to wxPython/wxPython/py/wxd/LayoutConstraints.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Logging.py b/wxPython/wxPython/py/wxd/Logging.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/Logging.py rename to wxPython/wxPython/py/wxd/Logging.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Menus.py b/wxPython/wxPython/py/wxd/Menus.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/Menus.py rename to wxPython/wxPython/py/wxd/Menus.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/MimeTypes.py b/wxPython/wxPython/py/wxd/MimeTypes.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/MimeTypes.py rename to wxPython/wxPython/py/wxd/MimeTypes.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Misc.py b/wxPython/wxPython/py/wxd/Misc.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/Misc.py rename to wxPython/wxPython/py/wxd/Misc.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Panel.py b/wxPython/wxPython/py/wxd/Panel.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/Panel.py rename to wxPython/wxPython/py/wxd/Panel.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Parameters.py b/wxPython/wxPython/py/wxd/Parameters.py similarity index 98% rename from wxPython/wxPython/lib/PyCrust/wxd/Parameters.py rename to wxPython/wxPython/py/wxd/Parameters.py index 2bc3c79592..60db3e157a 100644 --- a/wxPython/wxPython/lib/PyCrust/wxd/Parameters.py +++ b/wxPython/wxPython/py/wxd/Parameters.py @@ -36,6 +36,7 @@ _params = ( 'DefaultValidator', 'EmptyString', 'EVT_NULL', + 'HORIZONTAL', 'HSCROLL', 'NO_BORDER', 'NULL', diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Printing.py b/wxPython/wxPython/py/wxd/Printing.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/Printing.py rename to wxPython/wxPython/py/wxd/Printing.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Process.py b/wxPython/wxPython/py/wxd/Process.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/Process.py rename to wxPython/wxPython/py/wxd/Process.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/SashSplitter.py b/wxPython/wxPython/py/wxd/SashSplitter.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/SashSplitter.py rename to wxPython/wxPython/py/wxd/SashSplitter.py diff --git a/wxPython/wxPython/py/wxd/Sizers.py b/wxPython/wxPython/py/wxd/Sizers.py new file mode 100644 index 0000000000..918a80b7c2 --- /dev/null +++ b/wxPython/wxPython/py/wxd/Sizers.py @@ -0,0 +1,605 @@ +"""Decorator classes for documentation and shell scripting. + +Sizer is the abstract base class used for laying out subwindows in a +window. You cannot use Sizer directly; instead, you will have to use +one of the sizer classes derived from it. Currently there are +BoxSizer, StaticBoxSizer, NotebookSizer, GridSizer, and FlexGridSizer. + +The layout algorithm used by sizers in wxPython is closely related to +layout in other GUI toolkits, such as Java's AWT, the GTK toolkit or +the Qt toolkit. It is based upon the idea of the individual +subwindows reporting their minimal required size and their ability to +get stretched if the size of the parent window has changed. This will +most often mean, that the programmer does not set the original size of +a dialog in the beginning, rather the dialog will assigned a sizer and +this sizer will be queried about the recommended size. The sizer in +turn will query its children, which can be normal windows, empty space +or other sizers, so that a hierarchy of sizers can be constructed. +Note that wxSizer does not derive from wxWindow and thus do not +interfere with tab ordering and requires very little resources +compared to a real window on screen. + +What makes sizers so well fitted for use in wxPython is the fact that +every control reports its own minimal size and the algorithm can +handle differences in font sizes or different window (dialog item) +sizes on different platforms without problems. If e.g. the standard +font as well as the overall design of Motif widgets requires more +space than on Windows, the initial dialog size will automatically be +bigger on Motif than on Windows. + +If you wish to create a sizer class in wxPython you should derive the +class from PySizer in order to get Python-aware capabilities for the +various virtual methods. +""" + +__author__ = "Patrick K. O'Brien " +__cvsid__ = "$Id$" +__revision__ = "$Revision$"[11:-2] + + +# These are not the real wxPython classes. These are Python versions +# for documentation purposes. They are also used to apply docstrings +# to the real wxPython classes, which are SWIG-generated wrappers for +# C-language classes. + + +from Base import Object +import Parameters as wx + +try: + True +except NameError: + True = 1==1 + False = 1==0 + + +class Sizer(Object): + """Sizer is the abstract base class used for laying out subwindows + in a window. You shouldn't use Sizer directly; instead, you should + use one of the sizer classes derived from it. + + If you wish to create a sizer class in wxPython you should derive + the class from PySizer in order to get Python-aware capabilities + for the various virtual methods. + + Placing a child sizer in a sizer allows you to create hierarchies + of sizers (typically a vertical box as the top sizer and several + horizontal boxes on the level beneath). + + When you place a window in a sizer the window's initial size + (either set explicitly by the user or calculated internally when + using wxDefaultSize) is interpreted as the minimal and in many + cases also the initial size. This is particularly useful in + connection with SetSizeHints. + + Adding spacers to sizers gives more flexibility in the design of + dialogs. Imagine for example a horizontal box with two buttons at + the bottom of a dialog: you might want to insert a space between + the two buttons and make that space stretchable using the + proportion flag and the result will be that the left button will + be aligned with the left side of the dialog and the right button + with the right side - the space in between will shrink and grow + with the dialog. + + Several methods (Add, Insert, Prepend) take the following + parameters: + + proportion - Used only by BoxSizer to indicate if a child of a + sizer can change its size in the main orientation of the BoxSizer, + where 0 stands for not changeable and a value of more than zero is + interpreted relative to the value of other children of the same + BoxSizer. For example, you might have a horizontal BoxSizer with + three children, two of which are supposed to change their size + with the sizer. Then the two stretchable windows would each get a + value of 1 to make them grow and shrink equally with the sizer's + horizontal dimension. + + flag - This parameter can be used to set a number of flags which + can be combined using the binary OR operator |. Two main + behaviours are defined using these flags. One is the border + around a window: the border parameter determines the border width + whereas the flags given here determine where the border may be + (wx.TOP, wx.BOTTOM, wx.LEFT, wx.RIGHT or wx.ALL). The other flags + determine the child window's behaviour if the size of the sizer + changes. However this is not - in contrast to the proportion flag + - in the main orientation, but in the respectively other + orientation. So if you created a BoxSizer with the wx.VERTICAL + option, these flags will be relevant if the sizer changes its + horizontal size. A child may get resized to completely fill out + the new size (using either wx.GROW or wx.EXPAND), it may get + proportionally resized (wx.SHAPED), it may get centered + (wx.ALIGN_CENTER or wx.ALIGN_CENTRE) or it may get aligned to + either side (wx.ALIGN_LEFT and wx.ALIGN_TOP are set to 0 and thus + represent the default, wx.ALIGN_RIGHT and wx.ALIGN_BOTTOM have + their obvious meaning). With proportional resize, a child may + also be centered in the main orientation using + wx.ALIGN_CENTER_VERTICAL (same as wx.ALIGN_CENTRE_VERTICAL) and + wx.ALIGN_CENTER_HORIZONTAL (same as wx.ALIGN_CENTRE_HORIZONTAL) + flags. Finally, you can also specify wx.ADJUST_MINSIZE flag to + make the minimal size of the control dynamically adjust to the + value returned by its GetAdjustedBestSize() method - this allows, + for example, for correct relayouting of a static text control even + if its text is changed during run-time. + + border - Determines the border width, if the flag parameter is set + to any border. A border is not a visible element, but rather a + margin of empty space surrounding the item. + + userData - Allows an extra object to be attached to the sizer + item, for use in derived classes when sizing information is more + complex than the option and flag parameters will allow.""" + + def __init__(self): + """Must be defined by subclasses.""" + pass + + def Add(self, item, proportion=0, flag=0, border=0, + userData=wx.NULL): + """Add item to sizer. + + item - window, sizer, or spacer. Spacer is specified with a + (width, height) tuple or wx.Size representing the spacer size. + + Call Layout() to update the layout on-screen after adding.""" + pass + + def Clear(self, delete_windows=False): + """Remove all items from this sizer. + + If delete_windows is True, destroy any window items.""" + pass + + def DeleteWindows(self): + """Destroy windows associated with this sizer.""" + pass + + def Destroy(self): + """Destroy the sizer.""" + pass + + def Fit(self, window): + """Resize window to match sizer's minimal size; return size. + + This is commonly done in the constructor of the window itself.""" + pass + + def FitInside(self, window): + """Resize window virtual size to match sizer's minimal size. + + This will not alter the on screen size of the window, but may + cause the addition/removal/alteration of scrollbars required + to view the virtual area in windows which manage it.""" + pass + + def GetChildren(self): + """Return list of SizerItem instances.""" + pass + + def GetMinSize(self): + """Return the minimal size of the sizer. + + This is either the combined minimal size of all the children + and their borders or the minimal size set by SetMinSize, + whichever is larger.""" + pass + + def GetMinSizeTuple(self): + """Return the minimal size of the sizer as a tuple. + + This is either the combined minimal size of all the children + and their borders or the minimal size set by SetMinSize, + whichever is larger.""" + pass + + def GetPosition(self): + """Return the current position of the sizer.""" + pass + + def GetPositionTuple(self): + """Return the current position of the sizer as a tuple.""" + pass + + def GetSize(self): + """Return the current size of the sizer.""" + pass + + def GetSizeTuple(self): + """Return the current size of the sizer as a tuple.""" + pass + + def Hide(self, item): + """Hide item (sizer or window). To make a sizer item + disappear on-screen, use Hide() followed by Layout().""" + pass + + def Insert(self, before, item, proportion=0, flag=0, border=0, + userData=wx.NULL): + """Same as Add, but inserts item into list of items (windows, + subsizers or spacers) owned by this sizer. + + Call Layout() to update the layout on-screen after inserting.""" + pass + + def IsShown(self, item): + """Return True if item (sizer or window) is shown.""" + pass + + def Layout(self): + """Force layout of children anew. + + Use after adding or removing a child (window, other sizer, or + spacer) from the sizer while keeping the current dimension.""" + pass + + def Prepend(self, item, proportion=0, flag=0, border=0, + userData=wx.NULL): + """Same as Add, but prepends item to beginning of list of + items (windows, subsizers or spacers) owned by this sizer. + + Call Layout() to update the layout on-screen after prepending.""" + pass + + def Remove(self, item): + """Remove item from the sizer. + + item - sizer, window, or index of item in the sizer, typically + 0 for the first item. + + Does not cause any layout or resizing to take place, and does + not delete the child itself. Call Layout() to update the + layout on-screen after removing child. + + Return True if child found and removed, False otherwise.""" + pass + + def SetDimension(self, x, y, width, height): + """Force sizer to take the given dimension and thus force + items owned by sizer to resize themselves according to the + rules defined by the parameter in the Add and Prepend methods.""" + pass + + def SetItemMinSize(self, item, width, height): + """Set minimal size of item. + + item - sizer, window, or index of item in the sizer, typically + 0 for the first item. + + The item will be found recursively in the sizer's descendants. + Enables application to set size of item after initialization.""" + pass + + def SetMinSize(self, size): + """Set minimal size. + + Normally, sizer will calculate minimal size based on how much + space its children need. After calling this method, + GetMinSize will return the minimal size as requested by its + children or the minimal size set here, whichever is larger.""" + pass + + def SetSizeHints(self, window): + """Set (and Fit) minimal size of window to match sizer's + minimal size. Commonly called in the window's init.""" + pass + + def SetVirtualSizeHints(self, window): + """Set minimal size of window virtual area to match sizer's + minimal size. For windows with managed scrollbars this will + set them appropriately.""" + pass + + def Show(self, item, show=True): + """Show or hide item (sizer or window). To make item + disappear or reappear on-screen, use Show() followed by + Layout().""" + pass + + def ShowItems(self, show): + """Recursively call Show() on all sizer items.""" + pass + + +class PySizer(Sizer): + """If you wish to create a custom sizer class you should derive + the class from PySizer in order to get Python-aware capabilities + for the various virtual methods.""" + + def __init__(self): + """Create a PySizer instance. Override in subclass.""" + pass + + +class BoxSizer(Sizer): + """A box sizer is used to lay out a rather simple geometry, + typically a row or column or several hierarchies of either.""" + + def __init__(self, orient=wx.HORIZONTAL): + """Create BoxSizer instance. + + orient is either wx.VERTICAL or wx.HORIZONTAL""" + pass + + def CalcMin(self): + """Calculate minimum size. Do not call directly.""" + pass + + def GetOrientation(self): + """Return orientation: wx.VERTICAL or wx.HORIZONTAL.""" + pass + + def RecalcSizes(self): + """Recalculate sizes, then set the size of its children + (calling SetSize if child is a window). Do not call directly.""" + pass + + def SetOrientation(self, orient): + """Set orientation to either wx.VERTICAL or wx.HORIZONTAL.""" + pass + + +class StaticBoxSizer(BoxSizer): + """Like BoxSizer, but adds a static box around the sizer. Note + that the static box has to be created separately.""" + + def __init__(self, box, orient=wx.HORIZONTAL): + """Create StaticBoxSizer instance. + + box - instance of wx.StaticBox + + orient - either wx.VERTICAL or wx.HORIZONTAL""" + pass + + def CalcMin(self): + """Calculate minimum size. Do not call directly.""" + pass + + def GetStaticBox(self): + """Return the static box associated with the sizer.""" + pass + + def RecalcSizes(self): + """Recalculate sizes, then set the size of its children + (calling SetSize if child is a window). Do not call directly.""" + pass + + +class GridSizer(Sizer): + """A grid sizer lays out its children in a two-dimensional table + where all cells have the same size: the width of each cell is the + width of the widest child, the height of each cell is the height + of the tallest child. See also the FlexGridSizer.""" + + def __init__(self, rows=1, cols=0, vgap=0, hgap=0): + """Create a GridSizer instance. + + rows and cols - the number of rows and columns in the grid; if + either is zero, it will be calculated as the number of + children in the sizer, allowing the sizer grow dynamically. + + vgap and hgap - extra space between all cells, in pixels.""" + pass + + def CalcMin(self): + """Calculate minimum size. Do not call directly.""" + pass + + def GetCols(self): + """Return the number of columns in the grid.""" + pass + + def GetHGap(self): + """Return the horizontal gap (in pixels) between cells.""" + pass + + def GetRows(self): + """Return the number of rows in the grid.""" + pass + + def GetVGap(self): + """Return the vertical gap (in pixels) between cells.""" + pass + + def RecalcSizes(self): + """Recalculate sizes, then set the size of its children + (calling SetSize if child is a window). Do not call directly.""" + pass + + def SetCols(self, cols): + """Set the number of columns in the grid.""" + pass + + def SetHGap(self, gap): + """Set the horizontal gap (in pixels) between cells.""" + pass + + def SetRows(self, rows): + """Sets the number of rows in the grid.""" + pass + + def SetVGap(self, gap): + """Set the vertical gap (in pixels) between cells.""" + pass + + +class FlexGridSizer(GridSizer): + """A flex grid sizer lays out its children in a two-dimensional + table where all cells in one row have the same height and all + cells in one column have the same width, but all cells are not + necessarily the same height and width, as in the GridSizer.""" + + def __init__(self, rows=1, cols=0, vgap=0, hgap=0): + """Create a GridSizer instance. + + rows and cols - the number of rows and columns in the grid; if + either is zero, it will be calculated as the number of + children in the sizer, allowing the sizer grow dynamically. + + vgap and hgap - extra space between all cells, in pixels.""" + pass + + def AddGrowableCol(self, idx): + """Specify that column idx (starting from zero) should expand + if there is extra space available to the sizer.""" + pass + + def AddGrowableRow(self, idx): + """Specify that row idx (starting from zero) should expand if + there is extra space available to the sizer.""" + pass + + def CalcMin(self): + """Calculate minimum size. Do not call directly.""" + pass + + def RecalcSizes(self): + """Recalculate sizes, then set the size of its children + (calling SetSize if child is a window). Do not call directly.""" + pass + + def RemoveGrowableCol(self, idx): + """Specify that column idx is no longer growable.""" + pass + + def RemoveGrowableRow(self, idx): + """Specify that row idx is no longer growable.""" + pass + + +class NotebookSizer(Sizer): + """NotebookSizer works with a notebook to determine the size of + the biggest page and report an adjusted minimal size to a more + toplevel sizer. Do not add children to a NotebookSizer.""" + + def __init__(self, nb): + """Create a NotebookSizer instance for notebook.""" + pass + + def CalcMin(self): + """Calculate minimum size. Do not call directly.""" + pass + + def GetNotebook(self): + """Return the notebook associated with the sizer.""" + pass + + def RecalcSizes(self): + """Recalculate size. Do not call directly.""" + pass + + +class SizerItem(Object): + """SizerItem class. Wrapper for items managed by a sizer.""" + + def __init__(self, this): + """Create a SizerItem instance. You don't normally create one + directly.""" + pass + + def CalcMin(self): + """Calculate minimum size. Do not call directly.""" + pass + + def DeleteWindows(self): + """Recursively destroy windows associated with this SizerItem.""" + pass + + def GetBorder(self): + """Return border width.""" + pass + + def GetFlag(self): + """Return flag value.""" + pass + + def GetOption(self): + """Return option value.""" + pass + + def GetPosition(self): + """Return wx.Point instance representing position relative to + the client area.""" + pass + + def GetRatio(self): + """Return a floating point aspect ratio (width/height). If + wx.SHAPED flag is used item will maintain ratio when resized.""" + pass + + def GetSize(self): + """Return wx.Size instance with size.""" + pass + + def GetSizer(self): + """If IsSizer() return the sizer; otherwise return None.""" + pass + + def GetUserData(self): + """Return a wx.PyUserData object.""" + pass + + def GetWindow(self): + """If IsWindow() return the window; otherwise return None.""" + pass + + def IsShown(self): + """Return True if item is shown.""" + pass + + def IsSizer(self): + """Return True if SizerItem represents a sizer.""" + pass + + def IsSpacer(self): + """Return True if SizerItem represents a spacer.""" + pass + + def IsWindow(self): + """Return True if SizerItem represents a window.""" + pass + + def SetBorder(self, border): + """Set border width for item.""" + pass + + def SetDimension(self, pos, size): + """Set position and size for item.""" + pass + + def SetFlag(self, flag): + """Set flag for item.""" + pass + + def SetInitSize(self, x, y): + """Set initial size of item.""" + pass + + def SetOption(self, option): + """Set option for item.""" + pass + + def SetRatio(self, ratio): + """Set a floating point aspect ratio (width/height). If + wx.SHAPED flag is used item will maintain ratio when resized.""" + pass + + def SetRatioSize(self, size): + """Set a floating point aspect ratio (width/height). If + wx.SHAPED flag is used item will maintain ratio when resized.""" + pass + + def SetRatioWH(self, width, height): + """Set a floating point aspect ratio (width/height). If + wx.SHAPED flag is used item will maintain ratio when resized.""" + pass + + def SetSizer(self, sizer): + """Set sizer associated with SizerItem.""" + pass + + def SetWindow(self, window): + """Set window associated with SizerItem.""" + pass + + def Show(self, show): + """Is show is True, show item, otherwise hide item.""" + pass diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Streams.py b/wxPython/wxPython/py/wxd/Streams.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/Streams.py rename to wxPython/wxPython/py/wxd/Streams.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/StyledTextConstants.py b/wxPython/wxPython/py/wxd/StyledTextConstants.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/StyledTextConstants.py rename to wxPython/wxPython/py/wxd/StyledTextConstants.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Threading.py b/wxPython/wxPython/py/wxd/Threading.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/Threading.py rename to wxPython/wxPython/py/wxd/Threading.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/ToolBar.py b/wxPython/wxPython/py/wxd/ToolBar.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/ToolBar.py rename to wxPython/wxPython/py/wxd/ToolBar.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Tree.py b/wxPython/wxPython/py/wxd/Tree.py similarity index 82% rename from wxPython/wxPython/lib/PyCrust/wxd/Tree.py rename to wxPython/wxPython/py/wxd/Tree.py index 46ae981f33..9b71efd20e 100644 --- a/wxPython/wxPython/lib/PyCrust/wxd/Tree.py +++ b/wxPython/wxPython/py/wxd/Tree.py @@ -18,7 +18,10 @@ import Parameters as wx class TreeCtrl(Control): - """""" + """A tree control presents information as a hierarchy, with items + that may be expanded to show further items. Items in a tree + control are referenced by wx.TreeItemId handles, which may be + tested for validity by calling TreeItemId.IsOk().""" def AddRoot(self): """""" @@ -354,49 +357,47 @@ class TreeItemAttr: class TreeItemData(Object): - """""" + """TreeItemData is some (arbitrary) user class associated with + some item. The main advantage of having this class is that + TreeItemData objects are destroyed automatically by the tree and + the memory and any other resources associated with a tree item + will be automatically freed when it is deleted.""" + + def __init__(self, obj=wx.NULL): + """Associate any Python object with tree item using + wxTreeItemData as container.""" + pass def GetData(self): - """""" + """Return the Python object.""" pass def GetId(self): - """""" + """Return the item associated with this node.""" pass - def SetData(self): - """""" + def SetData(self, obj): + """Associate Python object with tree item.""" pass - def SetId(self): - """""" - pass - - def __init__(self): - """""" + def SetId(self, id): + """Set the item associated with this node.""" pass class TreeItemId: - """""" + """Item in a TreeCtrl.""" + +## You wouldn't create these directly. + +## def __init__(self): +## """""" +## pass def IsOk(self): - """""" + """Return True if item is valid.""" pass def Ok(self): - """""" + """Synonym for IsOk.""" pass - - def __cmp__(self): - """""" - pass - - def __del__(self): - """""" - pass - - def __init__(self): - """""" - pass - diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Validators.py b/wxPython/wxPython/py/wxd/Validators.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/Validators.py rename to wxPython/wxPython/py/wxd/Validators.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/Window.py b/wxPython/wxPython/py/wxd/Window.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/Window.py rename to wxPython/wxPython/py/wxd/Window.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/__init__.py b/wxPython/wxPython/py/wxd/__init__.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/__init__.py rename to wxPython/wxPython/py/wxd/__init__.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/d_stc.py b/wxPython/wxPython/py/wxd/d_stc.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/d_stc.py rename to wxPython/wxPython/py/wxd/d_stc.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/d_wx.py b/wxPython/wxPython/py/wxd/d_wx.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/d_wx.py rename to wxPython/wxPython/py/wxd/d_wx.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/decorator.py b/wxPython/wxPython/py/wxd/decorator.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/decorator.py rename to wxPython/wxPython/py/wxd/decorator.py diff --git a/wxPython/wxPython/py/wxd/default.css b/wxPython/wxPython/py/wxd/default.css new file mode 100644 index 0000000000..7343b12bc9 --- /dev/null +++ b/wxPython/wxPython/py/wxd/default.css @@ -0,0 +1,208 @@ +/* +:Author: David Goodger +:Contact: goodger@users.sourceforge.net +:date: $Date$ +:version: $Revision$ +:copyright: This stylesheet has been placed in the public domain. + +Default cascading style sheet for the HTML output of Docutils. +*/ + +.first { + margin-top: 0 } + +.last { + margin-bottom: 0 } + +a.toc-backref { + text-decoration: none ; + color: black } + +dd { + margin-bottom: 0.5em } + +div.abstract { + margin: 2em 5em } + +div.abstract p.topic-title { + font-weight: bold ; + text-align: center } + +div.attention, div.caution, div.danger, div.error, div.hint, +div.important, div.note, div.tip, div.warning { + margin: 2em ; + border: medium outset ; + padding: 1em } + +div.attention p.admonition-title, div.caution p.admonition-title, +div.danger p.admonition-title, div.error p.admonition-title, +div.warning p.admonition-title { + color: red ; + font-weight: bold ; + font-family: sans-serif } + +div.hint p.admonition-title, div.important p.admonition-title, +div.note p.admonition-title, div.tip p.admonition-title { + font-weight: bold ; + font-family: sans-serif } + +div.dedication { + margin: 2em 5em ; + text-align: center ; + font-style: italic } + +div.dedication p.topic-title { + font-weight: bold ; + font-style: normal } + +div.figure { + margin-left: 2em } + +div.footer, div.header { + font-size: smaller } + +div.sidebar { + margin-left: 1em ; + border: medium outset ; + padding: 0em 1em ; + background-color: #ffffee ; + width: 40% ; + float: right } + +div.system-messages { + margin: 5em } + +div.system-messages h1 { + color: red } + +div.system-message { + border: medium outset ; + padding: 1em } + +div.system-message p.system-message-title { + color: red ; + font-weight: bold } + +div.topic { + margin: 2em } + +h1.title { + text-align: center } + +h2.subtitle { + text-align: center } + +hr { + width: 75% } + +ol.simple, ul.simple { + margin-bottom: 1em } + +ol.arabic { + list-style: decimal } + +ol.loweralpha { + list-style: lower-alpha } + +ol.upperalpha { + list-style: upper-alpha } + +ol.lowerroman { + list-style: lower-roman } + +ol.upperroman { + list-style: upper-roman } + +p.caption { + font-style: italic } + +p.credits { + font-style: italic ; + font-size: smaller } + +p.label { + white-space: nowrap } + +p.sidebar-title { + font-family: sans-serif ; + font-weight: bold ; + font-size: larger } + +p.sidebar-subtitle { + font-family: sans-serif ; + font-weight: bold } + +p.topic-title { + font-weight: bold } + +pre.address { + margin-bottom: 0 ; + margin-top: 0 ; + font-family: serif ; + font-size: 100% } + +pre.line-block { + font-family: serif ; + font-size: 100% } + +pre.literal-block, pre.doctest-block { + margin-left: 2em ; + margin-right: 2em ; + background-color: #eeeeee } + +span.classifier { + font-family: sans-serif ; + font-style: oblique } + +span.classifier-delimiter { + font-family: sans-serif ; + font-weight: bold } + +span.interpreted { + font-family: sans-serif } + +span.option { + white-space: nowrap } + +span.option-argument { + font-style: italic } + +span.pre { + white-space: pre } + +span.problematic { + color: red } + +table { + margin-top: 0.5em ; + margin-bottom: 0.5em } + +table.citation { + border-left: solid thin gray ; + padding-left: 0.5ex } + +table.docinfo { + margin: 2em 4em } + +table.footnote { + border-left: solid thin black ; + padding-left: 0.5ex } + +td, th { + padding-left: 0.5em ; + padding-right: 0.5em ; + vertical-align: top } + +th.docinfo-name, th.field-name { + font-weight: bold ; + text-align: left ; + white-space: nowrap } + +h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { + font-size: 100% } + +tt { + background-color: #eeeeee } + +ul.auto-toc { + list-style-type: none } diff --git a/wxPython/wxPython/lib/PyCrust/wxd/gen.py b/wxPython/wxPython/py/wxd/gen.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/gen.py rename to wxPython/wxPython/py/wxd/gen.py diff --git a/wxPython/wxPython/lib/PyCrust/wxd/stc_.py b/wxPython/wxPython/py/wxd/stc_.py similarity index 100% rename from wxPython/wxPython/lib/PyCrust/wxd/stc_.py rename to wxPython/wxPython/py/wxd/stc_.py diff --git a/wxPython/wxPython/py/wxd/wxPython.html b/wxPython/wxPython/py/wxd/wxPython.html new file mode 100644 index 0000000000..9b90e328e0 --- /dev/null +++ b/wxPython/wxPython/py/wxd/wxPython.html @@ -0,0 +1,1006 @@ + + + + + + +The wxPython Reference + + + + + +
+

The wxPython Reference

+

A guide to the wxPython toolkit for Python programmers

+ +++ + + + + + + + + + + + +
Author:Patrick K. O'Brien
Contact:pobrien@orbtech.com
Date:2003-03-22
Revision:1.2
License:wxWindows Free Documentation Licence, Version 3
+ +
+

Introduction

+

This is a guide to the wxPython GUI toolkit, written by a Python +programmer for his fellow Python programmers. It began as a +simple translation of the wxWindows documentation (which is written +for C++ programmers), and evolved from there. And while there's +nothing wrong with C++...

+

Okay, you got me there. I hate C++. That's why I use Python. If you +like C++, go read the wxWindows documentation. If you'd rather read a +guide that's written with Python programmers in mind, keep reading +this one. If you like it, feel free to send me freshly roasted coffee +beans, dark chocolate, and large denomination currency. Better yet, +buy huge quantities of my wxPython book (written with Robin Dunn) and +send one to each of your friends, relatives, and coworkers.

+
+
+

What is wxPython?

+

wxPython is a GUI toolkit for the Python programming language. It +allows Python programmers to create programs with a robust, highly +functional graphical user interface, simply and easily. It is +implemented as a Python extension module (native code) that wraps the +popular wxWindows cross platform GUI library, which is written in C++.

+

Like Python and wxWindows, wxPython is Open Source, which means that +it is free for anyone to use and the source code is available for +anyone to look at and modify. And anyone can contribute fixes or +enhnacments to the project.

+

wxPython is a cross-platform toolkit. This means that the same +program will run on multiple platforms without modification. +Currently supported platforms are 32-bit Microsoft Windows, most Unix +or unix-like systems, and Macintosh OS X.

+

Since the language is Python, wxPython programs are simple, easy to +write and easy to understand.

+
+
+

wxPython requirements

+

To make use of wxPython, you currently need one of the following +setups.

+
+

MS-Windows

+
    +
  • A 486 or higher PC running MS Windows.
  • +
  • At least ?? MB of disk space.
  • +
+
+
+

Linux or Unix

+
    +
  • Almost any C++ compiler, including GNU C++ (EGCS 1.1.1 or above).
  • +
  • Almost any Unix workstation, and one of: GTK+ 1.2, GTK+ 2.0, Motif +1.2 or higher, Lesstif.
  • +
  • At least ?? MB of disk space.
  • +
+
+
+

Mac OS X

+
    +
  • A PowerPC Mac running Mac OS X 10.x.
  • +
  • At least ?? MB of disk space.
  • +
+
+
+
+

What is wxWindows?

+

wxWindows is a C++ framework providing GUI (Graphical User Interface) +and other facilities on more than one platform. Version 2 currently +supports all desktop versions of MS Windows, Unix with GTK+, Unix with +Motif, and MacOS. An OS/2 port is in progress.

+

wxWindows was originally developed at the Artificial Intelligence +Applications Institute, University of Edinburgh, for internal use, and +was first made publicly available in 1992. Version 2 is a vastly +improved version written and maintained by Julian Smart, Robert +Roebling, Vadim Zeitlin, Vaclav Slavik and many others.

+

Please note that in the following, "MS Windows" often refers to all +platforms related to Microsoft Windows, including 16-bit and 32-bit +variants, unless otherwise stated. All trademarks are acknowledged.

+
+
+

Why another cross-platform development tool?

+

wxWindows was developed to provide a cheap and flexible way to +maximize investment in GUI application development. While a number of +commercial class libraries already existed for cross-platform +development, none met all of the following criteria:

+
    +
  • low price
  • +
  • source availability
  • +
  • simplicity of programming
  • +
  • support for a wide range of compilers
  • +
+

Since wxWindows was started, several other free or almost-free GUI +frameworks have emerged. However, none has the range of features, +flexibility, documentation and the well-established development team +that wxWindows has.

+

As open source software, wxWindows has benefited from comments, ideas, +bug fixes, enhancements and the sheer enthusiasm of users. This gives +wxWindows a certain advantage over its commercial competitors (and +over free libraries without an independent development team), plus a +robustness against the transience of one individual or company. This +openness and availability of source code is especially important when +the future of thousands of lines of application code may depend upon +the longevity of the underlying class library.

+

Version 2 goes much further than previous versions in terms of +generality and features, allowing applications to be produced that are +often indistinguishable from those produced using single-platform +toolkits such as Motif, GTK+ and MFC.

+

The importance of using a platform-independent class library cannot be +overstated, since GUI application development is very time-consuming, +and sustained popularity of particular GUIs cannot be guaranteed. +Code can very quickly become obsolete if it addresses the wrong +platform or audience. wxWindows helps to insulate the programmer from +these winds of change. Although wxWindows may not be suitable for +every application (such as an OLE-intensive program), it provides +access to most of the functionality a GUI program normally requires, +plus many extras such as network programming, PostScript output, and +HTML rendering; and it can of course be extended as needs dictate. As +a bonus, it provides a far cleaner and easier programming interface +than the native APIs. Programmers may find it worthwhile to use +wxWindows even if they are developing on only one platform.

+

It is impossible to sum up the functionality of wxWindows in a few +paragraphs, but here are some of the benefits:

+
    +
  • Low cost (free, in fact!)
  • +
  • You get the source.
  • +
  • Available on a variety of popular platforms.
  • +
  • Works with almost all popular C++ compilers and Python.
  • +
  • Over 50 example programs.
  • +
  • Over 1000 pages of printable and on-line documentation.
  • +
  • Includes Tex2RTF, to allow you to produce your own documentation in +Windows Help, HTML and Word RTF formats.
  • +
  • Simple-to-use, object-oriented API.
  • +
  • Flexible event system.
  • +
  • Graphics calls include lines, rounded rectangles, splines, +polylines, etc.
  • +
  • Constraint-based and sizer-based layouts.
  • +
  • Print/preview and document/view architectures.
  • +
  • Toolbar, notebook, tree control, advanced list control classes.
  • +
  • PostScript generation under Unix, normal MS Windows printing on the +PC.
  • +
  • MDI (Multiple Document Interface) support.
  • +
  • Can be used to create DLLs under Windows, dynamic libraries on Unix.
  • +
  • Common dialogs for file browsing, printing, colour selection, etc.
  • +
  • Under MS Windows, support for creating metafiles and copying them to +the clipboard.
  • +
  • An API for invoking help from applications.
  • +
  • Ready-to-use HTML window (supporting a subset of HTML).
  • +
  • Dialog Editor for building dialogs.
  • +
  • Network support via a family of socket and protocol classes.
  • +
  • Support for platform independent image processing.
  • +
  • Built-in support for many file formats (BMP, PNG, JPEG, GIF, XPM, +PNM, PCX).
  • +
+
+
+

wxPython Overview

+

To set a wxPython application going, you will need to derive an App +class and override App.OnInit.

+

An application must have a top-level Frame or Dialog window. Each +frame may contain one or more instances of classes such as Panel, +SplitterWindow or other windows and controls.

+

A frame can have a MenuBar, a ToolBar, a status line, and an Icon for +when the frame is iconized.

+

A Panel is used to place controls (classes derived from Control) which +are used for user interaction. Examples of controls are Button, +CheckBox, Choice, ListBox, RadioBox, Slider.

+

Instances of Dialog can also be used for controls, and they have the +advantage of not requiring a separate frame.

+

Instead of creating a dialog box and populating it with items, it is +possible to choose one of the convenient common dialog classes, such +as MessageDialog and FileDialog.

+

You never draw directly onto a window. Instead, you use a device +context (DC). DC is the base for ClientDC, PaintDC, MemoryDC, +PostScriptDC, MemoryDC, MetafileDC and PrinterDC. If your drawing +functions have DC as a parameter, you can pass any of these DCs to the +function, and thus use the same code to draw to several different +devices. You can draw using the member functions of DC, such as +DC.DrawLine and DC.DrawText. Control colour on a window (Colour) with +brushes (Brush) and pens (Pen).

+ +

Most modern applications will have an on-line, hypertext help system; +for this, you need Help and the HelpController class to control +Help.

+

GUI applications aren't all graphical wizardry. You'll also need +lists and hash tables. But since you're working with Python, you +should use the ones Python provides (list, tuple, dict), rather than +the wxWindows versions. Same goes for the database related classes. +The basic rule of thumb is this: If you can do it directly in Python, +you probably should. If there is a reason not to use a Python data +type, wxPython will provide a wrapper for the wxWindows class.

+

You will undoubtedly need some platform-independent file functions, +and you may find it handy to maintain and search a list of paths using +PathList. There's a miscellany of operating system and other +functions.

+

See also Classes by Category for a list of classes.

+
+
+

Utilities and libraries supplied with wxPython

+

In addition to the core wxWindows library, a number of further +libraries and utilities are supplied with each distribution.

+

[Need to list these.]

+
+
+

Creating and deleting wxPython objects

+

[This section needs to be reviewed.]

+ + + + + +
+
+

App overview

+

Classes: wx.App

+
+

Application initialization

+

The OnInit method defined for a class derived from wx.App will usually +create a top window as a bare minimum.

+

OnInit must return a boolean value to indicate whether processing +should continue (True) or not (False). You call App.SetTopWindow to +let wxPython know about the top window.

+

An application closes by destroying all windows. Because all frames +must be destroyed for the application to exit, it is advisable to use +parent frames wherever possible when creating new frames, so that +deleting the top level frame will automatically delete child frames. +The alternative is to explicitly delete child frames in the top-level +frame's CloseEvent handler.

+

In emergencies the wx.Exit() function can be called to kill the +application, however, normally the application shuts down +automatically, see below.

+

An example of defining an application follows:

+
+import wx
+
+from frame import Frame
+
+class App(wx.App):
+    """Application class."""
+
+    def OnInit(self):
+        self.frame = Frame()
+        self.frame.Show()
+        self.SetTopWindow(self.frame)
+        return True
+
+def main():
+    app = App()
+    app.MainLoop()
+
+if __name__ == '__main__':
+    main()
+
+
+
+

Application shutdown

+

The application normally shuts down when the last of its top level +windows is closed. This is normally the expected behaviour and means +that it is enough to call Close() in response to the "Exit" menu +command if your program has a single top level window. If this +behaviour is not desirable, App.SetExitOnFrameDelete can be called to +change it. Note that such logic doesn't apply for the windows shown +before the program enters the main loop: in other words, you can +safely show a dialog from App.OnInit and not be afraid that your +application terminates when this dialog -- which is the last top level +window for the moment -- is closed.

+

Another aspect of the application shutdown is the OnExit which is +called when the application exits but before wxPython cleans up its +internal structures. You should delete all wxPython objects that you +created by the time OnExit finishes.

+

For example, this code may crash:

+

[Need examples of objects needing cleanup to keep app from crashing.]

+
+
+
+

Sizer overview

+

Classes: wx.Sizer, wx.GridSizer, wx.FlexGridSizer, wx.BoxSizer, +wx.StaticBoxSizer, wx.NotebookSizer, wx.CreateButtonSizer

+ ++++ + + + + + + + + + + + + + + + + + + + + +
SizerAbstract base class.
GridSizerA sizer for laying out windows in a grid with all +fields having the same size.
FlexGridSizerA sizer for laying out windows in a flexible grid.
BoxSizerA sizer for laying out windows in a row or column.
StaticBoxSizerSame as BoxSizer, but with a surrounding static box.
NotebookSizerSizer to use with the Notebook control.
+

Sizers, as represented by the wx.Sizer class and its descendants in +the wxPython class hierarchy, have become the method of choice to +define the layout of controls in dialogs in wxPython because of their +ability to create visually appealing dialogs independent of the +platform, taking into account the differences in size and style of the +individual controls. Editors such as wxDesigner, wxrcedit, XRCed and +wxWorkshop create dialogs based exclusively on sizers, practically +forcing the user to create platform independent layouts without +compromises.

+
+

The idea behind sizers

+

The layout algorithm used by sizers in wxPython is closely related to +layout systems in other GUI toolkits, such as Java's AWT, the GTK +toolkit or the Qt toolkit. It is based upon the idea of individual +subwindows reporting their minimal required size and their ability to +get stretched if the size of the parent window has changed. This will +most often mean that the programmer does not set the start-up size of +a dialog, the dialog will rather be assigned a sizer and this sizer +will be queried about the recommended size. This sizer in turn will +query its children (which can be normal windows, empty space or other +sizers) so that a hierarchy of sizers can be constructed. Note that +wx.Sizer does not derive from wx.Window and thus does not interfere +with tab ordering and requires very few resources compared to a real +window on screen.

+

What makes sizers so well fitted for use in wxPython is the fact that +every control reports its own minimal size and the algorithm can +handle differences in font sizes or different window (dialog item) +sizes on different platforms without problems. For example, if the +standard font as well as the overall design of Linux/GTK widgets +requires more space than on Windows, the initial dialog size will +automatically be bigger on Linux/GTK than on Windows.

+

There are currently five different kinds of sizers available in +wxPython. Each represents either a certain way to lay out dialog items +in a dialog or it fulfils a special task such as wrapping a static box +around a dialog item (or another sizer). These sizers will be +discussed one by one in the text below.

+
+
+

Common features

+

All sizers are containers, that is, they are used to lay out one +dialog item (or several dialog items), which they contain. Such items +are sometimes referred to as the children of the sizer. Independent +of how the individual sizers lay out their children, all children have +certain features in common:

+
+

A minimal size

+

This minimal size is usually identical to the initial size of the +controls and may either be set explicitly in the size field of the +control constructor or may be calculated by wxPython, typically by +setting the height and/or the width of the item to -1. Note that only +some controls can calculate their size (such as a checkbox) whereas +others (such as a listbox) don't have any natural width or height and +thus require an explicit size. Some controls can calculate their +height, but not their width (e.g. a single line text control):

+

[Need graphics]

+
+
+

A border

+

The border is just empty space and is used to separate dialog items in +a dialog. This border can either be all around, or at any combination +of sides such as only above and below the control. The thickness of +this border must be set explicitly, typically 5 points. The following +samples show dialogs with only one dialog item (a button) and a border +of 0, 5, and 10 pixels around the button:

+

[Need graphics]

+
+
+

An alignment

+

Often, a dialog item is given more space than its minimal size plus +its border. Depending on what flags are used for the respective dialog +item, the dialog item can be made to fill out the available space +entirely, i.e. it will grow to a size larger than the minimal size, or +it will be moved to either the centre of the available space or to +either side of the space. The following sample shows a listbox and +three buttons in a horizontal box sizer; one button is centred, one is +aligned at the top, one is aligned at the bottom:

+

[Need graphics]

+
+
+

A stretch factor

+

If a sizer contains more than one child and it is offered more space +than its children and their borders need, the question arises how to +distribute the surplus space among the children. For this purpose, a +stretch factor may be assigned to each child, where the default value +of 0 indicates that the child will not get more space than its +requested minimum size. A value of more than zero is interpreted in +relation to the sum of all stretch factors in the children of the +respective sizer, i.e. if two children get a stretch factor of 1, they +will get half the extra space each independent of whether one control +has a minimal sizer inferior to the other or not. The following +sample shows a dialog with three buttons, the first one has a stretch +factor of 1 and thus gets stretched, whereas the other two buttons +have a stretch factor of zero and keep their initial width:

+

[Need graphics]

+

Within wxDesigner, this stretch factor gets set from the Option menu.

+
+
+
+

BoxSizer

+

BoxSizer can lay out its children either vertically or horizontally, +depending on what flag is being used in its constructor. When using a +vertical sizer, each child can be centered, aligned to the right or +aligned to the left. Correspondingly, when using a horizontal sizer, +each child can be centered, aligned at the bottom or aligned at the +top. The stretch factor described in the last paragraph is used for +the main orientation, i.e. when using a horizontal box sizer, the +stretch factor determines how much the child can be stretched +horizontally. The following sample shows the same dialog as in the +last sample, only the box sizer is a vertical box sizer now:

+

[Need graphics]

+
+
+

StaticBoxSizer

+

StaticBoxSixer is the same as a BoxSizer, but surrounded by a static +box. Here is a sample:

+

[Need graphics]

+
+
+

GridSizer

+

GridSizer is a two-dimensional sizer. All children are given the same +size, which is the minimal size required by the biggest child, in this +case the text control in the left bottom border. Either the number of +columns or the number or rows is fixed and the grid sizer will grow in +the respectively other orientation if new children are added:

+

[Need graphics]

+
+
+

FlexGridSizer

+

Another two-dimensional sizer derived from GridSizer. The width of +each column and the height of each row are calculated individually +according the minimal requirements from the respectively biggest +child. Additionally, columns and rows can be declared to be +stretchable if the sizer is assigned a size different from that which +it requested. The following sample shows the same dialog as the one +above, but using a flex grid sizer:

+

[Need graphics]

+
+
+

NotebookSizer

+

NotebookSizer can be used with notebooks. It calculates the size of +each notebook page and sets the size of the notebook to the size of +the biggest page plus some extra space required for the notebook tabs +and decorations.

+

[Need graphics]

+
+
+

Programming with BoxSizer

+

The basic idea behind a BoxSizer is that windows will most often be +laid out in rather simple basic geometry, typically in a row or a +column or several hierarchies of either.

+

As an example, we will construct a dialog that will contain a text +field at the top and two buttons at the bottom. This can be seen as a +top-hierarchy column with the text at the top and buttons at the +bottom and a low-hierarchy row with an OK button to the left and a +Cancel button to the right. In many cases (particularly dialogs under +Unix and normal frames) the main window will be resizable by the user +and this change of size will have to get propagated to its children. +In our case, we want the text area to grow with the dialog, whereas +the button shall have a fixed size. In addition, there will be a thin +border around all controls to make the dialog look nice and - to make +matter worse - the buttons shall be centred as the width of the dialog +changes.

+

It is the unique feature of a box sizer, that it can grow in both +directions (height and width) but can distribute its growth in the +main direction (horizontal for a row) unevenly among its children. In +our example case, the vertical sizer is supposed to propagate all its +height changes to only the text area, not to the button area. This is +determined by the proportion parameter when adding a window (or +another sizer) to a sizer. It is interpreted as a weight factor, +i.e. it can be zero, indicating that the window may not be resized at +all, or above zero. If several windows have a value above zero, the +value is interpreted relative to the sum of all weight factors of the +sizer, so when adding two windows with a value of 1, they will both +get resized equally much and each half as much as the sizer owning +them.

+

Then what do we do when a column sizer changes its width? This +behaviour is controlled by flags (the second parameter of the Add() +function): zero or no flag indicates that the window will preserve it +is original size, wx.GROW flag (same as wx.EXPAND) forces the window +to grow with the sizer, and wx.SHAPED flag tells the window to change +it is size proportionally, preserving original aspect ratio. When +wx.GROW flag is not used, the item can be aligned within available +space. wx.ALIGN_LEFT, wx.ALIGN_TOP, wx.ALIGN_RIGHT, wx.ALIGN_BOTTOM, +wx.ALIGN_CENTER_HORIZONTAL and wx.ALIGN_CENTER_VERTICAL do what they +say. wx.ALIGN_CENTRE (same as wx.ALIGN_CENTER) is defined as +(wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL). Default +alignment is wx.ALIGN_LEFT | wx.ALIGN_TOP.

+

As mentioned above, any window belonging to a sizer may have border, +and it can be specified which of the four sides may have this border, +using the wx.TOP, wx.LEFT, wx.RIGHT and wx.BOTTOM constants or wx.ALL +for all directions (and you may also use wx.NORTH, wx.WEST etc +instead). These flags can be used in combination with the alignment +flags above as the second parameter of the Add() method using the +binary or operator (|). The sizer of the border also must be made +known, and it is the third parameter in the Add() method. This means, +that the entire behaviour of a sizer and its children can be +controlled by the three parameters of the Add() method.

+

[Show code and graphic here.]

+
+
+

Programming with GridSizer

+

GridSizer is a sizer which lays out its children in a two-dimensional +table with all table fields having the same size, i.e. the width of +each field is the width of the widest child, the height of each field +is the height of the tallest child.

+

[Show code and graphic here.]

+
+
+

Programming with FlexGridSizer

+

FlexGridSizer is a sizer which lays out its children in a +two-dimensional table with all table fields in one row having the same +height and all fields in one column having the same width, but all +rows or all columns are not necessarily the same height or width as in +the GridSizer.

+

[Show code and graphic here.]

+
+
+

Programming with NotebookSizer

+

NotebookSizer is a specialized sizer to make sizers work in connection +with using notebooks. This sizer is different from any other sizer as +you must not add any children to it - instead, it queries the notebook +class itself. The only thing this sizer does is to determine the size +of the biggest page of the notebook and report an adjusted minimal +size to a more toplevel sizer.

+

In order to query the size of notebook page, this page needs to have +its own sizer, otherwise the NotebookSizer will ignore it. Notebook +pages get their sizer by assigning one to them using SetSizer() and +setting the auto-layout option to True using SetAutoLayout(). Here is +one example showing how to add a notebook page that the notebook sizer +is aware of:

+

[Show code and graphic here.]

+
+
+

Programming with StaticBoxSizer

+

StaticBoxSizer is a sizer derived from BoxSizer but adds a static box +around the sizer. Note that this static box has to be created +separately.

+

[Show code and graphic here.]

+
+
+

Dialog.CreateButtonSizer

+

As a convenience, the Dialog class has a CreateButtonSizer(flags) +method that can be used to create a standard button sizer in which +standard buttons are displayed. The following flags can be passed to +this method:

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
wx.YES_NOadd Yes/No subpanel
wx.YESreturn wx.ID_YES
wx.NOreturn wx.ID_NO
wx.NO_DEFAULTmake the wx.NO button the default, otherwise wx.YES or +wx.OK button will be default
wx.OKreturn wx.ID_OK
wx.CANCELreturn wx.ID_CANCEL
wx.HELPreturn wx.ID_HELP
wx.FORWARDreturn wx.ID_FORWARD
wx.BACKWARDreturn wx.ID_BACKWARD
wx.SETUPreturn wx.ID_SETUP
wx.MOREreturn wx.ID_MORE
+
+
+
+

Date and time classes overview

+

wxPython provides a set of powerful classes to work with dates and +times. Some of the supported features of the DateTime class are:

+ ++++ + + + + + + + + + + + + + + +
Wide rangeThe range of supported dates goes from about 4714 B.C. to +some 480 million years in the future.
PrecisionNot using floating point calculations anywhere ensures that +the date calculations don't suffer from rounding +errors.
Many featuresNot only all usual calculations with dates are +supported, but also more exotic week and year day +calculations, work day testing, standard astronomical +functions, conversion to and from strings in either +strict or free format.
EfficiencyObjects of DateTime are small (8 bytes) and working +with them is fast
+
+

All date/time classes at a glance

+

There are 3 main classes: except DateTime itself which represents an +absolute moment in time, there are also two classes - TimeSpan and +DateSpan which represent the intervals of time.

+

There are also helper classes which are used together with DateTime: +DateTimeHolidayAuthority which is used to determine whether a given +date is a holiday or not and DateTimeWorkDays which is a derivation of +this class for which (only) Saturdays and Sundays are the holidays. +See more about these classes in the discussion of the holidays.

+
+
+

DateTime characteristics

+

DateTime stores the time as a signed number of milliseconds since the +Epoch which is fixed, by convention, to Jan 1, 1970 - however this is +not visible to the class users (in particular, dates prior to the +Epoch are handled just as well (or as bad) as the dates after it). +But it does mean that the best resolution which can be achieved with +this class is 1 millisecond.

+

The size of DateTime object is 8 bytes because it is represented as a +64 bit integer. The resulting range of supported dates is thus +approximatively 580 million years, but due to the current limitations +in the Gregorian calendar support, only dates from Nov 24, 4714BC are +supported (this is subject to change if there is sufficient interest +in doing it).

+

Finally, the internal representation is time zone independent (always +in GMT) and the time zones only come into play when a date is broken +into year/month/day components. See more about timezones below.

+

Currently, the only supported calendar is Gregorian one (which is used +even for the dates prior to the historic introduction of this calendar +which was first done on Oct 15, 1582 but is, generally speaking, +country, and even region, dependent). Future versions will probably +have Julian calendar support as well and support for other calendars +(Maya, Hebrew, Chinese...) is not ruled out.

+
+
+

Difference between DateSpan and TimeSpan

+

While there is only one logical way to represent an absolute moment in +the time (and hence only one DateTime class), there are at least two +methods to describe a time interval.

+

First, there is the direct and self-explaining way implemented by +TimeSpan: it is just a difference in milliseconds between two moments +in time. Adding or subtracting such an interval to DateTime is always +well-defined and is a fast operation.

+

But in daily life other, calendar-dependent time interval +specifications are used. For example, 'one month later' is commonly +used. However, it is clear that this is not the same as TimeSpan of +60*60*24*31 seconds because 'one month later' Feb 15 is Mar 15 and not +Mar 17 or Mar 16 (depending on whether the year is leap or not).

+

This is why there is another class for representing such intervals +called DateSpan. It handles these sort of operations in the most +natural way possible, but note that manipulating with intervals of +this kind is not always well-defined. Consider, for example, Jan 31 + +'1 month': this will give Feb 28 (or 29), i.e. the last day of +February and not the non-existent Feb 31. Of course, this is what is +usually wanted, but you still might be surprised to notice that now +subtracting back the same interval from Feb 28 will result in Jan 28 +and not Jan 31 we started with!

+

So, unless you plan to implement some kind of natural language parsing +in the program, you should probably use TimeSpan instead of DateSpan +(which is also more efficient). However, DateSpan may be very useful +in situations when you do need to understand what 'in a month' means +(of course, it is just DateTime.Now() + DateSpan.Month()).

+
+
+

Date arithmetics

+

Many different operations may be performed with the dates, however not +all of them make sense. For example, multiplying a date by a number +is an invalid operation, even though multiplying either of the time +span classes by a number is perfectly valid.

+

Here is what can be done:

+ ++++ + + + + + + + + + + + + + + +
Additiona TimeSpan or DateSpan can be added to DateTime resulting in +a new DateTime object and also 2 objects of the same +span class can be added together giving another object +of the same class.
Subtractionthe same types of operations as above are allowed and, +additionally, a difference between two DateTime +objects can be taken and this will yield TimeSpan.
Multiplicationa TimeSpan or DateSpan object can be multiplied by an +integer number resulting in an object of the same +type.
Unary minusa TimeSpan or DateSpan object may finally be negated +giving an interval of the same magnitude but of +opposite time direction.
+
+
+

Time zone considerations

+

Although the time is always stored internally in GMT, you will usually +work in the local time zone. Because of this, all DateTime +constructors and setters which take the broken down date assume that +these values are for the local time zone. Thus, DateTime(1, +DateTime.Jan, 1970) will not correspond to the DateTime Epoch unless +you happen to live in the UK.

+

All methods returning the date components (year, month, day, hour, +minute, second...) will also return the correct values for the local +time zone by default. So, generally, doing the natural things will +lead to natural and correct results.

+

If you only want to do this, you may safely skip the rest of this +section. However, if you want to work with different time zones, you +should read it to the end.

+

In this (rare) case, you are still limited to the local time zone when +constructing DateTime objects, i.e. there is no way to construct a +DateTime corresponding to the given date in, say, Pacific Standard +Time. To do it, you will need to call ToTimezone or MakeTimezone +methods to adjust the date for the target time zone. There are also +special versions of these functions ToGMT and MakeGMT for the most +common case - when the date should be constructed in GMT.

+

You also can just retrieve the value for some time zone without +converting the object to it first. For this you may pass TimeZone +argument to any of the methods which are affected by the time zone +(all methods getting date components and the date formatting ones, for +example). In particular, the Format() family of methods accepts a +TimeZone parameter and this allows to simply print time in any time +zone.

+

To see how to do it, the last issue to address is how to construct a +TimeZone object which must be passed to all these methods. First of +all, you may construct it manually by specifying the time zone offset +in seconds from GMT, but usually you will just use one of the symbolic +time zone names and let the conversion constructor do the +job. I.e. you would just write

+

wxDateTime dt(...whatever...); +printf("The time is %s in local time zone", dt.FormatTime().c_str()); +printf("The time is %s in GMT", dt.FormatTime(wxDateTime::GMT).c_str());

+
+
+

Daylight saving time (DST)

+

DST (a.k.a. 'summer time') handling is always a delicate task which is +better left to the operating system which is supposed to be configured +by the administrator to behave correctly. Unfortunately, when doing +calculations with date outside of the range supported by the standard +library, we are forced to deal with these issues ourselves.

+

Several functions are provided to calculate the beginning and end of +DST in the given year and to determine whether it is in effect at the +given moment or not, but they should not be considered as absolutely +correct because, first of all, they only work more or less correctly +for only a handful of countries (any information about other ones +appreciated!) and even for them the rules may perfectly well change in +the future.

+

The time zone handling methods use these functions too, so they are +subject to the same limitations.

+
+ +
+
+

Classes by category

+

Not done yet.

+
+
+

Source Document

+

The source document is named wxPython.txt and is located in the +PyCrust/wxd directory. It is written using a fantastic formatting +convention called reStructuredText. The wxPython.html file is created +using the Docutils utilities, which can turn reStructuredText +documents into html, xml, pdf, and even OpenOffice files.

+

Some items in the source text file look like this:

+
+.. This is text from the wxWindows documentation that needs to be
+   translated into something appropriate for the wxPython version.
+   The two dots followed by uniformly indented text turns this
+   paragraph into a reStructuredText comment, so it doesn't appear
+   in any output file, such as the html file.
+
+

They have been commented out and are awaiting editorial review and a +rewrite so that they make sense in the context of wxPython. Feel free +to send me suggestions for rewording these, or any other parts of this +document that you think need improving. I will be eternally grateful +to you and will show my gratitude by adding your name to the list of +contributors. (Contributors who also send me gifts of coffee, +chocolate, or currency will have their names listed in bold.)

+
+
+

Contributors

+

Individuals who contributed to this documentation (in order by last +name):

+
    +
  • Robin Dunn
  • +
  • Patrick K. O'Brien
  • +
  • Robert Roebling
  • +
  • Julian Smart
  • +
  • Vadim Zeitlin
  • +
+
+
+

License

+

This document began as a translation of the wxWindows documentation. +As such, it adheres to the same license, which is provided here:

+
+                wxWindows Free Documentation Licence, Version 3
+                ===============================================
+
+  Copyright (c) 1998 Julian Smart, Robert Roebling et al
+
+  Everyone is permitted to copy and distribute verbatim copies
+  of this licence document, but changing it is not allowed.
+   
+                   WXWINDOWS FREE DOCUMENTATION LICENCE
+     TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  1. Permission is granted to make and distribute verbatim copies of this
+  manual or piece of documentation provided any copyright notice and this
+  permission notice are preserved on all copies.
+
+  2. Permission is granted to process this file or document through a
+  document processing system and, at your option and the option of any third
+  party, print the results, provided a printed document carries a copying
+  permission notice identical to this one.
+
+  3. Permission is granted to copy and distribute modified versions of this
+  manual or piece of documentation under the conditions for verbatim
+  copying, provided also that any sections describing licensing conditions
+  for this manual, such as, in particular, the GNU General Public Licence,
+  the GNU Library General Public Licence, and any wxWindows Licence are
+  included exactly as in the original, and provided that the entire
+  resulting derived work is distributed under the terms of a permission
+  notice identical to this one.
+
+  4. Permission is granted to copy and distribute translations of this
+  manual or piece of documentation into another language, under the above
+  conditions for modified versions, except that sections related to
+  licensing, including this paragraph, may also be included in translations
+  approved by the copyright holders of the respective licence documents in
+  addition to the original English.
+
+                            WARRANTY DISCLAIMER
+
+  5. BECAUSE THIS MANUAL OR PIECE OF DOCUMENTATION IS LICENSED FREE OF CHARGE,
+  THERE IS NO WARRANTY FOR IT, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 
+  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER
+  PARTIES PROVIDE THIS MANUAL OR PIECE OF DOCUMENTATION "AS IS" WITHOUT
+  WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+  PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF
+  THE MANUAL OR PIECE OF DOCUMENTATION IS WITH YOU.  SHOULD THE MANUAL OR
+  PIECE OF DOCUMENTATION PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+  NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  6. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL
+  ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+  REDISTRIBUTE THE MANUAL OR PIECE OF DOCUMENTATION AS PERMITTED ABOVE, BE
+  LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+  CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+  MANUAL OR PIECE OF DOCUMENTATION (INCLUDING BUT NOT LIMITED TO LOSS OF
+  DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+  PARTIES OR A FAILURE OF A PROGRAM BASED ON THE MANUAL OR PIECE OF
+  DOCUMENTATION TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR
+  OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+
+
+
+
+ + diff --git a/wxPython/wxPython/py/wxd/wxPython.txt b/wxPython/wxPython/py/wxd/wxPython.txt new file mode 100644 index 0000000000..4d62ed784d --- /dev/null +++ b/wxPython/wxPython/py/wxd/wxPython.txt @@ -0,0 +1,1032 @@ +======================== + The wxPython Reference +======================== + +-------------------------------------------------------- + A guide to the wxPython toolkit for Python programmers +-------------------------------------------------------- + +:Author: Patrick K. O'Brien +:Contact: pobrien@orbtech.com +:Date: $Date$ +:Revision: $Revision$ +:License: wxWindows Free Documentation Licence, Version 3 + +.. contents:: + + +Introduction +============ + +This is a guide to the wxPython GUI toolkit, written **by** a Python +programmer **for** his fellow Python programmers. It began as a +simple translation of the wxWindows documentation (which is written +for C++ programmers), and evolved from there. And while there's +nothing wrong with C++... + +Okay, you got me there. I hate C++. That's why I use Python. If you +like C++, go read the wxWindows documentation. If you'd rather read a +guide that's written with Python programmers in mind, keep reading +this one. If you like it, feel free to send me freshly roasted coffee +beans, dark chocolate, and large denomination currency. Better yet, +buy huge quantities of my wxPython book (written with Robin Dunn) and +send one to each of your friends, relatives, and coworkers. + + +What is wxPython? +================= + +wxPython is a GUI toolkit for the Python programming language. It +allows Python programmers to create programs with a robust, highly +functional graphical user interface, simply and easily. It is +implemented as a Python extension module (native code) that wraps the +popular wxWindows cross platform GUI library, which is written in C++. + +Like Python and wxWindows, wxPython is Open Source, which means that +it is free for anyone to use and the source code is available for +anyone to look at and modify. And anyone can contribute fixes or +enhnacments to the project. + +wxPython is a cross-platform toolkit. This means that the same +program will run on multiple platforms without modification. +Currently supported platforms are 32-bit Microsoft Windows, most Unix +or unix-like systems, and Macintosh OS X. + +Since the language is Python, wxPython programs are simple, easy to +write and easy to understand. + + +wxPython requirements +===================== + +To make use of wxPython, you currently need one of the following +setups. + +MS-Windows +---------- + +* A 486 or higher PC running MS Windows. +* At least ?? MB of disk space. + +Linux or Unix +------------- + +* Almost any C++ compiler, including GNU C++ (EGCS 1.1.1 or above). +* Almost any Unix workstation, and one of: GTK+ 1.2, GTK+ 2.0, Motif + 1.2 or higher, Lesstif. +* At least ?? MB of disk space. + +Mac OS X +-------- + +* A PowerPC Mac running Mac OS X 10.x. +* At least ?? MB of disk space. + + +What is wxWindows? +================== + +wxWindows is a C++ framework providing GUI (Graphical User Interface) +and other facilities on more than one platform. Version 2 currently +supports all desktop versions of MS Windows, Unix with GTK+, Unix with +Motif, and MacOS. An OS/2 port is in progress. + +wxWindows was originally developed at the Artificial Intelligence +Applications Institute, University of Edinburgh, for internal use, and +was first made publicly available in 1992. Version 2 is a vastly +improved version written and maintained by Julian Smart, Robert +Roebling, Vadim Zeitlin, Vaclav Slavik and many others. + +Please note that in the following, "MS Windows" often refers to all +platforms related to Microsoft Windows, including 16-bit and 32-bit +variants, unless otherwise stated. All trademarks are acknowledged. + + +Why another cross-platform development tool? +============================================ + +wxWindows was developed to provide a cheap and flexible way to +maximize investment in GUI application development. While a number of +commercial class libraries already existed for cross-platform +development, none met all of the following criteria: + +* low price +* source availability +* simplicity of programming +* support for a wide range of compilers + +Since wxWindows was started, several other free or almost-free GUI +frameworks have emerged. However, none has the range of features, +flexibility, documentation and the well-established development team +that wxWindows has. + +As open source software, wxWindows has benefited from comments, ideas, +bug fixes, enhancements and the sheer enthusiasm of users. This gives +wxWindows a certain advantage over its commercial competitors (and +over free libraries without an independent development team), plus a +robustness against the transience of one individual or company. This +openness and availability of source code is especially important when +the future of thousands of lines of application code may depend upon +the longevity of the underlying class library. + +Version 2 goes much further than previous versions in terms of +generality and features, allowing applications to be produced that are +often indistinguishable from those produced using single-platform +toolkits such as Motif, GTK+ and MFC. + +The importance of using a platform-independent class library cannot be +overstated, since GUI application development is very time-consuming, +and sustained popularity of particular GUIs cannot be guaranteed. +Code can very quickly become obsolete if it addresses the wrong +platform or audience. wxWindows helps to insulate the programmer from +these winds of change. Although wxWindows may not be suitable for +every application (such as an OLE-intensive program), it provides +access to most of the functionality a GUI program normally requires, +plus many extras such as network programming, PostScript output, and +HTML rendering; and it can of course be extended as needs dictate. As +a bonus, it provides a far cleaner and easier programming interface +than the native APIs. Programmers may find it worthwhile to use +wxWindows even if they are developing on only one platform. + +It is impossible to sum up the functionality of wxWindows in a few +paragraphs, but here are some of the benefits: + +* Low cost (free, in fact!) +* You get the source. +* Available on a variety of popular platforms. +* Works with almost all popular C++ compilers and Python. +* Over 50 example programs. +* Over 1000 pages of printable and on-line documentation. +* Includes Tex2RTF, to allow you to produce your own documentation in + Windows Help, HTML and Word RTF formats. +* Simple-to-use, object-oriented API. +* Flexible event system. +* Graphics calls include lines, rounded rectangles, splines, + polylines, etc. +* Constraint-based and sizer-based layouts. +* Print/preview and document/view architectures. +* Toolbar, notebook, tree control, advanced list control classes. +* PostScript generation under Unix, normal MS Windows printing on the + PC. +* MDI (Multiple Document Interface) support. +* Can be used to create DLLs under Windows, dynamic libraries on Unix. +* Common dialogs for file browsing, printing, colour selection, etc. +* Under MS Windows, support for creating metafiles and copying them to + the clipboard. +* An API for invoking help from applications. +* Ready-to-use HTML window (supporting a subset of HTML). +* Dialog Editor for building dialogs. +* Network support via a family of socket and protocol classes. +* Support for platform independent image processing. +* Built-in support for many file formats (BMP, PNG, JPEG, GIF, XPM, + PNM, PCX). + + +wxPython Overview +================= + +To set a wxPython application going, you will need to derive an App +class and override App.OnInit. + +An application must have a top-level Frame or Dialog window. Each +frame may contain one or more instances of classes such as Panel, +SplitterWindow or other windows and controls. + +A frame can have a MenuBar, a ToolBar, a status line, and an Icon for +when the frame is iconized. + +A Panel is used to place controls (classes derived from Control) which +are used for user interaction. Examples of controls are Button, +CheckBox, Choice, ListBox, RadioBox, Slider. + +Instances of Dialog can also be used for controls, and they have the +advantage of not requiring a separate frame. + +Instead of creating a dialog box and populating it with items, it is +possible to choose one of the convenient common dialog classes, such +as MessageDialog and FileDialog. + +You never draw directly onto a window. Instead, you use a device +context (DC). DC is the base for ClientDC, PaintDC, MemoryDC, +PostScriptDC, MemoryDC, MetafileDC and PrinterDC. If your drawing +functions have DC as a parameter, you can pass any of these DCs to the +function, and thus use the same code to draw to several different +devices. You can draw using the member functions of DC, such as +DC.DrawLine and DC.DrawText. Control colour on a window (Colour) with +brushes (Brush) and pens (Pen). + +.. To intercept events, you add a DECLARE_EVENT_TABLE macro to the + window class declaration, and put a BEGIN_EVENT_TABLE + ... END_EVENT_TABLE block in the implementation file. Between these + macros, you add event macros which map the event (such as a mouse + click) to a member function. These might override predefined event + handlers such as for KeyEvent and MouseEvent. + +Most modern applications will have an on-line, hypertext help system; +for this, you need Help and the HelpController class to control +Help. + +GUI applications aren't all graphical wizardry. You'll also need +lists and hash tables. But since you're working with Python, you +should use the ones Python provides (list, tuple, dict), rather than +the wxWindows versions. Same goes for the database related classes. +The basic rule of thumb is this: If you can do it directly in Python, +you probably should. If there is a reason not to use a Python data +type, wxPython will provide a wrapper for the wxWindows class. + +You will undoubtedly need some platform-independent file functions, +and you may find it handy to maintain and search a list of paths using +PathList. There's a miscellany of operating system and other +functions. + +See also Classes by Category for a list of classes. + + +Utilities and libraries supplied with wxPython +============================================== + +In addition to the core wxWindows library, a number of further +libraries and utilities are supplied with each distribution. + +[Need to list these.] + + +Creating and deleting wxPython objects +====================================== + +[This section needs to be reviewed.] + +.. In general, classes derived from wxWindow must dynamically + allocated with new and deleted with delete. If you delete a window, + all of its children and descendants will be automatically deleted, + so you don't need to delete these descendants explicitly. + +.. When deleting a frame or dialog, use Destroy rather than delete so + that the wxWindows delayed deletion can take effect. This waits + until idle time (when all messages have been processed) to actually + delete the window, to avoid problems associated with the GUI + sending events to deleted windows. + +.. If you decide to allocate a C++ array of objects (such as wxBitmap) + that may be cleaned up by wxWindows, make sure you delete the array + explicitly before wxWindows has a chance to do so on exit, since + calling delete on array members will cause memory problems. + +.. wxColour can be created statically: it is not automatically cleaned + up and is unlikely to be shared between other objects; it is + lightweight enough for copies to be made. + +.. Beware of deleting objects such as a wxPen or wxBitmap if they are + still in use. Windows is particularly sensitive to this: so make + sure you make calls like wxDC::SetPen(wxNullPen) or + wxDC::SelectObject(wxNullBitmap) before deleting a drawing object + that may be in use. Code that doesn't do this will probably work + fine on some platforms, and then fail under Windows. + + +App overview +============ + +Classes: wx.App + +Application initialization +-------------------------- + +The OnInit method defined for a class derived from wx.App will usually +create a top window as a bare minimum. + +OnInit must return a boolean value to indicate whether processing +should continue (True) or not (False). You call App.SetTopWindow to +let wxPython know about the top window. + +An application closes by destroying all windows. Because all frames +must be destroyed for the application to exit, it is advisable to use +parent frames wherever possible when creating new frames, so that +deleting the top level frame will automatically delete child frames. +The alternative is to explicitly delete child frames in the top-level +frame's CloseEvent handler. + +In emergencies the wx.Exit() function can be called to kill the +application, however, normally the application shuts down +automatically, see below. + +An example of defining an application follows:: + + import wx + + from frame import Frame + + class App(wx.App): + """Application class.""" + + def OnInit(self): + self.frame = Frame() + self.frame.Show() + self.SetTopWindow(self.frame) + return True + + def main(): + app = App() + app.MainLoop() + + if __name__ == '__main__': + main() + + +Application shutdown +-------------------- + +The application normally shuts down when the last of its top level +windows is closed. This is normally the expected behaviour and means +that it is enough to call Close() in response to the "Exit" menu +command if your program has a single top level window. If this +behaviour is not desirable, App.SetExitOnFrameDelete can be called to +change it. Note that such logic doesn't apply for the windows shown +before the program enters the main loop: in other words, you can +safely show a dialog from App.OnInit and not be afraid that your +application terminates when this dialog -- which is the last top level +window for the moment -- is closed. + +Another aspect of the application shutdown is the OnExit which is +called when the application exits but before wxPython cleans up its +internal structures. You should delete all wxPython objects that you +created by the time OnExit finishes. + +For example, this code may crash: + +[Need examples of objects needing cleanup to keep app from crashing.] + + +Sizer overview +============== + +Classes: wx.Sizer, wx.GridSizer, wx.FlexGridSizer, wx.BoxSizer, +wx.StaticBoxSizer, wx.NotebookSizer, wx.CreateButtonSizer + +============== ====================================================== + +Sizer Abstract base class. + +GridSizer A sizer for laying out windows in a grid with all + fields having the same size. + +FlexGridSizer A sizer for laying out windows in a flexible grid. + +BoxSizer A sizer for laying out windows in a row or column. + +StaticBoxSizer Same as BoxSizer, but with a surrounding static box. + +NotebookSizer Sizer to use with the Notebook control. + +============== ====================================================== + +Sizers, as represented by the wx.Sizer class and its descendants in +the wxPython class hierarchy, have become the method of choice to +define the layout of controls in dialogs in wxPython because of their +ability to create visually appealing dialogs independent of the +platform, taking into account the differences in size and style of the +individual controls. Editors such as wxDesigner, wxrcedit, XRCed and +wxWorkshop create dialogs based exclusively on sizers, practically +forcing the user to create platform independent layouts without +compromises. + + +The idea behind sizers +---------------------- + +The layout algorithm used by sizers in wxPython is closely related to +layout systems in other GUI toolkits, such as Java's AWT, the GTK +toolkit or the Qt toolkit. It is based upon the idea of individual +subwindows reporting their minimal required size and their ability to +get stretched if the size of the parent window has changed. This will +most often mean that the programmer does not set the start-up size of +a dialog, the dialog will rather be assigned a sizer and this sizer +will be queried about the recommended size. This sizer in turn will +query its children (which can be normal windows, empty space or other +sizers) so that a hierarchy of sizers can be constructed. Note that +wx.Sizer does not derive from wx.Window and thus does not interfere +with tab ordering and requires very few resources compared to a real +window on screen. + +What makes sizers so well fitted for use in wxPython is the fact that +every control reports its own minimal size and the algorithm can +handle differences in font sizes or different window (dialog item) +sizes on different platforms without problems. For example, if the +standard font as well as the overall design of Linux/GTK widgets +requires more space than on Windows, the initial dialog size will +automatically be bigger on Linux/GTK than on Windows. + +There are currently five different kinds of sizers available in +wxPython. Each represents either a certain way to lay out dialog items +in a dialog or it fulfils a special task such as wrapping a static box +around a dialog item (or another sizer). These sizers will be +discussed one by one in the text below. + + +Common features +--------------- + +All sizers are containers, that is, they are used to lay out one +dialog item (or several dialog items), which they contain. Such items +are sometimes referred to as the children of the sizer. Independent +of how the individual sizers lay out their children, all children have +certain features in common: + + +A minimal size +~~~~~~~~~~~~~~ + +This minimal size is usually identical to the initial size of the +controls and may either be set explicitly in the size field of the +control constructor or may be calculated by wxPython, typically by +setting the height and/or the width of the item to -1. Note that only +some controls can calculate their size (such as a checkbox) whereas +others (such as a listbox) don't have any natural width or height and +thus require an explicit size. Some controls can calculate their +height, but not their width (e.g. a single line text control): + +[Need graphics] + + +A border +~~~~~~~~ + +The border is just empty space and is used to separate dialog items in +a dialog. This border can either be all around, or at any combination +of sides such as only above and below the control. The thickness of +this border must be set explicitly, typically 5 points. The following +samples show dialogs with only one dialog item (a button) and a border +of 0, 5, and 10 pixels around the button: + +[Need graphics] + + +An alignment +~~~~~~~~~~~~ + +Often, a dialog item is given more space than its minimal size plus +its border. Depending on what flags are used for the respective dialog +item, the dialog item can be made to fill out the available space +entirely, i.e. it will grow to a size larger than the minimal size, or +it will be moved to either the centre of the available space or to +either side of the space. The following sample shows a listbox and +three buttons in a horizontal box sizer; one button is centred, one is +aligned at the top, one is aligned at the bottom: + +[Need graphics] + + +A stretch factor +~~~~~~~~~~~~~~~~ + +If a sizer contains more than one child and it is offered more space +than its children and their borders need, the question arises how to +distribute the surplus space among the children. For this purpose, a +stretch factor may be assigned to each child, where the default value +of 0 indicates that the child will not get more space than its +requested minimum size. A value of more than zero is interpreted in +relation to the sum of all stretch factors in the children of the +respective sizer, i.e. if two children get a stretch factor of 1, they +will get half the extra space each independent of whether one control +has a minimal sizer inferior to the other or not. The following +sample shows a dialog with three buttons, the first one has a stretch +factor of 1 and thus gets stretched, whereas the other two buttons +have a stretch factor of zero and keep their initial width: + +[Need graphics] + +Within wxDesigner, this stretch factor gets set from the Option menu. + + +BoxSizer +-------- + +BoxSizer can lay out its children either vertically or horizontally, +depending on what flag is being used in its constructor. When using a +vertical sizer, each child can be centered, aligned to the right or +aligned to the left. Correspondingly, when using a horizontal sizer, +each child can be centered, aligned at the bottom or aligned at the +top. The stretch factor described in the last paragraph is used for +the main orientation, i.e. when using a horizontal box sizer, the +stretch factor determines how much the child can be stretched +horizontally. The following sample shows the same dialog as in the +last sample, only the box sizer is a vertical box sizer now: + +[Need graphics] + + +StaticBoxSizer +-------------- + +StaticBoxSixer is the same as a BoxSizer, but surrounded by a static +box. Here is a sample: + +[Need graphics] + + +GridSizer +--------- + +GridSizer is a two-dimensional sizer. All children are given the same +size, which is the minimal size required by the biggest child, in this +case the text control in the left bottom border. Either the number of +columns or the number or rows is fixed and the grid sizer will grow in +the respectively other orientation if new children are added: + +[Need graphics] + + +FlexGridSizer +------------- + +Another two-dimensional sizer derived from GridSizer. The width of +each column and the height of each row are calculated individually +according the minimal requirements from the respectively biggest +child. Additionally, columns and rows can be declared to be +stretchable if the sizer is assigned a size different from that which +it requested. The following sample shows the same dialog as the one +above, but using a flex grid sizer: + +[Need graphics] + + +NotebookSizer +------------- + +NotebookSizer can be used with notebooks. It calculates the size of +each notebook page and sets the size of the notebook to the size of +the biggest page plus some extra space required for the notebook tabs +and decorations. + +[Need graphics] + + +Programming with BoxSizer +------------------------- + +The basic idea behind a BoxSizer is that windows will most often be +laid out in rather simple basic geometry, typically in a row or a +column or several hierarchies of either. + +As an example, we will construct a dialog that will contain a text +field at the top and two buttons at the bottom. This can be seen as a +top-hierarchy column with the text at the top and buttons at the +bottom and a low-hierarchy row with an OK button to the left and a +Cancel button to the right. In many cases (particularly dialogs under +Unix and normal frames) the main window will be resizable by the user +and this change of size will have to get propagated to its children. +In our case, we want the text area to grow with the dialog, whereas +the button shall have a fixed size. In addition, there will be a thin +border around all controls to make the dialog look nice and - to make +matter worse - the buttons shall be centred as the width of the dialog +changes. + +It is the unique feature of a box sizer, that it can grow in both +directions (height and width) but can distribute its growth in the +main direction (horizontal for a row) unevenly among its children. In +our example case, the vertical sizer is supposed to propagate all its +height changes to only the text area, not to the button area. This is +determined by the proportion parameter when adding a window (or +another sizer) to a sizer. It is interpreted as a weight factor, +i.e. it can be zero, indicating that the window may not be resized at +all, or above zero. If several windows have a value above zero, the +value is interpreted relative to the sum of all weight factors of the +sizer, so when adding two windows with a value of 1, they will both +get resized equally much and each half as much as the sizer owning +them. + +Then what do we do when a column sizer changes its width? This +behaviour is controlled by flags (the second parameter of the Add() +function): zero or no flag indicates that the window will preserve it +is original size, wx.GROW flag (same as wx.EXPAND) forces the window +to grow with the sizer, and wx.SHAPED flag tells the window to change +it is size proportionally, preserving original aspect ratio. When +wx.GROW flag is not used, the item can be aligned within available +space. wx.ALIGN_LEFT, wx.ALIGN_TOP, wx.ALIGN_RIGHT, wx.ALIGN_BOTTOM, +wx.ALIGN_CENTER_HORIZONTAL and wx.ALIGN_CENTER_VERTICAL do what they +say. wx.ALIGN_CENTRE (same as wx.ALIGN_CENTER) is defined as +(``wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL``). Default +alignment is ``wx.ALIGN_LEFT | wx.ALIGN_TOP``. + +As mentioned above, any window belonging to a sizer may have border, +and it can be specified which of the four sides may have this border, +using the wx.TOP, wx.LEFT, wx.RIGHT and wx.BOTTOM constants or wx.ALL +for all directions (and you may also use wx.NORTH, wx.WEST etc +instead). These flags can be used in combination with the alignment +flags above as the second parameter of the Add() method using the +binary or operator (``|``). The sizer of the border also must be made +known, and it is the third parameter in the Add() method. This means, +that the entire behaviour of a sizer and its children can be +controlled by the three parameters of the Add() method. + +[Show code and graphic here.] + + +Programming with GridSizer +-------------------------- + +GridSizer is a sizer which lays out its children in a two-dimensional +table with all table fields having the same size, i.e. the width of +each field is the width of the widest child, the height of each field +is the height of the tallest child. + +[Show code and graphic here.] + + +Programming with FlexGridSizer +------------------------------ + +FlexGridSizer is a sizer which lays out its children in a +two-dimensional table with all table fields in one row having the same +height and all fields in one column having the same width, but all +rows or all columns are not necessarily the same height or width as in +the GridSizer. + +[Show code and graphic here.] + + +Programming with NotebookSizer +------------------------------ + +NotebookSizer is a specialized sizer to make sizers work in connection +with using notebooks. This sizer is different from any other sizer as +you must not add any children to it - instead, it queries the notebook +class itself. The only thing this sizer does is to determine the size +of the biggest page of the notebook and report an adjusted minimal +size to a more toplevel sizer. + +In order to query the size of notebook page, this page needs to have +its own sizer, otherwise the NotebookSizer will ignore it. Notebook +pages get their sizer by assigning one to them using SetSizer() and +setting the auto-layout option to True using SetAutoLayout(). Here is +one example showing how to add a notebook page that the notebook sizer +is aware of: + +[Show code and graphic here.] + + +Programming with StaticBoxSizer +------------------------------- + +StaticBoxSizer is a sizer derived from BoxSizer but adds a static box +around the sizer. Note that this static box has to be created +separately. + +[Show code and graphic here.] + + +Dialog.CreateButtonSizer +------------------------ + +As a convenience, the Dialog class has a CreateButtonSizer(flags) +method that can be used to create a standard button sizer in which +standard buttons are displayed. The following flags can be passed to +this method: + +============= ======================================================= +wx.YES_NO add Yes/No subpanel +wx.YES return wx.ID_YES +wx.NO return wx.ID_NO +wx.NO_DEFAULT make the wx.NO button the default, otherwise wx.YES or + wx.OK button will be default +wx.OK return wx.ID_OK +wx.CANCEL return wx.ID_CANCEL +wx.HELP return wx.ID_HELP +wx.FORWARD return wx.ID_FORWARD +wx.BACKWARD return wx.ID_BACKWARD +wx.SETUP return wx.ID_SETUP +wx.MORE return wx.ID_MORE +============= ======================================================= + + +Date and time classes overview +============================== + +wxPython provides a set of powerful classes to work with dates and +times. Some of the supported features of the DateTime class are: + +============= ======================================================= + +Wide range The range of supported dates goes from about 4714 B.C. to + some 480 million years in the future. + +Precision Not using floating point calculations anywhere ensures that + the date calculations don't suffer from rounding + errors. + +Many features Not only all usual calculations with dates are + supported, but also more exotic week and year day + calculations, work day testing, standard astronomical + functions, conversion to and from strings in either + strict or free format. + + +Efficiency Objects of DateTime are small (8 bytes) and working + with them is fast + +============= ======================================================= + + +All date/time classes at a glance +--------------------------------- + +There are 3 main classes: except DateTime itself which represents an +absolute moment in time, there are also two classes - TimeSpan and +DateSpan which represent the intervals of time. + +There are also helper classes which are used together with DateTime: +DateTimeHolidayAuthority which is used to determine whether a given +date is a holiday or not and DateTimeWorkDays which is a derivation of +this class for which (only) Saturdays and Sundays are the holidays. +See more about these classes in the discussion of the holidays. + + +DateTime characteristics +------------------------ + +DateTime stores the time as a signed number of milliseconds since the +Epoch which is fixed, by convention, to Jan 1, 1970 - however this is +not visible to the class users (in particular, dates prior to the +Epoch are handled just as well (or as bad) as the dates after it). +But it does mean that the best resolution which can be achieved with +this class is 1 millisecond. + +The size of DateTime object is 8 bytes because it is represented as a +64 bit integer. The resulting range of supported dates is thus +approximatively 580 million years, but due to the current limitations +in the Gregorian calendar support, only dates from Nov 24, 4714BC are +supported (this is subject to change if there is sufficient interest +in doing it). + +Finally, the internal representation is time zone independent (always +in GMT) and the time zones only come into play when a date is broken +into year/month/day components. See more about timezones below. + +Currently, the only supported calendar is Gregorian one (which is used +even for the dates prior to the historic introduction of this calendar +which was first done on Oct 15, 1582 but is, generally speaking, +country, and even region, dependent). Future versions will probably +have Julian calendar support as well and support for other calendars +(Maya, Hebrew, Chinese...) is not ruled out. + + +Difference between DateSpan and TimeSpan +---------------------------------------- + +While there is only one logical way to represent an absolute moment in +the time (and hence only one DateTime class), there are at least two +methods to describe a time interval. + +First, there is the direct and self-explaining way implemented by +TimeSpan: it is just a difference in milliseconds between two moments +in time. Adding or subtracting such an interval to DateTime is always +well-defined and is a fast operation. + +But in daily life other, calendar-dependent time interval +specifications are used. For example, 'one month later' is commonly +used. However, it is clear that this is not the same as TimeSpan of +60*60*24*31 seconds because 'one month later' Feb 15 is Mar 15 and not +Mar 17 or Mar 16 (depending on whether the year is leap or not). + +This is why there is another class for representing such intervals +called DateSpan. It handles these sort of operations in the most +natural way possible, but note that manipulating with intervals of +this kind is not always well-defined. Consider, for example, Jan 31 + +'1 month': this will give Feb 28 (or 29), i.e. the last day of +February and not the non-existent Feb 31. Of course, this is what is +usually wanted, but you still might be surprised to notice that now +subtracting back the same interval from Feb 28 will result in Jan 28 +and not Jan 31 we started with! + +So, unless you plan to implement some kind of natural language parsing +in the program, you should probably use TimeSpan instead of DateSpan +(which is also more efficient). However, DateSpan may be very useful +in situations when you do need to understand what 'in a month' means +(of course, it is just DateTime.Now() + DateSpan.Month()). + + +Date arithmetics +---------------- + +Many different operations may be performed with the dates, however not +all of them make sense. For example, multiplying a date by a number +is an invalid operation, even though multiplying either of the time +span classes by a number is perfectly valid. + +Here is what can be done: + +============== ====================================================== + +Addition a TimeSpan or DateSpan can be added to DateTime resulting in + a new DateTime object and also 2 objects of the same + span class can be added together giving another object + of the same class. + + +Subtraction the same types of operations as above are allowed and, + additionally, a difference between two DateTime + objects can be taken and this will yield TimeSpan. + +Multiplication a TimeSpan or DateSpan object can be multiplied by an + integer number resulting in an object of the same + type. + + +Unary minus a TimeSpan or DateSpan object may finally be negated + giving an interval of the same magnitude but of + opposite time direction. + +============== ====================================================== + + +Time zone considerations +------------------------ + +Although the time is always stored internally in GMT, you will usually +work in the local time zone. Because of this, all DateTime +constructors and setters which take the broken down date assume that +these values are for the local time zone. Thus, DateTime(1, +DateTime.Jan, 1970) will not correspond to the DateTime Epoch unless +you happen to live in the UK. + +All methods returning the date components (year, month, day, hour, +minute, second...) will also return the correct values for the local +time zone by default. So, generally, doing the natural things will +lead to natural and correct results. + +If you only want to do this, you may safely skip the rest of this +section. However, if you want to work with different time zones, you +should read it to the end. + +In this (rare) case, you are still limited to the local time zone when +constructing DateTime objects, i.e. there is no way to construct a +DateTime corresponding to the given date in, say, Pacific Standard +Time. To do it, you will need to call ToTimezone or MakeTimezone +methods to adjust the date for the target time zone. There are also +special versions of these functions ToGMT and MakeGMT for the most +common case - when the date should be constructed in GMT. + +You also can just retrieve the value for some time zone without +converting the object to it first. For this you may pass TimeZone +argument to any of the methods which are affected by the time zone +(all methods getting date components and the date formatting ones, for +example). In particular, the Format() family of methods accepts a +TimeZone parameter and this allows to simply print time in any time +zone. + +To see how to do it, the last issue to address is how to construct a +TimeZone object which must be passed to all these methods. First of +all, you may construct it manually by specifying the time zone offset +in seconds from GMT, but usually you will just use one of the symbolic +time zone names and let the conversion constructor do the +job. I.e. you would just write + +wxDateTime dt(...whatever...); +printf("The time is %s in local time zone", dt.FormatTime().c_str()); +printf("The time is %s in GMT", dt.FormatTime(wxDateTime::GMT).c_str()); + + +Daylight saving time (DST) +-------------------------- + +DST (a.k.a. 'summer time') handling is always a delicate task which is +better left to the operating system which is supposed to be configured +by the administrator to behave correctly. Unfortunately, when doing +calculations with date outside of the range supported by the standard +library, we are forced to deal with these issues ourselves. + +Several functions are provided to calculate the beginning and end of +DST in the given year and to determine whether it is in effect at the +given moment or not, but they should not be considered as absolutely +correct because, first of all, they only work more or less correctly +for only a handful of countries (any information about other ones +appreciated!) and even for them the rules may perfectly well change in +the future. + +The time zone handling methods use these functions too, so they are +subject to the same limitations. + + +DateTime and Holidays +--------------------- + +[TODO] + + +Classes by category +=================== + +Not done yet. + + +ID constants +============ + +wxPython provides the following predefined ID constants: + +ID_ABORT +ID_ABOUT +ID_ANY +ID_APPLY +ID_BACKWARD +ID_CANCEL +ID_CLEAR +ID_CLOSE +ID_CLOSE_ALL +ID_CONTEXT_HELP +ID_COPY +ID_CUT +ID_DEFAULT +ID_DUPLICATE +ID_EXIT +ID_FILE1 +ID_FILE2 +ID_FILE3 +ID_FILE4 +ID_FILE5 +ID_FILE6 +ID_FILE7 +ID_FILE8 +ID_FILE9 +ID_FILTERLISTCTRL +ID_FIND +ID_FORWARD +ID_HELP +ID_HELP_COMMANDS +ID_HELP_CONTENTS +ID_HELP_CONTEXT +ID_HELP_PROCEDURES +ID_IGNORE +ID_MORE +ID_NEW +ID_NO +ID_NOTOALL +ID_OK +ID_OPEN +ID_PASTE +ID_PREVIEW +ID_PRINT +ID_PRINT_SETUP +ID_REDO +ID_RESET +ID_RETRY +ID_REVERT +ID_SAVE +ID_SAVEAS +ID_SELECTALL +ID_SEPARATOR +ID_SETUP +ID_STATIC +ID_TREECTRL +ID_UNDO +ID_YES +ID_YESTOALL + + +Source Document +=============== + +The source document is named wxPython.txt and is located in the +PyCrust/wxd directory. It is written using a fantastic formatting +convention called reStructuredText. The wxPython.html file is created +using the Docutils utilities, which can turn reStructuredText +documents into html, xml, pdf, and even OpenOffice files. + +Some items in the source text file look like this:: + + .. This is text from the wxWindows documentation that needs to be + translated into something appropriate for the wxPython version. + The two dots followed by uniformly indented text turns this + paragraph into a reStructuredText comment, so it doesn't appear + in any output file, such as the html file. + +They have been commented out and are awaiting editorial review and a +rewrite so that they make sense in the context of wxPython. Feel free +to send me suggestions for rewording these, or any other parts of this +document that you think need improving. I will be eternally grateful +to you and will show my gratitude by adding your name to the list of +contributors. (Contributors who also send me gifts of coffee, +chocolate, or currency will have their names listed in bold.) + + +Contributors +============ + +Individuals who contributed to this documentation (in order by last +name): + +* Robin Dunn +* Patrick K. O'Brien +* Robert Roebling +* Julian Smart +* Vadim Zeitlin + + +License +======= + +This document began as a translation of the wxWindows documentation. +As such, it adheres to the same license, which is provided here: + +.. include:: LICENSE.txt + :literal: diff --git a/wxPython/wxPython/lib/PyCrust/wxd/wx_.py b/wxPython/wxPython/py/wxd/wx_.py similarity index 89% rename from wxPython/wxPython/lib/PyCrust/wxd/wx_.py rename to wxPython/wxPython/py/wxd/wx_.py index 492d623714..90fea4db74 100644 --- a/wxPython/wxPython/lib/PyCrust/wxd/wx_.py +++ b/wxPython/wxPython/py/wxd/wx_.py @@ -1,4 +1,8 @@ """Decorator classes for documentation and shell scripting. + +Information contained in this module, and all modules imported by this +module, is covered by the wxWindows Free Documentation Licence. See +the LICENSE.txt file for details. """ __author__ = "Patrick K. O'Brien "