# Licence: wxWindows license
#----------------------------------------------------------------------------
-import sys, os, time
+# FIXME List:
+# * Problems with flickering related to ERASE_BACKGROUND
+# and the splitters. Might be a problem with this 2.5 beta...?
+# UPDATE: can't see on 2.5.2 GTK - maybe just a faster machine :)
+# * Demo Code menu?
+# * Annoying switching between tabs and resulting flicker
+# how to replace a page in the notebook without deleting/adding?
+# Where is SetPage!? tried freeze...tried reparent of dummy panel....
+
+# TODO List:
+# * UI design more prefessional
+# * save file positions (new field in demoModules) (@ LoadDemoSource)
+# * Update main overview
+
+# * Why don't we move _treeList into a separate module
+
+import sys, os, time, traceback, types
import wx # This module uses the new wx namespace
import wx.html
import images
+# For debugging
+##wx.Trap();
+##print "wx.VERSION_STRING = %s (%s)" % (wx.VERSION_STRING, wx.USE_UNICODE and 'unicode' or 'ansi')
+##print "pid:", os.getpid()
+##raw_input("Press Enter...")
+
#---------------------------------------------------------------------------
_treeList = [
# new stuff
- ('Recent Additions', [
- 'wxVListBox',
+ ('Recent Additions/Updates', [
+ 'AnalogClock',
+ 'CheckListCtrlMixin',
+ 'RichTextCtrl',
+ 'Treebook',
+ 'Toolbook',
]),
# managed windows == things with a (optional) caption you can close
- ('Base Frames and Dialogs', [
- 'wxDialog',
- 'wxFrame',
- 'wxMDIWindows',
- 'wxMiniFrame',
- 'wxWizard',
+ ('Frames and Dialogs', [
+ 'Dialog',
+ 'Frame',
+ 'MDIWindows',
+ 'MiniFrame',
+ 'Wizard',
]),
# the common dialogs
('Common Dialogs', [
- 'wxColourDialog',
- 'wxDirDialog',
- 'wxFileDialog',
- 'wxFileDialog_Save',
- 'wxFindReplaceDialog',
- 'wxFontDialog',
- 'wxMessageDialog',
- 'wxPageSetupDialog',
- 'wxPrintDialog',
- 'wxProgressDialog',
- 'wxSingleChoiceDialog',
- 'wxTextEntryDialog',
+ 'ColourDialog',
+ 'DirDialog',
+ 'FileDialog',
+ 'FindReplaceDialog',
+ 'FontDialog',
+ 'MessageDialog',
+ 'MultiChoiceDialog',
+ 'PageSetupDialog',
+ 'PrintDialog',
+ 'ProgressDialog',
+ 'SingleChoiceDialog',
+ 'TextEntryDialog',
]),
# dialogs from libraries
('More Dialogs', [
- 'ErrorDialogs',
'ImageBrowser',
- 'wxMultipleChoiceDialog',
- 'wxScrolledMessageDialog',
+ 'ScrolledMessageDialog',
]),
# core controls
('Core Windows/Controls', [
+ 'BitmapButton',
+ 'Button',
+ 'CheckBox',
+ 'CheckListBox',
+ 'Choice',
+ 'ComboBox',
+ 'Gauge',
+ 'Grid',
+ 'Grid_MegaExample',
+ 'ListBox',
+ 'ListCtrl',
+ 'ListCtrl_virtual',
+ 'ListCtrl_edit',
+ 'Menu',
'PopupMenu',
- 'wxButton',
- 'wxCheckBox',
- 'wxCheckListBox',
- 'wxChoice',
- 'wxComboBox',
- 'wxGauge',
- 'wxGenericDirCtrl',
- 'wxGrid',
- 'wxGrid_MegaExample',
- 'wxListBox',
- 'wxListCtrl',
- 'wxListCtrl_virtual',
- 'wxMenu',
- 'wxNotebook',
- 'wxPopupWindow',
- 'wxRadioBox',
- 'wxRadioButton',
- 'wxSashWindow',
- 'wxScrolledWindow',
- 'wxSlider',
- 'wxSpinButton',
- 'wxSpinCtrl',
- 'wxSplitterWindow',
- 'wxStaticBitmap',
- 'wxStaticText',
- 'wxStatusBar',
- 'wxTextCtrl',
- 'wxToggleButton',
- 'wxToolBar',
- 'wxTreeCtrl',
- 'wxValidator',
+ 'PopupWindow',
+ 'RadioBox',
+ 'RadioButton',
+ 'SashWindow',
+ 'ScrolledWindow',
+ 'Slider',
+ 'SpinButton',
+ 'SpinCtrl',
+ 'SplitterWindow',
+ 'StaticBitmap',
+ 'StaticBox',
+ 'StaticText',
+ 'StatusBar',
+ 'StockButtons',
+ 'TextCtrl',
+ 'ToggleButton',
+ 'ToolBar',
+ 'TreeCtrl',
+ 'Validator',
+ ]),
+
+ ('"Book" Controls', [
+ 'Choicebook',
+ 'Listbook',
+ 'Notebook',
+ 'Toolbook',
+ 'Treebook',
]),
- # controls coming from other librairies
- ('More Windows/Controls', [
- #'wxFloatBar', deprecated
- #'wxMVCTree', deprecated
- #'wxRightTextCtrl', deprecated as we have wxTE_RIGHT now.
- 'AnalogClockWindow',
+ ('Custom Controls', [
+ 'AnalogClock',
'ColourSelect',
+ 'Editor',
+ 'GenericButtons',
+ 'GenericDirCtrl',
+ 'LEDNumberCtrl',
+ 'MultiSash',
+ 'PopupControl',
+ 'PyColourChooser',
+ 'TreeListCtrl',
+ ]),
+
+ # controls coming from other libraries
+ ('More Windows/Controls', [
+ 'ActiveX_FlashWindow',
+ 'ActiveX_IEHtmlWindow',
+ 'ActiveX_PDFWindow',
+ #'RightTextCtrl', deprecated as we have wxTE_RIGHT now.
+ 'Calendar',
+ 'CalendarCtrl',
+ 'CheckListCtrlMixin',
'ContextHelp',
+ 'DatePickerCtrl',
+ 'DynamicSashWindow',
+ 'EditableListBox',
'FancyText',
'FileBrowseButton',
- 'GenericButtons',
+ 'FloatBar',
+ 'FloatCanvas',
+ 'FoldPanelBar',
+ 'GIFAnimationCtrl',
+ 'HtmlWindow',
+ 'HyperLinkCtrl',
+ 'IntCtrl',
+ 'MVCTree',
'MaskedEditControls',
- 'PyShell',
+ 'MaskedNumCtrl',
+ 'MediaCtrl',
+ 'MultiSplitterWindow',
'PyCrust',
+ 'PyPlot',
+ 'PyShell',
+ 'RichTextCtrl',
+ 'ScrolledPanel',
'SplitTree',
+ 'StyledTextCtrl_1',
+ 'StyledTextCtrl_2',
'TablePrint',
'Throbber',
- 'wxCalendar',
- 'wxCalendarCtrl',
- 'wxPyColourChooser',
- 'wxDynamicSashWindow',
- 'wxEditableListBox',
- 'wxEditor',
- 'wxHtmlWindow',
- 'wxIEHtmlWin',
- 'wxIntCtrl',
- 'wxLEDNumberCtrl',
- 'wxMimeTypesManager',
- 'wxMultiSash',
- 'wxPopupControl',
- 'wxStyledTextCtrl_1',
- 'wxStyledTextCtrl_2',
- 'wxTimeCtrl',
- 'wxTreeListCtrl',
- 'wxVListBox',
+ 'Ticker',
+ 'TimeCtrl',
+ 'VListBox',
]),
# How to lay out the controls in a frame/dialog
('Window Layout', [
+ 'GridBagSizer',
'LayoutAnchors',
+ 'LayoutConstraints',
'Layoutf',
'RowColSizer',
+ 'ScrolledPanel',
'Sizers',
- 'wxLayoutConstraints',
- 'wxScrolledPanel',
- 'wxXmlResource',
- 'wxXmlResourceHandler',
+ 'XmlResource',
+ 'XmlResourceHandler',
+ 'XmlResourceSubclass',
]),
# ditto
('Process and Events', [
'EventManager',
- 'infoframe',
- 'OOR',
+ 'KeyEvents',
+ 'Process',
'PythonEvents',
'Threads',
- 'wxKeyEvents',
- 'wxProcess',
- 'wxTimer',
+ 'Timer',
+ ##'infoframe', # needs better explanation and some fixing
]),
# Clipboard and DnD
# Images
('Using Images', [
+ 'ArtProvider',
+ 'Cursor',
+ 'DragImage',
+ 'GIFAnimationCtrl',
+ 'Image',
+ 'ImageAlpha',
+ 'ImageFromStream',
+ 'Mask',
'Throbber',
- 'wxArtProvider',
- 'wxDragImage',
- 'wxImage',
- 'wxImageFromStream',
- 'wxMask',
]),
# Other stuff
('Miscellaneous', [
'ColourDB',
- 'DialogUnits',
+ ##'DialogUnits', # needs more explanations
'DrawXXXList',
+ 'FileHistory',
'FontEnumerator',
- 'NewNamespace',
+ 'GLCanvas',
+ 'Joystick',
+ 'MimeTypesManager',
+ 'MouseGestures',
+ 'OGL',
'PrintFramework',
'ShapedWindow',
- 'Throbber',
+ 'Sound',
+ 'StandardPaths',
'Unicode',
- 'wxFileHistory',
- 'wxJoystick',
- 'wxOGL',
- 'wxWave',
- ]),
-
- # need libs not coming with the demo
- ('Objects using an external library', [
- 'ActiveXWrapper_Acrobat',
- 'ActiveXWrapper_IE',
- 'wxGLCanvas',
- 'wxPlotCanvas',
]),
#---------------------------------------------------------------------------
+# Show how to derive a custom wxLog class
class MyLog(wx.PyLog):
def __init__(self, textCtrl, logTime=0):
self.logTime = logTime
def DoLogString(self, message, timeStamp):
- if self.logTime:
- message = time.strftime("%X", time.localtime(timeStamp)) + \
- ": " + message
+ #print message, timeStamp
+ #if self.logTime:
+ # message = time.strftime("%X", time.localtime(timeStamp)) + \
+ # ": " + message
if self.tc:
self.tc.AppendText(message + '\n')
def GetTip(self):
return "This is my tip"
+#---------------------------------------------------------------------------
+# A class to be used to simply display a message in the demo pane
+# rather than running the sample itself.
+
+class MessagePanel(wx.Panel):
+ def __init__(self, parent, message, caption='', flags=0):
+ wx.Panel.__init__(self, parent)
+
+ # Make widgets
+ if flags:
+ artid = None
+ if flags & wx.ICON_EXCLAMATION:
+ artid = wx.ART_WARNING
+ elif flags & wx.ICON_ERROR:
+ artid = wx.ART_ERROR
+ elif flags & wx.ICON_QUESTION:
+ artid = wx.ART_QUESTION
+ elif flags & wx.ICON_INFORMATION:
+ artid = wx.ART_INFORMATION
+
+ if artid is not None:
+ bmp = wx.ArtProvider.GetBitmap(artid, wx.ART_MESSAGE_BOX, (32,32))
+ icon = wx.StaticBitmap(self, -1, bmp)
+ else:
+ icon = (32,32) # make a spacer instead
+
+ if caption:
+ caption = wx.StaticText(self, -1, caption)
+ caption.SetFont(wx.Font(28, wx.SWISS, wx.NORMAL, wx.BOLD))
+
+ message = wx.StaticText(self, -1, message)
+
+ # add to sizers for layout
+ tbox = wx.BoxSizer(wx.VERTICAL)
+ if caption:
+ tbox.Add(caption)
+ tbox.Add((10,10))
+ tbox.Add(message)
+
+ hbox = wx.BoxSizer(wx.HORIZONTAL)
+ hbox.Add((10,10), 1)
+ hbox.Add(icon)
+ hbox.Add((10,10))
+ hbox.Add(tbox)
+ hbox.Add((10,10), 1)
+
+ box = wx.BoxSizer(wx.VERTICAL)
+ box.Add((10,10), 1)
+ box.Add(hbox, 0, wx.EXPAND)
+ box.Add((10,10), 2)
+
+ self.SetSizer(box)
+ self.Fit()
+
+
#---------------------------------------------------------------------------
# A class to be used to display source code in the demo. Try using the
-# wxSTC in the wxStyledTextCtrl_2 sample first, fall back to wxTextCtrl
+# wxSTC in the StyledTextCtrl_2 sample first, fall back to wxTextCtrl
# if there is an error, such as the stc module not being present.
+#
try:
- ##raise ImportError
+ ##raise ImportError # for testing the alternate implementation
from wx import stc
- from wxStyledTextCtrl_2 import PythonSTC
- class DemoCodeViewer(PythonSTC):
- def __init__(self, parent, ID):
- PythonSTC.__init__(self, parent, ID)
- self.SetEdgeMode(stc.STC_EDGE_NONE)
- self.SetSelBackground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT))
- self.SetSelForeground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT))
+ from StyledTextCtrl_2 import PythonSTC
+
+ class DemoCodeEditor(PythonSTC):
+ def __init__(self, parent):
+ PythonSTC.__init__(self, parent, -1, style=wx.BORDER_NONE)
+ self.SetUpEditor()
# Some methods to make it compatible with how the wxTextCtrl is used
def SetValue(self, value):
- self.SetReadOnly(False)
+ if wx.USE_UNICODE:
+ value = value.decode('iso8859_1')
self.SetText(value)
- self.SetReadOnly(True)
+ self.EmptyUndoBuffer()
+ self.SetSavePoint()
+
+ def IsModified(self):
+ return self.GetModify()
def Clear(self):
self.ClearAll()
def SetInsertionPoint(self, pos):
self.SetCurrentPos(pos)
+ self.SetAnchor(pos)
def ShowPosition(self, pos):
- self.GotoPos(pos)
+ line = self.LineFromPosition(pos)
+ #self.EnsureVisible(line)
+ self.GotoLine(line)
def GetLastPosition(self):
return self.GetLength()
+ def GetPositionFromLine(self, line):
+ return self.PositionFromLine(line)
+
def GetRange(self, start, end):
return self.GetTextRange(start, end)
self.SetSelectionStart(start)
self.SetSelectionEnd(end)
+ def SelectLine(self, line):
+ start = self.PositionFromLine(line)
+ end = self.GetLineEndPosition(line)
+ self.SetSelection(start, end)
+
+ def SetUpEditor(self):
+ """
+ This method carries out the work of setting up the demo editor.
+ It's seperate so as not to clutter up the init code.
+ """
+ import keyword
+
+ self.SetLexer(stc.STC_LEX_PYTHON)
+ self.SetKeyWords(0, " ".join(keyword.kwlist))
+
+ # Enable folding
+ self.SetProperty("fold", "1" )
+
+ # Highlight tab/space mixing (shouldn't be any)
+ self.SetProperty("tab.timmy.whinge.level", "1")
+
+ # Set left and right margins
+ self.SetMargins(2,2)
+
+ # Set up the numbers in the margin for margin #1
+ self.SetMarginType(1, wx.stc.STC_MARGIN_NUMBER)
+ # Reasonable value for, say, 4-5 digits using a mono font (40 pix)
+ self.SetMarginWidth(1, 40)
+
+ # Indentation and tab stuff
+ self.SetIndent(4) # Proscribed indent size for wx
+ self.SetIndentationGuides(True) # Show indent guides
+ self.SetBackSpaceUnIndents(True)# Backspace unindents rather than delete 1 space
+ self.SetTabIndents(True) # Tab key indents
+ self.SetTabWidth(4) # Proscribed tab size for wx
+ self.SetUseTabs(False) # Use spaces rather than tabs, or
+ # TabTimmy will complain!
+ # White space
+ self.SetViewWhiteSpace(False) # Don't view white space
+
+ # EOL: Since we are loading/saving ourselves, and the
+ # strings will always have \n's in them, set the STC to
+ # edit them that way.
+ self.SetEOLMode(wx.stc.STC_EOL_LF)
+ self.SetViewEOL(False)
+
+ # No right-edge mode indicator
+ self.SetEdgeMode(stc.STC_EDGE_NONE)
+
+ # Setup a margin to hold fold markers
+ self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
+ self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
+ self.SetMarginSensitive(2, True)
+ self.SetMarginWidth(2, 12)
+
+ # and now set up the fold markers
+ self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_BOXPLUSCONNECTED, "white", "black")
+ self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "black")
+ self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER, "white", "black")
+ self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNER, "white", "black")
+ self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "black")
+ self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_BOXPLUS, "white", "black")
+ self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_BOXMINUS, "white", "black")
+
+ # Global default style
+ if wx.Platform == '__WXMSW__':
+ self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
+ 'fore:#000000,back:#FFFFFF,face:Courier New,size:9')
+ else:
+ self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
+ 'fore:#000000,back:#FFFFFF,face:Courier,size:9')
+
+ # Clear styles and revert to default.
+ self.StyleClearAll()
+
+ # Following style specs only indicate differences from default.
+ # The rest remains unchanged.
+
+ # Line numbers in margin
+ self.StyleSetSpec(wx.stc.STC_STYLE_LINENUMBER,'fore:#000000,back:#99A9C2')
+ # Highlighted brace
+ self.StyleSetSpec(wx.stc.STC_STYLE_BRACELIGHT,'fore:#00009D,back:#FFFF00')
+ # Unmatched brace
+ self.StyleSetSpec(wx.stc.STC_STYLE_BRACEBAD,'fore:#00009D,back:#FF0000')
+ # Indentation guide
+ self.StyleSetSpec(wx.stc.STC_STYLE_INDENTGUIDE, "fore:#CDCDCD")
+
+ # Python styles
+ self.StyleSetSpec(wx.stc.STC_P_DEFAULT, 'fore:#000000')
+ # Comments
+ self.StyleSetSpec(wx.stc.STC_P_COMMENTLINE, 'fore:#008000,back:#F0FFF0')
+ self.StyleSetSpec(wx.stc.STC_P_COMMENTBLOCK, 'fore:#008000,back:#F0FFF0')
+ # Numbers
+ self.StyleSetSpec(wx.stc.STC_P_NUMBER, 'fore:#008080')
+ # Strings and characters
+ self.StyleSetSpec(wx.stc.STC_P_STRING, 'fore:#800080')
+ self.StyleSetSpec(wx.stc.STC_P_CHARACTER, 'fore:#800080')
+ # Keywords
+ self.StyleSetSpec(wx.stc.STC_P_WORD, 'fore:#000080,bold')
+ # Triple quotes
+ self.StyleSetSpec(wx.stc.STC_P_TRIPLE, 'fore:#800080,back:#FFFFEA')
+ self.StyleSetSpec(wx.stc.STC_P_TRIPLEDOUBLE, 'fore:#800080,back:#FFFFEA')
+ # Class names
+ self.StyleSetSpec(wx.stc.STC_P_CLASSNAME, 'fore:#0000FF,bold')
+ # Function names
+ self.StyleSetSpec(wx.stc.STC_P_DEFNAME, 'fore:#008080,bold')
+ # Operators
+ self.StyleSetSpec(wx.stc.STC_P_OPERATOR, 'fore:#800000,bold')
+ # Identifiers. I leave this as not bold because everything seems
+ # to be an identifier if it doesn't match the above criterae
+ self.StyleSetSpec(wx.stc.STC_P_IDENTIFIER, 'fore:#000000')
+
+ # Caret color
+ self.SetCaretForeground("BLUE")
+ # Selection background
+ self.SetSelBackground(1, '#66CCFF')
+
+ self.SetSelBackground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT))
+ self.SetSelForeground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT))
+
+ def RegisterModifiedEvent(self, eventHandler):
+ self.Bind(wx.stc.EVT_STC_CHANGE, eventHandler)
+
except ImportError:
- class DemoCodeViewer(wx.TextCtrl):
- def __init__(self, parent, ID):
- wx.TextCtrl.__init__(self, parent, ID, style =
- wx.TE_MULTILINE | wx.TE_READONLY |
- wx.HSCROLL | wx.TE_RICH2 | wx.TE_NOHIDESEL)
+ class DemoCodeEditor(wx.TextCtrl):
+ def __init__(self, parent):
+ wx.TextCtrl.__init__(self, parent, -1, style =
+ wx.TE_MULTILINE | wx.HSCROLL | wx.TE_RICH2 | wx.TE_NOHIDESEL)
+
+ def RegisterModifiedEvent(self, eventHandler):
+ self.Bind(wx.EVT_TEXT, eventHandler)
+
+ def SetReadOnly(self, flag):
+ self.SetEditable(not flag)
+ # NOTE: STC already has this method
+
+ def GetText(self):
+ return self.GetValue()
+
+ def GetPositionFromLine(self, line):
+ return self.XYToPosition(0,line)
+
+ def GotoLine(self, line):
+ pos = self.GetPositionFromLine(line)
+ self.SetInsertionPoint(pos)
+ self.ShowPosition(pos)
+
+ def SelectLine(self, line):
+ start = self.GetPositionFromLine(line)
+ end = start + self.GetLineLength(line)
+ self.SetSelection(start, end)
+
+
+#---------------------------------------------------------------------------
+# Constants for module versions
+
+modOriginal = 0
+modModified = 1
+modDefault = modOriginal
+
+#---------------------------------------------------------------------------
+
+class DemoCodePanel(wx.Panel):
+ """Panel for the 'Demo Code' tab"""
+ def __init__(self, parent, mainFrame):
+ wx.Panel.__init__(self, parent, size=(1,1))
+ if 'wxMSW' in wx.PlatformInfo:
+ self.Hide()
+ self.mainFrame = mainFrame
+ self.editor = DemoCodeEditor(self)
+ self.editor.RegisterModifiedEvent(self.OnCodeModified)
+
+ self.btnSave = wx.Button(self, -1, "Save Changes")
+ self.btnRestore = wx.Button(self, -1, "Delete Modified")
+ self.btnSave.Enable(False)
+ self.btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
+ self.btnRestore.Bind(wx.EVT_BUTTON, self.OnRestore)
+
+ self.radioButtons = { modOriginal: wx.RadioButton(self, -1, "Original", style = wx.RB_GROUP),
+ modModified: wx.RadioButton(self, -1, "Modified") }
+
+ self.controlBox = wx.BoxSizer(wx.HORIZONTAL)
+ self.controlBox.Add(wx.StaticText(self, -1, "Active Version:"), 0,
+ wx.RIGHT | wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5)
+ for modID, radioButton in self.radioButtons.items():
+ self.controlBox.Add(radioButton, 0, wx.EXPAND | wx.RIGHT, 5)
+ radioButton.modID = modID # makes it easier for the event handler
+ radioButton.Bind(wx.EVT_RADIOBUTTON, self.OnRadioButton)
+
+ self.controlBox.Add(self.btnSave, 0, wx.RIGHT, 5)
+ self.controlBox.Add(self.btnRestore, 0)
+
+ self.box = wx.BoxSizer(wx.VERTICAL)
+ self.box.Add(self.controlBox, 0, wx.EXPAND)
+ self.box.Add(wx.StaticLine(self), 0, wx.EXPAND)
+ self.box.Add(self.editor, 1, wx.EXPAND)
+
+ self.box.Fit(self)
+ self.SetSizer(self.box)
+
+
+ # Loads a demo from a DemoModules object
+ def LoadDemo(self, demoModules):
+ self.demoModules = demoModules
+ if (modDefault == modModified) and demoModules.Exists(modModified):
+ demoModules.SetActive(modModified)
+ else:
+ demoModules.SetActive(modOriginal)
+ self.radioButtons[demoModules.GetActiveID()].Enable(True)
+ self.ActiveModuleChanged()
+
+
+ def ActiveModuleChanged(self):
+ self.LoadDemoSource(self.demoModules.GetSource())
+ self.UpdateControlState()
+ self.ReloadDemo()
+
+
+ def LoadDemoSource(self, source):
+ self.editor.Clear()
+ self.editor.SetValue(source)
+ self.JumpToLine(0)
+ self.btnSave.Enable(False)
+
+
+ def JumpToLine(self, line, highlight=False):
+ self.editor.GotoLine(line)
+ self.editor.SetFocus()
+ if highlight:
+ self.editor.SelectLine(line)
+
+
+ def UpdateControlState(self):
+ active = self.demoModules.GetActiveID()
+ # Update the radio/restore buttons
+ for moduleID in self.radioButtons:
+ btn = self.radioButtons[moduleID]
+ if moduleID == active:
+ btn.SetValue(True)
+ else:
+ btn.SetValue(False)
+
+ if self.demoModules.Exists(moduleID):
+ btn.Enable(True)
+ if moduleID == modModified:
+ self.btnRestore.Enable(True)
+ else:
+ btn.Enable(False)
+ if moduleID == modModified:
+ self.btnRestore.Enable(False)
+
+
+ def OnRadioButton(self, event):
+ radioSelected = event.GetEventObject()
+ modSelected = radioSelected.modID
+ if modSelected != self.demoModules.GetActiveID():
+ busy = wx.BusyInfo("Reloading demo module...")
+ self.demoModules.SetActive(modSelected)
+ self.ActiveModuleChanged()
+
+
+ def ReloadDemo(self):
+ if self.demoModules.name != __name__:
+ self.mainFrame.RunModule()
+
+
+ def OnCodeModified(self, event):
+ self.btnSave.Enable(self.editor.IsModified())
+
+
+ def OnSave(self, event):
+ if self.demoModules.Exists(modModified):
+ if self.demoModules.GetActiveID() == modOriginal:
+ overwriteMsg = "You are about to overwrite an already existing modified copy\n" + \
+ "Do you want to continue?"
+ dlg = wx.MessageDialog(self, overwriteMsg, "wxPython Demo",
+ wx.YES_NO | wx.NO_DEFAULT| wx.ICON_EXCLAMATION)
+ result = dlg.ShowModal()
+ if result == wx.ID_NO:
+ return
+ dlg.Destroy()
+
+ self.demoModules.SetActive(modModified)
+ modifiedFilename = GetModifiedFilename(self.demoModules.name)
+
+ # Create the demo directory if one doesn't already exist
+ if not os.path.exists(GetModifiedDirectory()):
+ try:
+ os.makedirs(GetModifiedDirectory())
+ if not os.path.exists(GetModifiedDirectory()):
+ wx.LogMessage("BUG: Created demo directory but it still doesn't exist")
+ raise AssetionError
+ except:
+ wx.LogMessage("Error creating demo directory: %s" % GetModifiedDirectory())
+ return
+ else:
+ wx.LogMessage("Created directory for modified demos: %s" % GetModifiedDirectory())
+
+ # Save
+ f = open(modifiedFilename, "wt")
+ source = self.editor.GetText()
+ try:
+ f.write(source)
+ finally:
+ f.close()
+
+ busy = wx.BusyInfo("Reloading demo module...")
+ self.demoModules.LoadFromFile(modModified, modifiedFilename)
+ self.ActiveModuleChanged()
+
+
+ def OnRestore(self, event): # Handles the "Delete Modified" button
+ modifiedFilename = GetModifiedFilename(self.demoModules.name)
+ self.demoModules.Delete(modModified)
+ os.unlink(modifiedFilename) # Delete the modified copy
+ busy = wx.BusyInfo("Reloading demo module...")
+ self.ActiveModuleChanged()
#---------------------------------------------------------------------------
def opj(path):
"""Convert paths to the platform-specific separator"""
- return apply(os.path.join, tuple(path.split('/')))
+ str = apply(os.path.join, tuple(path.split('/')))
+ # HACK: on Linux, a leading / gets lost...
+ if path.startswith('/'):
+ str = '/' + str
+ return str
+
+
+def GetModifiedDirectory():
+ """
+ Returns the directory where modified versions of the demo files
+ are stored
+ """
+ return opj(wx.GetHomeDir() + "/.wxPyDemo/modified/")
+
+
+def GetModifiedFilename(name):
+ """
+ Returns the filename of the modified version of the specified demo
+ """
+ if not name.endswith(".py"):
+ name = name + ".py"
+ return GetModifiedDirectory() + name
+
+
+def GetOriginalFilename(name):
+ """
+ Returns the filename of the original version of the specified demo
+ """
+ if not name.endswith(".py"):
+ name = name + ".py"
+ return name
+
+
+def DoesModifiedExist(name):
+ """Returns whether the specified demo has a modified copy"""
+ if os.path.exists(GetModifiedFilename(name)):
+ return True
+ else:
+ return False
+
+
+#---------------------------------------------------------------------------
+
+class ModuleDictWrapper:
+ """Emulates a module with a dynamically compiled __dict__"""
+ def __init__(self, dict):
+ self.dict = dict
+
+ def __getattr__(self, name):
+ if name in self.dict:
+ return self.dict[name]
+ else:
+ raise AttributeError
+
+class DemoModules:
+ """
+ Dynamically manages the original/modified versions of a demo
+ module
+ """
+ def __init__(self, name):
+ self.modActive = -1
+ self.name = name
+
+ # (dict , source , filename , description , error information )
+ # ( 0 , 1 , 2 , 3 , 4 )
+ self.modules = [[None, "" , "" , "<original>" , None],
+ [None, "" , "" , "<modified>" , None]]
+
+ # load original module
+ self.LoadFromFile(modOriginal, GetOriginalFilename(name))
+ self.SetActive(modOriginal)
+
+ # load modified module (if one exists)
+ if DoesModifiedExist(name):
+ self.LoadFromFile(modModified, GetModifiedFilename(name))
+
+
+ def LoadFromFile(self, modID, filename):
+ self.modules[modID][2] = filename
+ file = open(filename, "rt")
+ self.LoadFromSource(modID, file.read())
+ file.close()
+
+
+ def LoadFromSource(self, modID, source):
+ self.modules[modID][1] = source
+ self.LoadDict(modID)
+
+
+ def LoadDict(self, modID):
+ if self.name != __name__:
+ source = self.modules[modID][1]
+ #description = self.modules[modID][3]
+ description = self.modules[modID][2]
+
+ try:
+ self.modules[modID][0] = {}
+ code = compile(source, description, "exec")
+ exec code in self.modules[modID][0]
+ except:
+ self.modules[modID][4] = DemoError(sys.exc_info())
+ self.modules[modID][0] = None
+ else:
+ self.modules[modID][4] = None
+
+
+ def SetActive(self, modID):
+ if modID != modOriginal and modID != modModified:
+ raise LookupError
+ else:
+ self.modActive = modID
+
+
+ def GetActive(self):
+ dict = self.modules[self.modActive][0]
+ if dict is None:
+ return None
+ else:
+ return ModuleDictWrapper(dict)
+
+
+ def GetActiveID(self):
+ return self.modActive
+
+
+ def GetSource(self, modID = None):
+ if modID is None:
+ modID = self.modActive
+ return self.modules[modID][1]
+
+
+ def GetFilename(self, modID = None):
+ if modID is None:
+ modID = self.modActive
+ return self.modules[self.modActive][2]
+
+
+ def GetErrorInfo(self, modID = None):
+ if modID is None:
+ modID = self.modActive
+ return self.modules[self.modActive][4]
+
+
+ def Exists(self, modID):
+ return self.modules[modID][1] != ""
+
+
+ def UpdateFile(self, modID = None):
+ """Updates the file from which a module was loaded
+ with (possibly updated) source"""
+ if modID is None:
+ modID = self.modActive
+
+ source = self.modules[modID][1]
+ filename = self.modules[modID][2]
+
+ try:
+ file = open(filename, "wt")
+ file.write(source)
+ finally:
+ file.close()
+
+
+ def Delete(self, modID):
+ if self.modActive == modID:
+ self.SetActive(0)
+
+ self.modules[modID][0] = None
+ self.modules[modID][1] = ""
+ self.modules[modID][2] = ""
+
+
+#---------------------------------------------------------------------------
+
+class DemoError:
+ """Wraps and stores information about the current exception"""
+ def __init__(self, exc_info):
+ import copy
+
+ excType, excValue = exc_info[:2]
+ # traceback list entries: (filename, line number, function name, text)
+ self.traceback = traceback.extract_tb(exc_info[2])
+
+ # --Based on traceback.py::format_exception_only()--
+ if type(excType) == types.ClassType:
+ self.exception_type = excType.__name__
+ else:
+ self.exception_type = excType
+
+ # If it's a syntax error, extra information needs
+ # to be added to the traceback
+ if excType is SyntaxError:
+ try:
+ msg, (filename, lineno, self.offset, line) = excValue
+ except:
+ pass
+ else:
+ if not filename:
+ filename = "<string>"
+ line = line.strip()
+ self.traceback.append( (filename, lineno, "", line) )
+ excValue = msg
+ try:
+ self.exception_details = str(excValue)
+ except:
+ self.exception_details = "<unprintable %s object>" & type(excValue).__name__
+
+ del exc_info
+
+ def __str__(self):
+ ret = "Type %s \n \
+ Traceback: %s \n \
+ Details : %s" % ( str(self.exception_type), str(self.traceback), self.exception_details )
+ return ret
+
+#---------------------------------------------------------------------------
+
+class DemoErrorPanel(wx.Panel):
+ """Panel put into the demo tab when the demo fails to run due to errors"""
+
+ def __init__(self, parent, codePanel, demoError, log):
+ wx.Panel.__init__(self, parent, -1)#, style=wx.NO_FULL_REPAINT_ON_RESIZE)
+ self.codePanel = codePanel
+ self.nb = parent
+ self.log = log
+
+ self.box = wx.BoxSizer(wx.VERTICAL)
+
+ # Main Label
+ self.box.Add(wx.StaticText(self, -1, "An error has occurred while trying to run the demo")
+ , 0, wx.ALIGN_CENTER | wx.TOP, 10)
+
+ # Exception Information
+ boxInfo = wx.StaticBox(self, -1, "Exception Info" )
+ boxInfoSizer = wx.StaticBoxSizer(boxInfo, wx.VERTICAL ) # Used to center the grid within the box
+ boxInfoGrid = wx.FlexGridSizer(0, 2, 0, 0)
+ textFlags = wx.ALIGN_RIGHT | wx.LEFT | wx.RIGHT | wx.TOP
+ boxInfoGrid.Add(wx.StaticText(self, -1, "Type: "), 0, textFlags, 5 )
+ boxInfoGrid.Add(wx.StaticText(self, -1, demoError.exception_type) , 0, textFlags, 5 )
+ boxInfoGrid.Add(wx.StaticText(self, -1, "Details: ") , 0, textFlags, 5 )
+ boxInfoGrid.Add(wx.StaticText(self, -1, demoError.exception_details) , 0, textFlags, 5 )
+ boxInfoSizer.Add(boxInfoGrid, 0, wx.ALIGN_CENTRE | wx.ALL, 5 )
+ self.box.Add(boxInfoSizer, 0, wx.ALIGN_CENTER | wx.ALL, 5)
+
+ # Set up the traceback list
+ # This one automatically resizes last column to take up remaining space
+ from ListCtrl import TestListCtrl
+ self.list = TestListCtrl(self, -1, style=wx.LC_REPORT | wx.SUNKEN_BORDER)
+ self.list.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
+ self.list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
+ self.list.InsertColumn(0, "Filename")
+ self.list.InsertColumn(1, "Line", wx.LIST_FORMAT_RIGHT)
+ self.list.InsertColumn(2, "Function")
+ self.list.InsertColumn(3, "Code")
+ self.InsertTraceback(self.list, demoError.traceback)
+ self.list.SetColumnWidth(0, wx.LIST_AUTOSIZE)
+ self.list.SetColumnWidth(2, wx.LIST_AUTOSIZE)
+ self.box.Add(wx.StaticText(self, -1, "Traceback:")
+ , 0, wx.ALIGN_CENTER | wx.TOP, 5)
+ self.box.Add(self.list, 1, wx.GROW | wx.ALIGN_CENTER | wx.ALL, 5)
+ self.box.Add(wx.StaticText(self, -1, "Entries from the demo module are shown in blue\n"
+ + "Double-click on them to go to the offending line")
+ , 0, wx.ALIGN_CENTER | wx.BOTTOM, 5)
+
+ self.box.Fit(self)
+ self.SetSizer(self.box)
+
+
+ def InsertTraceback(self, list, traceback):
+ #Add the traceback data
+ for x in range(len(traceback)):
+ data = traceback[x]
+ list.InsertStringItem(x, os.path.basename(data[0])) # Filename
+ list.SetStringItem(x, 1, str(data[1])) # Line
+ list.SetStringItem(x, 2, str(data[2])) # Function
+ list.SetStringItem(x, 3, str(data[3])) # Code
+
+ # Check whether this entry is from the demo module
+ if data[0] == "<original>" or data[0] == "<modified>": # FIXME: make more generalised
+ self.list.SetItemData(x, int(data[1])) # Store line number for easy access
+ # Give it a blue colour
+ item = self.list.GetItem(x)
+ item.SetTextColour(wx.BLUE)
+ self.list.SetItem(item)
+ else:
+ self.list.SetItemData(x, -1) # Editor can't jump into this one's code
+
+
+ def OnItemSelected(self, event):
+ # This occurs before OnDoubleClick and can be used to set the
+ # currentItem. OnDoubleClick doesn't get a wxListEvent....
+ self.currentItem = event.m_itemIndex
+ event.Skip()
+
+ def OnDoubleClick(self, event):
+ # If double-clicking on a demo's entry, jump to the line number
+ line = self.list.GetItemData(self.currentItem)
+ if line != -1:
+ self.nb.SetSelection(1) # Switch to the code viewer tab
+ wx.CallAfter(self.codePanel.JumpToLine, line-1, True)
+ event.Skip()
+
#---------------------------------------------------------------------------
+class DemoTaskBarIcon(wx.TaskBarIcon):
+ TBMENU_RESTORE = wx.NewId()
+ TBMENU_CLOSE = wx.NewId()
+ TBMENU_CHANGE = wx.NewId()
+ TBMENU_REMOVE = wx.NewId()
+
+ def __init__(self, frame):
+ wx.TaskBarIcon.__init__(self)
+ self.frame = frame
+
+ # Set the image
+ icon = self.MakeIcon(images.getWXPdemoImage())
+ self.SetIcon(icon, "wxPython Demo")
+ self.imgidx = 1
+
+ # bind some events
+ self.Bind(wx.EVT_TASKBAR_LEFT_DCLICK, self.OnTaskBarActivate)
+ self.Bind(wx.EVT_MENU, self.OnTaskBarActivate, id=self.TBMENU_RESTORE)
+ self.Bind(wx.EVT_MENU, self.OnTaskBarClose, id=self.TBMENU_CLOSE)
+ self.Bind(wx.EVT_MENU, self.OnTaskBarChange, id=self.TBMENU_CHANGE)
+ self.Bind(wx.EVT_MENU, self.OnTaskBarRemove, id=self.TBMENU_REMOVE)
+
+
+ def CreatePopupMenu(self):
+ """
+ This method is called by the base class when it needs to popup
+ the menu for the default EVT_RIGHT_DOWN event. Just create
+ the menu how you want it and return it from this function,
+ the base class takes care of the rest.
+ """
+ menu = wx.Menu()
+ menu.Append(self.TBMENU_RESTORE, "Restore wxPython Demo")
+ menu.Append(self.TBMENU_CLOSE, "Close wxPython Demo")
+ menu.AppendSeparator()
+ menu.Append(self.TBMENU_CHANGE, "Change the TB Icon")
+ menu.Append(self.TBMENU_REMOVE, "Remove the TB Icon")
+ return menu
+
+
+ def MakeIcon(self, img):
+ """
+ The various platforms have different requirements for the
+ icon size...
+ """
+ if "wxMSW" in wx.PlatformInfo:
+ img = img.Scale(16, 16)
+ elif "wxGTK" in wx.PlatformInfo:
+ img = img.Scale(22, 22)
+ # wxMac can be any size upto 128x128, so leave the source img alone....
+ icon = wx.IconFromBitmap(img.ConvertToBitmap() )
+ return icon
+
+
+ def OnTaskBarActivate(self, evt):
+ if self.frame.IsIconized():
+ self.frame.Iconize(False)
+ if not self.frame.IsShown():
+ self.frame.Show(True)
+ self.frame.Raise()
+
+
+ def OnTaskBarClose(self, evt):
+ self.frame.Close()
+
+
+ def OnTaskBarChange(self, evt):
+ names = [ "WXPdemo", "Mondrian", "Pencil", "Carrot" ]
+ name = names[self.imgidx]
+
+ getFunc = getattr(images, "get%sImage" % name)
+ self.imgidx += 1
+ if self.imgidx >= len(names):
+ self.imgidx = 0
+
+ icon = self.MakeIcon(getFunc())
+ self.SetIcon(icon, "This is a new icon: " + name)
+
+
+ def OnTaskBarRemove(self, evt):
+ self.RemoveIcon()
+
+
+#---------------------------------------------------------------------------
class wxPythonDemo(wx.Frame):
overviewText = "wxPython Overview"
- def __init__(self, parent, id, title):
- wx.Frame.__init__(self, parent, -1, title, size = (800, 600),
- style=wx.DEFAULT_FRAME_STYLE|wx.NO_FULL_REPAINT_ON_RESIZE)
+ def __init__(self, parent, title):
+ wx.Frame.__init__(self, parent, -1, title, size = (950, 720),
+ style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
+
+ self.SetMinSize((640,480))
+ self.loaded = False
self.cwd = os.getcwd()
self.curOverview = ""
- self.window = None
+ self.demoPage = None
+ self.codePage = None
+ self.shell = None
+ self.firstTime = True
+ self.finddlg = None
- icon = images.getMondrianIcon()
+ icon = images.getWXPdemoIcon()
self.SetIcon(icon)
- if wx.Platform == '__WXMSW__':
- # setup a taskbar icon, and catch some events from it
- self.tbicon = wx.TaskBarIcon()
- self.tbicon.SetIcon(icon, "wxPython Demo")
- wx.EVT_TASKBAR_LEFT_DCLICK(self.tbicon, self.OnTaskBarActivate)
- wx.EVT_TASKBAR_RIGHT_UP(self.tbicon, self.OnTaskBarMenu)
- wx.EVT_MENU(self.tbicon, self.TBMENU_RESTORE, self.OnTaskBarActivate)
- wx.EVT_MENU(self.tbicon, self.TBMENU_CLOSE, self.OnTaskBarClose)
+ self.tbicon = DemoTaskBarIcon(self)
wx.CallAfter(self.ShowTip)
self.otherWin = None
- wx.EVT_IDLE(self, self.OnIdle)
- wx.EVT_CLOSE(self, self.OnCloseWindow)
- wx.EVT_ICONIZE(self, self.OnIconfiy)
- wx.EVT_MAXIMIZE(self, self.OnMaximize)
+ self.Bind(wx.EVT_IDLE, self.OnIdle)
+ self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+ self.Bind(wx.EVT_ICONIZE, self.OnIconfiy)
+ self.Bind(wx.EVT_MAXIMIZE, self.OnMaximize)
self.Centre(wx.BOTH)
self.CreateStatusBar(1, wx.ST_SIZEGRIP)
- splitter = wx.SplitterWindow(self, -1)
- splitter2 = wx.SplitterWindow(splitter, -1)
+ splitter = wx.SplitterWindow(self, -1, style=wx.CLIP_CHILDREN | wx.SP_LIVE_UPDATE | wx.SP_3D)
+ splitter2 = wx.SplitterWindow(splitter, -1, style=wx.CLIP_CHILDREN | wx.SP_LIVE_UPDATE | wx.SP_3D)
def EmptyHandler(evt): pass
- wx.EVT_ERASE_BACKGROUND(splitter, EmptyHandler)
- wx.EVT_ERASE_BACKGROUND(splitter2, EmptyHandler)
+ #splitter.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
+ #splitter2.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
# Prevent TreeCtrl from displaying all items after destruction when True
self.dying = False
+ # Create a Notebook
+ self.nb = wx.Notebook(splitter2, -1, style=wx.CLIP_CHILDREN)
+
# Make a File menu
self.mainmenu = wx.MenuBar()
menu = wx.Menu()
- exitID = wx.NewId()
- menu.Append(exitID, 'E&xit\tAlt-X', 'Get the heck outta here!')
- wx.EVT_MENU(self, exitID, self.OnFileExit)
- wx.App_SetMacExitMenuItemId(exitID)
+ item = menu.Append(-1, '&Redirect Output',
+ 'Redirect print statements to a window',
+ wx.ITEM_CHECK)
+ self.Bind(wx.EVT_MENU, self.OnToggleRedirect, item)
+
+ item = menu.Append(-1, 'E&xit\tAlt-X', 'Get the heck outta here!')
+ self.Bind(wx.EVT_MENU, self.OnFileExit, item)
+ wx.App.SetMacExitMenuItemId(item.GetId())
self.mainmenu.Append(menu, '&File')
# Make a Demo menu
for item in _treeList:
submenu = wx.Menu()
for childItem in item[1]:
- mID = wx.NewId()
- submenu.Append(mID, childItem)
- wx.EVT_MENU(self, mID, self.OnDemoMenu)
+ mi = submenu.Append(-1, childItem)
+ self.Bind(wx.EVT_MENU, self.OnDemoMenu, mi)
menu.AppendMenu(wx.NewId(), item[0], submenu)
self.mainmenu.Append(menu, '&Demo')
+ # Make a Demo Code menu
+ #TODO: Add new menu items
+ # Like the option-enabled entries to select the
+ # active module
+ #TODO: should we bother?
+
+ #menu = wx.Menu()
+ #saveID = wx.NewId()
+ #restoreID = wx.NewId()
+ #
+ #menu.Append(saveID, '&Save\tCtrl-S', 'Save edited demo')
+ #menu.Append(restoreID, '&Delete Modified\tCtrl-R', 'Delete modified copy')
+ #self.Bind(wx.EVT_MENU, self.codePage.OnSave, id=saveID)
+ #self.Bind(wx.EVT_MENU, self.codePage.OnRestore, id=restoreID)
+ #self.mainmenu.Append(menu, 'Demo &Code')
+ #
# Make a Help menu
- helpID = wx.NewId()
- findID = wx.NewId()
- findnextID = wx.NewId()
menu = wx.Menu()
- menu.Append(findID, '&Find\tCtrl-F', 'Find in the Demo Code')
- menu.Append(findnextID, 'Find &Next\tF3', 'Find Next')
+ findItem = menu.Append(-1, '&Find\tCtrl-F', 'Find in the Demo Code')
+ findnextItem = menu.Append(-1, 'Find &Next\tF3', 'Find Next')
menu.AppendSeparator()
- menu.Append(helpID, '&About\tCtrl-H', 'wxPython RULES!!!')
- wx.App_SetMacAboutMenuItemId(helpID)
- wx.EVT_MENU(self, helpID, self.OnHelpAbout)
- wx.EVT_MENU(self, findID, self.OnHelpFind)
- wx.EVT_MENU(self, findnextID, self.OnFindNext)
- wx.EVT_COMMAND_FIND(self, -1, self.OnFind)
- wx.EVT_COMMAND_FIND_NEXT(self, -1, self.OnFind)
- wx.EVT_COMMAND_FIND_CLOSE(self, -1 , self.OnFindClose)
+
+ shellItem = menu.Append(-1, 'Open Py&Shell Window\tF5',
+ 'An interactive interpreter window with the demo app and frame objects in the namesapce')
+ menu.AppendSeparator()
+ helpItem = menu.Append(-1, '&About\tCtrl-H', 'wxPython RULES!!!')
+ wx.App.SetMacAboutMenuItemId(helpItem.GetId())
+
+ self.Bind(wx.EVT_MENU, self.OnOpenShellWindow, shellItem)
+ self.Bind(wx.EVT_MENU, self.OnHelpAbout, helpItem)
+ self.Bind(wx.EVT_MENU, self.OnHelpFind, findItem)
+ self.Bind(wx.EVT_MENU, self.OnFindNext, findnextItem)
+ self.Bind(wx.EVT_FIND, self.OnFind)
+ self.Bind(wx.EVT_FIND_NEXT, self.OnFind)
+ self.Bind(wx.EVT_FIND_CLOSE, self.OnFindClose)
+ self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateFindItems, findItem)
+ self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateFindItems, findnextItem)
self.mainmenu.Append(menu, '&Help')
self.SetMenuBar(self.mainmenu)
self.finddata = wx.FindReplaceData()
+ self.finddata.SetFlags(wx.FR_DOWN)
if 0:
# This is another way to set Accelerators, in addition to
self.tree.Expand(root)
self.tree.Expand(firstChild)
- wx.EVT_TREE_ITEM_EXPANDED (self.tree, tID, self.OnItemExpanded)
- wx.EVT_TREE_ITEM_COLLAPSED (self.tree, tID, self.OnItemCollapsed)
- wx.EVT_TREE_SEL_CHANGED (self.tree, tID, self.OnSelChanged)
- wx.EVT_LEFT_DOWN (self.tree, self.OnTreeLeftDown)
-
- # Create a Notebook
- self.nb = wx.Notebook(splitter2, -1, style=wx.CLIP_CHILDREN)
+ self.tree.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded, id=tID)
+ self.tree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed, id=tID)
+ self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=tID)
+ self.tree.Bind(wx.EVT_LEFT_DOWN, self.OnTreeLeftDown)
# Set up a wx.html.HtmlWindow on the Overview Notebook page
# we put it in a panel first because there seems to be a
def OnOvrSize(evt, ovr=self.ovr):
ovr.SetSize(evt.GetSize())
+ panel.Bind(wx.EVT_SIZE, OnOvrSize)
+ panel.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
- wx.EVT_SIZE(panel, OnOvrSize)
- wx.EVT_ERASE_BACKGROUND(panel, EmptyHandler)
-
-
- self.SetOverview(self.overviewText, overview)
+ if "gtk2" in wx.PlatformInfo:
+ self.ovr.SetStandardFonts()
+ self.SetOverview(self.overviewText, mainOverview)
- # Set up a notebook page for viewing the source code of each sample
- self.txt = DemoCodeViewer(self.nb, -1)
- self.nb.AddPage(self.txt, "Demo Code")
- self.GetDemoFile('Main.py')
-
-
- # Set up a log on the View Log Notebook page
+ # Set up a log window
self.log = wx.TextCtrl(splitter2, -1,
style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
# But instead of the above we want to show how to use our own wx.Log class
wx.Log_SetActiveTarget(MyLog(self.log))
-
+
# for serious debugging
#wx.Log_SetActiveTarget(wx.LogStderr())
#wx.Log_SetTraceMask(wx.TraceMessages)
- self.Show(True)
+ self.Bind(wx.EVT_ACTIVATE, self.OnActivate)
+ wx.GetApp().Bind(wx.EVT_ACTIVATE_APP, self.OnAppActivate)
# add the windows to the splitter and split it.
- splitter2.SplitHorizontally(self.nb, self.log, 450)
- splitter.SplitVertically(self.tree, splitter2, 180)
+ splitter2.SplitHorizontally(self.nb, self.log, -160)
+ splitter.SplitVertically(self.tree, splitter2, 200)
- splitter.SetMinimumPaneSize(20)
- splitter2.SetMinimumPaneSize(20)
+ splitter.SetMinimumPaneSize(120)
+ splitter2.SetMinimumPaneSize(60)
+ # Make the splitter on the right expand the top window when resized
+ def SplitterOnSize(evt):
+ splitter = evt.GetEventObject()
+ sz = splitter.GetSize()
+ splitter.SetSashPosition(sz.height - 160, False)
+ evt.Skip()
+ splitter2.Bind(wx.EVT_SIZE, SplitterOnSize)
# select initial items
self.nb.SetSelection(0)
self.tree.SelectItem(root)
- if len(sys.argv) == 2:
- try:
- selectedDemo = self.treeMap[sys.argv[1]]
- except:
- selectedDemo = None
+ # Load 'Main' module
+ self.LoadDemo(self.overviewText)
+ self.loaded = True
+
+ # select some other initial module?
+ if len(sys.argv) > 1:
+ arg = sys.argv[1]
+ if arg.endswith('.py'):
+ arg = arg[:-3]
+ selectedDemo = self.treeMap.get(arg, None)
if selectedDemo:
self.tree.SelectItem(selectedDemo)
self.tree.EnsureVisible(selectedDemo)
- wx.LogMessage('window handle: %s' % self.GetHandle())
-
-
#---------------------------------------------
def WriteText(self, text):
if text[-1:] == '\n':
text = text[:-1]
wx.LogMessage(text)
-
def write(self, txt):
self.WriteText(txt)
#---------------------------------------------
def OnTreeLeftDown(self, event):
+ # reset the overview text if the tree item is clicked on again
pt = event.GetPosition();
item, flags = self.tree.HitTest(pt)
if item == self.tree.GetSelection():
#---------------------------------------------
def OnSelChanged(self, event):
- if self.dying:
+ if self.dying or not self.loaded:
return
item = event.GetItem()
itemText = self.tree.GetItemText(item)
- self.RunDemo(itemText)
-
+ self.LoadDemo(itemText)
#---------------------------------------------
- def RunDemo(self, itemText):
- os.chdir(self.cwd)
- if self.nb.GetPageCount() == 3:
- if self.nb.GetSelection() == 2:
- self.nb.SetSelection(0)
- # inform the window that it's time to quit if it cares
- if self.window is not None:
- if hasattr(self.window, "ShutdownDemo"):
- self.window.ShutdownDemo()
- wx.SafeYield() # in case the page has pending events
- self.nb.DeletePage(2)
-
- if itemText == self.overviewText:
- self.GetDemoFile('Main.py')
- self.SetOverview(self.overviewText, overview)
- self.nb.Refresh();
- self.window = None
-
- else:
- if os.path.exists(itemText + '.py'):
- wx.BeginBusyCursor()
- wx.LogMessage("Running demo %s.py..." % itemText)
- try:
- self.GetDemoFile(itemText + '.py')
- module = __import__(itemText, globals())
- self.SetOverview(itemText + " Overview", module.overview)
- finally:
- wx.EndBusyCursor()
- self.tree.Refresh()
-
- # in case runTest is modal, make sure things look right...
- self.nb.Refresh();
- wx.SafeYield()
-
- self.window = module.runTest(self, self.nb, self) ###
- if self.window is not None:
- self.nb.AddPage(self.window, 'Demo')
- self.nb.SetSelection(2)
- self.nb.Refresh() # without this wxMac has troubles showing the just added page
-
+ def LoadDemo(self, demoName):
+ try:
+ wx.BeginBusyCursor()
+
+ os.chdir(self.cwd)
+ self.ShutdownDemoModule()
+
+ if demoName == self.overviewText:
+ # User selected the "wxPython Overview" node
+ # ie: _this_ module
+ # Changing the main window at runtime not yet supported...
+ self.demoModules = DemoModules(__name__)
+ self.SetOverview(self.overviewText, mainOverview)
+ self.LoadDemoSource()
+ self.UpdateNotebook(0)
else:
- self.ovr.SetPage("")
- self.txt.Clear()
- self.window = None
+ if os.path.exists(GetOriginalFilename(demoName)):
+ wx.LogMessage("Loading demo %s.py..." % demoName)
+ self.demoModules = DemoModules(demoName)
+ self.LoadDemoSource()
+ self.tree.Refresh()
+ else:
+ self.SetOverview("wxPython", mainOverview)
+ self.codePage = None
+ self.UpdateNotebook(0)
+ finally:
+ wx.EndBusyCursor()
+ #---------------------------------------------
+ def LoadDemoSource(self):
+ self.codePage = None
+ self.codePage = DemoCodePanel(self.nb, self)
+ self.codePage.LoadDemo(self.demoModules)
+
+ #---------------------------------------------
+ def RunModule(self):
+ """Runs the active module"""
+
+ module = self.demoModules.GetActive()
+ self.ShutdownDemoModule()
+ overviewText = ""
+
+ # o The RunTest() for all samples must now return a window that can
+ # be palced in a tab in the main notebook.
+ # o If an error occurs (or has occurred before) an error tab is created.
+
+ if module is not None:
+ wx.LogMessage("Running demo module...")
+ if hasattr(module, "overview"):
+ overviewText = module.overview
+ try:
+ self.demoPage = module.runTest(self, self.nb, self)
+ except:
+ self.demoPage = DemoErrorPanel(self.nb, self.codePage,
+ DemoError(sys.exc_info()), self)
+
+ assert self.demoPage is not None, "runTest must return a window!"
+
+ else:
+ # There was a previous error in compiling or exec-ing
+ self.demoPage = DemoErrorPanel(self.nb, self.codePage,
+ self.demoModules.GetErrorInfo(), self)
+
+ self.SetOverview(self.demoModules.name + " Overview", overviewText)
+
+ if self.firstTime:
+ # cahnge to the demo page the first time a module is run
+ self.UpdateNotebook(2)
+ self.firstTime = False
+ else:
+ # otherwise just stay on the same tab in case the user has changed to another one
+ self.UpdateNotebook()
#---------------------------------------------
- # Get the Demo files
- def GetDemoFile(self, filename):
- self.txt.Clear()
- try:
- self.txt.SetValue(open(filename).read())
- except IOError:
- self.txt.SetValue("Cannot open %s file." % filename)
+ def ShutdownDemoModule(self):
+ if self.demoPage:
+ # inform the window that it's time to quit if it cares
+ if hasattr(self.demoPage, "ShutdownDemo"):
+ self.demoPage.ShutdownDemo()
+ wx.YieldIfNeeded() # in case the page has pending events
+ self.demoPage = None
+
+ #---------------------------------------------
+ def UpdateNotebook(self, select = -1):
+ nb = self.nb
+ debug = False
+
+ def UpdatePage(page, pageText):
+ pageExists = False
+ pagePos = -1
+ for i in range(nb.GetPageCount()):
+ if nb.GetPageText(i) == pageText:
+ pageExists = True
+ pagePos = i
+ break
+
+ if page:
+ if not pageExists:
+ # Add a new page
+ nb.AddPage(page, pageText)
+ if debug: wx.LogMessage("DBG: ADDED %s" % pageText)
+ else:
+ if nb.GetPage(pagePos) != page:
+ # Reload an existing page
+ nb.Freeze()
+ nb.DeletePage(pagePos)
+ nb.InsertPage(pagePos, page, pageText)
+ nb.Thaw()
+ if debug: wx.LogMessage("DBG: RELOADED %s" % pageText)
+ else:
+ # Excellent! No redraw/flicker
+ if debug: wx.LogMessage("DBG: SAVED from reloading %s" % pageText)
+ elif pageExists:
+ # Delete a page
+ nb.DeletePage(pagePos)
+ if debug: wx.LogMessage("DBG: DELETED %s" % pageText)
+ else:
+ if debug: wx.LogMessage("DBG: STILL GONE - %s" % pageText)
+
+ if select == -1:
+ select = nb.GetSelection()
- self.txt.SetInsertionPoint(0)
- self.txt.ShowPosition(0)
+ UpdatePage(self.codePage, "Demo Code")
+ UpdatePage(self.demoPage, "Demo")
+ if select >= 0 and select < nb.GetPageCount():
+ nb.SetSelection(select)
+
#---------------------------------------------
def SetOverview(self, name, text):
self.curOverview = text
lead = text[:6]
if lead != '<html>' and lead != '<HTML>':
text = '<br>'.join(text.split('\n'))
+ if wx.USE_UNICODE:
+ text = text.decode('iso8859_1')
self.ovr.SetPage(text)
self.nb.SetPageText(0, name)
def OnFileExit(self, *event):
self.Close()
+ def OnToggleRedirect(self, event):
+ app = wx.GetApp()
+ if event.Checked():
+ app.RedirectStdio()
+ print "Print statements and other standard output will now be directed to this window."
+ else:
+ app.RestoreStdio()
+ print "Print statements and other standard output will now be sent to the usual location."
+
def OnHelpAbout(self, event):
from About import MyAboutBox
about = MyAboutBox(self)
about.Destroy()
def OnHelpFind(self, event):
+ if self.finddlg != None:
+ return
+
self.nb.SetSelection(1)
self.finddlg = wx.FindReplaceDialog(self, self.finddata, "Find",
- wx.FR_NOUPDOWN |
- wx.FR_NOMATCHCASE |
- wx.FR_NOWHOLEWORD)
+ wx.FR_NOMATCHCASE | wx.FR_NOWHOLEWORD)
self.finddlg.Show(True)
+
+ def OnUpdateFindItems(self, evt):
+ evt.Enable(self.finddlg == None)
+
+
def OnFind(self, event):
+ editor = self.codePage.editor
self.nb.SetSelection(1)
- end = self.txt.GetLastPosition()
- textstring = self.txt.GetRange(0, end).lower()
- start = self.txt.GetSelection()[1]
+ end = editor.GetLastPosition()
+ textstring = editor.GetRange(0, end).lower()
findstring = self.finddata.GetFindString().lower()
- loc = textstring.find(findstring, start)
+ backward = not (self.finddata.GetFlags() & wx.FR_DOWN)
+ if backward:
+ start = editor.GetSelection()[0]
+ loc = textstring.rfind(findstring, 0, start)
+ else:
+ start = editor.GetSelection()[1]
+ loc = textstring.find(findstring, start)
if loc == -1 and start != 0:
# string not found, start at beginning
- start = 0
- loc = textstring.find(findstring, start)
+ if backward:
+ start = end
+ loc = textstring.rfind(findstring, 0, start)
+ else:
+ start = 0
+ loc = textstring.find(findstring, start)
if loc == -1:
dlg = wx.MessageDialog(self, 'Find String Not Found',
'Find String Not Found in Demo File',
return
else:
self.finddlg.Destroy()
- self.txt.ShowPosition(loc)
- self.txt.SetSelection(loc, loc + len(findstring))
+ self.finddlg = None
+ editor.ShowPosition(loc)
+ editor.SetSelection(loc, loc + len(findstring))
def OnFindClose(self, event):
event.GetDialog().Destroy()
+ self.finddlg = None
+ def OnOpenShellWindow(self, evt):
+ if self.shell:
+ # if it already exists then just make sure it's visible
+ s = self.shell
+ if s.IsIconized():
+ s.Iconize(False)
+ s.Raise()
+ else:
+ # Make a PyShell window
+ from wx import py
+ namespace = { 'wx' : wx,
+ 'app' : wx.GetApp(),
+ 'frame' : self,
+ }
+ self.shell = py.shell.ShellFrame(None, locals=namespace)
+ self.shell.SetSize((640,480))
+ self.shell.Show()
+
+ # Hook the close event of the main frame window so that we
+ # close the shell at the same time if it still exists
+ def CloseShell(evt):
+ if self.shell:
+ self.shell.Close()
+ evt.Skip()
+ self.Bind(wx.EVT_CLOSE, CloseShell)
+
+
#---------------------------------------------
def OnCloseWindow(self, event):
self.dying = True
- self.window = None
+ self.demoPage = None
+ self.codePage = None
self.mainmenu = None
- if hasattr(self, "tbicon"):
- del self.tbicon
+ self.tbicon.Destroy()
self.Destroy()
def OnIdle(self, event):
if self.otherWin:
self.otherWin.Raise()
- self.window = self.otherWin
+ self.demoPage = self.otherWin
self.otherWin = None
self.tree.EnsureVisible(selectedDemo)
- #---------------------------------------------
- def OnTaskBarActivate(self, evt):
- if self.IsIconized():
- self.Iconize(False)
- if not self.IsShown():
- self.Show(True)
- self.Raise()
-
- #---------------------------------------------
-
- TBMENU_RESTORE = 1000
- TBMENU_CLOSE = 1001
-
- def OnTaskBarMenu(self, evt):
- menu = wx.Menu()
- menu.Append(self.TBMENU_RESTORE, "Restore wxPython Demo")
- menu.Append(self.TBMENU_CLOSE, "Close")
- self.tbicon.PopupMenu(menu)
- menu.Destroy()
-
- #---------------------------------------------
- def OnTaskBarClose(self, evt):
- self.Close()
-
- # because of the way wx.TaskBarIcon.PopupMenu is implemented we have to
- # prod the main idle handler a bit to get the window to actually close
- wx.GetApp().ProcessIdle()
-
#---------------------------------------------
def OnIconfiy(self, evt):
- wx.LogMessage("OnIconfiy")
+ wx.LogMessage("OnIconfiy: %s" % evt.Iconized())
evt.Skip()
#---------------------------------------------
wx.LogMessage("OnMaximize")
evt.Skip()
+ #---------------------------------------------
+ def OnActivate(self, evt):
+ wx.LogMessage("OnActivate: %s" % evt.GetActive())
+ evt.Skip()
-
+ #---------------------------------------------
+ def OnAppActivate(self, evt):
+ wx.LogMessage("OnAppActivate: %s" % evt.GetActive())
+ evt.Skip()
#---------------------------------------------------------------------------
#---------------------------------------------------------------------------
class MySplashScreen(wx.SplashScreen):
def __init__(self):
- bmp = wx.Image(opj("bitmaps/splash.gif")).ConvertToBitmap()
+ bmp = wx.Image(opj("bitmaps/splash.png")).ConvertToBitmap()
wx.SplashScreen.__init__(self, bmp,
- wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_TIMEOUT,
- 4000, None, -1,
- style = wx.SIMPLE_BORDER|wx.FRAME_NO_TASKBAR|wx.STAY_ON_TOP)
- wx.EVT_CLOSE(self, self.OnClose)
+ wx.SPLASH_CENTRE_ON_SCREEN | wx.SPLASH_TIMEOUT,
+ 5000, None, -1)
+ self.Bind(wx.EVT_CLOSE, self.OnClose)
+ self.fc = wx.FutureCall(2000, self.ShowMain)
+
def OnClose(self, evt):
- frame = wxPythonDemo(None, -1, "wxPython: (A Demonstration)")
- frame.Show()
- evt.Skip() # Make sure the default handler runs too...
+ # Make sure the default handler runs too so this window gets
+ # destroyed
+ evt.Skip()
+ self.Hide()
+
+ # if the timer is still running then go ahead and show the
+ # main frame now
+ if self.fc.IsRunning():
+ self.fc.Stop()
+ self.ShowMain()
+ def ShowMain(self):
+ frame = wxPythonDemo(None, "wxPython: (A Demonstration)")
+ frame.Show()
+ if self.fc.IsRunning():
+ self.Raise()
+
+
class MyApp(wx.App):
def OnInit(self):
"""
the main frame when it is time to do so.
"""
- #import locale
- #self.locale = wx.Locale(wx.LANGUAGE_FRENCH)
- #locale.setlocale(locale.LC_ALL, 'fr')
+ wx.SystemOptions.SetOptionInt("mac.window-plain-transition", 1)
+
+ # For debugging
+ #self.SetAssertMode(wx.PYAPP_ASSERT_DIALOG)
- wx.InitAllImageHandlers()
+ # Normally when using a SplashScreen you would create it, show
+ # it and then continue on with the applicaiton's
+ # initialization, finally creating and showing the main
+ # application window(s). In this case we have nothing else to
+ # do so we'll delay showing the main frame until later (see
+ # ShowMain above) so the users can see the SplashScreen effect.
splash = MySplashScreen()
splash.Show()
+
return True
os.chdir(demoPath)
except:
pass
- app = MyApp(wx.Platform == "__WXMAC__")
+ app = MyApp(False)
app.MainLoop()
-
#---------------------------------------------------------------------------
-
-overview = """<html><body>
+mainOverview = """<html><body>
<h2>wxPython</h2>
-<p> wxPython is a <b>GUI toolkit</b> for the <a
-href="http://www.python.org/">Python</a> 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 <a href="http://wxwindows.org/front.htm">wxWindows</a> cross
-platform GUI library, which is written in C++.
+<p> wxPython is a <b>GUI toolkit</b> 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++.
<p> Like Python and wxWindows, wxPython is <b>Open Source</b> which
means that it is free for anyone to use and the source code is
available for anyone to look at and modify. Or anyone can contribute
-fixes or enhnacments to the project.
+fixes or enhancements to the project.
<p> wxPython is a <b>cross-platform</b> toolkit. This means that the
same program will run on multiple platforms without modification.
#----------------------------------------------------------------------------
if __name__ == '__main__':
+ __name__ = 'Main'
main()
#----------------------------------------------------------------------------