+ # new stuff
+ ('Recent Additions/Updates', [
+ 'FoldPanelBar',
+ 'GIFAnimationCtrl',
+ 'HyperLinkCtrl',
+ ]),
+
+ # managed windows == things with a (optional) caption you can close
+ ('Frames and Dialogs', [
+ 'Dialog',
+ 'Frame',
+ 'MDIWindows',
+ 'MiniFrame',
+ 'Wizard',
+ ]),
+
+ # the common dialogs
+ ('Common Dialogs', [
+ 'ColourDialog',
+ 'DirDialog',
+ 'FileDialog',
+ 'FindReplaceDialog',
+ 'FontDialog',
+ 'MessageDialog',
+ 'MultiChoiceDialog',
+ 'PageSetupDialog',
+ 'PrintDialog',
+ 'ProgressDialog',
+ 'SingleChoiceDialog',
+ 'TextEntryDialog',
+ ]),
+
+ # dialogs from libraries
+ ('More Dialogs', [
+ 'ImageBrowser',
+ 'MultipleChoiceDialog',
+ 'ScrolledMessageDialog',
+ ]),
+
+ # core controls
+ ('Core Windows/Controls', [
+ 'BitmapButton',
+ 'Button',
+ 'CheckBox',
+ 'CheckListBox',
+ 'Choice',
+ 'Choicebook',
+ 'ComboBox',
+ 'Gauge',
+ 'Grid',
+ 'Grid_MegaExample',
+ 'ListBox',
+ 'ListCtrl',
+ 'ListCtrl_virtual',
+ 'ListCtrl_edit',
+ 'Listbook',
+ 'Menu',
+ 'Notebook',
+ 'PopupMenu',
+ 'PopupWindow',
+ 'RadioBox',
+ 'RadioButton',
+ 'SashWindow',
+ 'ScrolledWindow',
+ 'Slider',
+ 'SpinButton',
+ 'SpinCtrl',
+ 'SplitterWindow',
+ 'StaticBitmap',
+ 'StaticBox',
+ 'StaticText',
+ 'StatusBar',
+ 'StockButtons',
+ 'TextCtrl',
+ 'ToggleButton',
+ 'ToolBar',
+ 'TreeCtrl',
+ 'Validator',
+ ]),
+
+ ('Custom Controls', [
+ 'AnalogClockWindow',
+ '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',
+ 'ContextHelp',
+ 'DatePickerCtrl',
+ 'DynamicSashWindow',
+ 'EditableListBox',
+ 'FancyText',
+ 'FileBrowseButton',
+ 'FloatBar',
+ 'FloatCanvas',
+ 'FoldPanelBar',
+ 'GIFAnimationCtrl',
+ 'HtmlWindow',
+ 'HyperLinkCtrl',
+ 'IntCtrl',
+ 'MediaCtrl',
+ 'MVCTree',
+ 'MaskedEditControls',
+ 'MaskedNumCtrl',
+ 'MimeTypesManager',
+ 'PyCrust',
+ 'PyPlot',
+ 'PyShell',
+ 'ScrolledPanel',
+ 'SplitTree',
+ 'StyledTextCtrl_1',
+ 'StyledTextCtrl_2',
+ 'TablePrint',
+ 'Throbber',
+ 'Ticker',
+ 'TimeCtrl',
+ 'VListBox',
+ ]),
+
+ # How to lay out the controls in a frame/dialog
+ ('Window Layout', [
+ 'GridBagSizer',
+ 'LayoutAnchors',
+ 'LayoutConstraints',
+ 'Layoutf',
+ 'RowColSizer',
+ 'ScrolledPanel',
+ 'Sizers',
+ 'XmlResource',
+ 'XmlResourceHandler',
+ 'XmlResourceSubclass',
+ ]),
+
+ # ditto
+ ('Process and Events', [
+ 'EventManager',
+ 'KeyEvents',
+ 'Process',
+ 'PythonEvents',
+ 'Threads',
+ 'Timer',
+ ##'infoframe', # needs better explanation and some fixing
+ ]),
+
+ # Clipboard and DnD
+ ('Clipboard and DnD', [
+ 'CustomDragAndDrop',
+ 'DragAndDrop',
+ 'URLDragAndDrop',
+ ]),
+
+ # Images
+ ('Using Images', [
+ 'ArtProvider',
+ 'Cursor',
+ 'DragImage',
+ 'GIFAnimationCtrl',
+ 'Image',
+ 'ImageAlpha',
+ 'ImageFromStream',
+ 'Mask',
+ 'Throbber',
+ ]),
+
+ # Other stuff
+ ('Miscellaneous', [
+ 'ColourDB',
+ ##'DialogUnits', # needs more explanations
+ 'DrawXXXList',
+ 'FileHistory',
+ 'FontEnumerator',
+ 'Joystick',
+ 'MouseGestures',
+ 'OGL',
+ 'PrintFramework',
+ 'ShapedWindow',
+ 'Sound',
+ 'StandardPaths',
+ 'Unicode',
+ ]),
+
+ # need libs not coming with the demo
+ ('Samples using an external library', [
+ 'GLCanvas',
+ ]),
+
+
+ ('Check out the samples dir too', [
+ ]),
+
+]
+
+
+
+#---------------------------------------------------------------------------
+# Show how to derive a custom wxLog class
+
+class MyLog(wx.PyLog):
+ def __init__(self, textCtrl, logTime=0):
+ wx.PyLog.__init__(self)
+ self.tc = textCtrl
+ self.logTime = logTime
+
+ def DoLogString(self, message, timeStamp):
+ #print message, timeStamp
+ #if self.logTime:
+ # message = time.strftime("%X", time.localtime(timeStamp)) + \
+ # ": " + message
+ if self.tc:
+ self.tc.AppendText(message + '\n')
+
+
+class MyTP(wx.PyTipProvider):
+ 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 StyledTextCtrl_2 sample first, fall back to wxTextCtrl
+# if there is an error, such as the stc module not being present.
+#
+
+try:
+ ##raise ImportError # for testing the alternate implementation
+ from wx import stc
+ 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):
+ if wx.USE_UNICODE:
+ value = value.decode('iso8859_1')
+ self.SetText(value)
+ 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):
+ 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)
+
+ def GetSelection(self):
+ return self.GetAnchor(), self.GetCurrentPos()
+
+ def SetSelection(self, 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:12')
+
+ # 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 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"""
+ 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]
+
+ 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