From 5f933e58ef693eb6222bcd95786182502e8a0592 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Wed, 9 Apr 2003 17:44:53 +0000 Subject: [PATCH] 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. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/WX_2_4_BRANCH@20104 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- wxPython/CHANGES.txt | 12 +- wxPython/scripts/CreateBatchFiles.py | 16 +- wxPython/scripts/pyalacarte | 4 + wxPython/scripts/pyalamode | 4 + wxPython/scripts/pycrust | 2 +- wxPython/scripts/pycwrap.bat | 3 - wxPython/scripts/pyshell | 2 +- wxPython/scripts/{pycwrap => pywrap} | 2 +- wxPython/wx/py/PyAlaCarte.py | 3 + wxPython/wx/py/PyAlaMode.py | 3 + wxPython/wx/py/PyCrust.py | 3 + wxPython/wx/py/PyFilling.py | 3 + wxPython/wx/py/PyShell.py | 3 + wxPython/wx/py/PyWrap.py | 3 + wxPython/wx/py/__init__.py | 3 + wxPython/wxPython/lib/PyCrust/.cvsignore | 3 - wxPython/wxPython/lib/PyCrust/PyCrustApp.py | 66 +- wxPython/wxPython/lib/PyCrust/PyFillingApp.py | 41 +- wxPython/wxPython/lib/PyCrust/PyShellApp.py | 67 +- wxPython/wxPython/lib/PyCrust/__init__.py | 6 +- wxPython/wxPython/lib/PyCrust/crust.py | 169 +-- wxPython/wxPython/lib/PyCrust/filling.py | 435 +----- wxPython/wxPython/lib/PyCrust/shell.py | 1186 +---------------- wxPython/wxPython/lib/PyCrust/shellmenu.py | 226 ---- wxPython/wxPython/lib/PyCrust/wrap.py | 51 +- wxPython/wxPython/lib/PyCrust/wxd/Sizers.py | 488 ------- .../wxPython/{lib/PyCrust => py}/CHANGES.txt | 29 + wxPython/wxPython/py/MANUAL.txt | 76 ++ .../{lib/PyCrust/PyCrust.ico => py/Py.ico} | Bin wxPython/wxPython/py/PyAlaCarte.py | 44 + wxPython/wxPython/py/PyAlaMode.py | 44 + wxPython/wxPython/py/PyCrust.py | 71 + wxPython/wxPython/py/PyFilling.py | 46 + wxPython/wxPython/py/PyShell.py | 72 + wxPython/wxPython/py/PyWrap.py | 57 + .../wxPython/{lib/PyCrust => py}/README.txt | 0 wxPython/wxPython/py/__init__.py | 20 + wxPython/wxPython/py/base.py | 195 +++ wxPython/wxPython/py/buffer.py | 140 ++ wxPython/wxPython/py/crust.py | 182 +++ wxPython/wxPython/py/default.css | 208 +++ .../{lib/PyCrust => py}/dispatcher.py | 0 wxPython/wxPython/py/document.py | 46 + wxPython/wxPython/py/editor.py | 733 ++++++++++ wxPython/wxPython/py/filling.py | 335 +++++ wxPython/wxPython/py/frame.py | 348 +++++ .../wxPython/{lib/PyCrust => py}/images.py | 0 .../{lib/PyCrust => py}/interpreter.py | 0 .../{lib/PyCrust => py}/introspect.py | 13 +- .../wxPython/{lib/PyCrust => py}/pseudo.py | 0 wxPython/wxPython/py/shell.py | 1021 ++++++++++++++ .../wxPython/py/tests/test_interpreter.py | 82 ++ wxPython/wxPython/py/tests/test_introspect.py | 862 ++++++++++++ wxPython/wxPython/py/tests/test_pseudo.py | 81 ++ wxPython/wxPython/py/tests/test_version.py | 49 + wxPython/wxPython/py/tests/testall.py | 25 + .../wxPython/{lib/PyCrust => py}/version.py | 2 +- .../{lib/PyCrust => py}/wxd/Accelerators.py | 0 .../wxPython/{lib/PyCrust => py}/wxd/App.py | 0 .../wxPython/{lib/PyCrust => py}/wxd/Base.py | 0 .../{lib/PyCrust => py}/wxd/ClipDragDrop.py | 0 .../{lib/PyCrust => py}/wxd/Config.py | 0 .../{lib/PyCrust => py}/wxd/Controls.py | 22 +- .../{lib/PyCrust => py}/wxd/DataStructures.py | 0 .../{lib/PyCrust => py}/wxd/DateTime.py | 0 .../{lib/PyCrust => py}/wxd/Dialogs.py | 0 .../{lib/PyCrust => py}/wxd/Drawing.py | 0 .../{lib/PyCrust => py}/wxd/Errors.py | 0 .../{lib/PyCrust => py}/wxd/EventFunctions.py | 0 .../{lib/PyCrust => py}/wxd/Events.py | 0 .../{lib/PyCrust => py}/wxd/FileSystem.py | 0 .../{lib/PyCrust => py}/wxd/Frames.py | 109 +- .../{lib/PyCrust => py}/wxd/Functions.py | 0 .../wxPython/{lib/PyCrust => py}/wxd/Help.py | 0 .../{lib/PyCrust => py}/wxd/ImageHandlers.py | 0 .../{lib/PyCrust => py}/wxd/Joystick.py | 0 wxPython/wxPython/py/wxd/LICENSE.txt | 60 + .../PyCrust => py}/wxd/LayoutConstraints.py | 0 .../{lib/PyCrust => py}/wxd/Logging.py | 0 .../wxPython/{lib/PyCrust => py}/wxd/Menus.py | 0 .../{lib/PyCrust => py}/wxd/MimeTypes.py | 0 .../wxPython/{lib/PyCrust => py}/wxd/Misc.py | 0 .../wxPython/{lib/PyCrust => py}/wxd/Panel.py | 0 .../{lib/PyCrust => py}/wxd/Parameters.py | 1 + .../{lib/PyCrust => py}/wxd/Printing.py | 0 .../{lib/PyCrust => py}/wxd/Process.py | 0 .../{lib/PyCrust => py}/wxd/SashSplitter.py | 0 wxPython/wxPython/py/wxd/Sizers.py | 605 +++++++++ .../{lib/PyCrust => py}/wxd/Streams.py | 0 .../PyCrust => py}/wxd/StyledTextConstants.py | 0 .../{lib/PyCrust => py}/wxd/Threading.py | 0 .../{lib/PyCrust => py}/wxd/ToolBar.py | 0 .../wxPython/{lib/PyCrust => py}/wxd/Tree.py | 57 +- .../{lib/PyCrust => py}/wxd/Validators.py | 0 .../{lib/PyCrust => py}/wxd/Window.py | 0 .../{lib/PyCrust => py}/wxd/__init__.py | 0 .../wxPython/{lib/PyCrust => py}/wxd/d_stc.py | 0 .../wxPython/{lib/PyCrust => py}/wxd/d_wx.py | 0 .../{lib/PyCrust => py}/wxd/decorator.py | 0 wxPython/wxPython/py/wxd/default.css | 208 +++ .../wxPython/{lib/PyCrust => py}/wxd/gen.py | 0 .../wxPython/{lib/PyCrust => py}/wxd/stc_.py | 0 wxPython/wxPython/py/wxd/wxPython.html | 1006 ++++++++++++++ wxPython/wxPython/py/wxd/wxPython.txt | 1032 ++++++++++++++ .../wxPython/{lib/PyCrust => py}/wxd/wx_.py | 4 + 105 files changed, 7892 insertions(+), 2797 deletions(-) create mode 100755 wxPython/scripts/pyalacarte create mode 100755 wxPython/scripts/pyalamode delete mode 100755 wxPython/scripts/pycwrap.bat rename wxPython/scripts/{pycwrap => pywrap} (66%) create mode 100644 wxPython/wx/py/PyAlaCarte.py create mode 100644 wxPython/wx/py/PyAlaMode.py create mode 100644 wxPython/wx/py/PyCrust.py create mode 100644 wxPython/wx/py/PyFilling.py create mode 100644 wxPython/wx/py/PyShell.py create mode 100644 wxPython/wx/py/PyWrap.py create mode 100644 wxPython/wx/py/__init__.py delete mode 100644 wxPython/wxPython/lib/PyCrust/.cvsignore delete mode 100644 wxPython/wxPython/lib/PyCrust/shellmenu.py delete mode 100644 wxPython/wxPython/lib/PyCrust/wxd/Sizers.py rename wxPython/wxPython/{lib/PyCrust => py}/CHANGES.txt (95%) create mode 100644 wxPython/wxPython/py/MANUAL.txt rename wxPython/wxPython/{lib/PyCrust/PyCrust.ico => py/Py.ico} (100%) create mode 100755 wxPython/wxPython/py/PyAlaCarte.py create mode 100755 wxPython/wxPython/py/PyAlaMode.py create mode 100755 wxPython/wxPython/py/PyCrust.py create mode 100755 wxPython/wxPython/py/PyFilling.py create mode 100755 wxPython/wxPython/py/PyShell.py create mode 100755 wxPython/wxPython/py/PyWrap.py rename wxPython/wxPython/{lib/PyCrust => py}/README.txt (100%) create mode 100644 wxPython/wxPython/py/__init__.py create mode 100644 wxPython/wxPython/py/base.py create mode 100644 wxPython/wxPython/py/buffer.py create mode 100644 wxPython/wxPython/py/crust.py create mode 100644 wxPython/wxPython/py/default.css rename wxPython/wxPython/{lib/PyCrust => py}/dispatcher.py (100%) create mode 100644 wxPython/wxPython/py/document.py create mode 100644 wxPython/wxPython/py/editor.py create mode 100644 wxPython/wxPython/py/filling.py create mode 100644 wxPython/wxPython/py/frame.py rename wxPython/wxPython/{lib/PyCrust => py}/images.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/interpreter.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/introspect.py (97%) rename wxPython/wxPython/{lib/PyCrust => py}/pseudo.py (100%) create mode 100644 wxPython/wxPython/py/shell.py create mode 100644 wxPython/wxPython/py/tests/test_interpreter.py create mode 100644 wxPython/wxPython/py/tests/test_introspect.py create mode 100644 wxPython/wxPython/py/tests/test_pseudo.py create mode 100644 wxPython/wxPython/py/tests/test_version.py create mode 100644 wxPython/wxPython/py/tests/testall.py rename wxPython/wxPython/{lib/PyCrust => py}/version.py (94%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Accelerators.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/App.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Base.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/ClipDragDrop.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Config.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Controls.py (98%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/DataStructures.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/DateTime.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Dialogs.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Drawing.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Errors.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/EventFunctions.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Events.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/FileSystem.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Frames.py (67%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Functions.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Help.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/ImageHandlers.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Joystick.py (100%) create mode 100644 wxPython/wxPython/py/wxd/LICENSE.txt rename wxPython/wxPython/{lib/PyCrust => py}/wxd/LayoutConstraints.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Logging.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Menus.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/MimeTypes.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Misc.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Panel.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Parameters.py (98%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Printing.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Process.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/SashSplitter.py (100%) create mode 100644 wxPython/wxPython/py/wxd/Sizers.py rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Streams.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/StyledTextConstants.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Threading.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/ToolBar.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Tree.py (82%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Validators.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/Window.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/__init__.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/d_stc.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/d_wx.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/decorator.py (100%) create mode 100644 wxPython/wxPython/py/wxd/default.css rename wxPython/wxPython/{lib/PyCrust => py}/wxd/gen.py (100%) rename wxPython/wxPython/{lib/PyCrust => py}/wxd/stc_.py (100%) create mode 100644 wxPython/wxPython/py/wxd/wxPython.html create mode 100644 wxPython/wxPython/py/wxd/wxPython.txt rename wxPython/wxPython/{lib/PyCrust => py}/wxd/wx_.py (89%) 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 "