+class DemoCodePanel(wx.Panel):
+ """Panel for the 'Demo Code' tab"""
+ def __init__(self, parent, mainFrame):
+ wx.Panel.__init__(self, parent)
+ 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 exit")
+ 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