#!/bin/env python #---------------------------------------------------------------------------- # Name: Main.py # Purpose: Testing lots of stuff, controls, window types, etc. # # Author: Robin Dunn # # Created: A long time ago, in a galaxy far, far away... # Copyright: (c) 1999-2020 by Total Control Software # Licence: wxWindows license # Tags: phoenix-port, py3-port #---------------------------------------------------------------------------- # 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.... # AG: It looks like this issue is fixed by Freeze()ing and Thaw()ing the # main frame and not the notebook # TODO List: # * UI design more professional (is the new version more professional?) # * save file positions (new field in demoModules) (@ LoadDemoSource) # * Update main overview # * Why don't we move _treeList into a separate module # ===================== # = EXTERNAL Packages = # ===================== # In order to let a package (like AGW) be included in the wxPython demo, # the package owner should create a sub-directory of the wxPython demo folder # in which all the package's demos should live. In addition, the sub-folder # should contain a Python file called __demo__.py which, when imported, should # contain the following methods: # # * GetDemoBitmap: returns the bitmap to be used in the wxPython demo tree control # in a PyEmbeddedImage format; # * GetRecentAdditions: returns a list of demos which will be displayed under the # "Recent Additions/Updates" tree item. This list should be a subset (or the full # set) of the package's demos; # * GetDemos: returns a tuple. The first item of the tuple is the package's name # as will be displayed in the wxPython demo tree, right after the "Custom Controls" # item. The second element of the tuple is the list of demos for the external package. # * GetOverview: returns a wx.html-ready representation of the package's documentation. # # Please see the __demo__.py file in the demo/agw/ folder for an example. # Last updated: Andrea Gavana, 20 Oct 2008, 18.00 GMT import sys, os, time, traceback import pickle import re import shutil import urllib.error import urllib.request from io import BytesIO from threading import Thread import wx import wx.adv import wx.lib.agw.aui as aui import wx.html from wx.lib.msgpanel import MessagePanel from wx.adv import TaskBarIcon as TaskBarIcon from wx.adv import SplashScreen as SplashScreen import wx.lib.mixins.inspection import version # We won't import the images module yet, but we'll assign it to this # global when we do. images = None # 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...") #--------------------------------------------------------------------------- USE_CUSTOMTREECTRL = False DEFAULT_PERSPECTIVE = "Default Perspective" #--------------------------------------------------------------------------- # get images and demo list from demodata import _demoPngs, _treeList #--------------------------------------------------------------------------- _styleTable = '

Window %s

\n' \ '

This class supports the following window %s:\n' \ '

' _eventTable = '

Events

\n' \ '

Events emitted by this class:\n' \ '

' _appearanceTable = '

Appearance

\n' \ '

Control appearance on various platform:\n' \ '

' _styleHeaders = ["Style Name", "Description"] _eventHeaders = ["Event Name", "Description"] _headerTable = '' _styleTag = '' _eventTag = '' _hexValues = '' _description = '' _imageTag = '' _platformTag = '' _trunkURL = "http://docs.wxwidgets.org/trunk/" _docsURL = _trunkURL + "classwx%s.html" _platformNames = ["wxMSW", "wxGTK", "wxMac"] _importList = ["wx.aui", "wx.calendar", "wx.html", "wx.media", "wx.wizard", "wx.combo", "wx.animate", "wx.gizmos", "wx.glcanvas", "wx.grid", "wx.richtext", "wx.stc"] _dirWX = dir(wx) for mod in _importList: try: module = __import__(mod) except ImportError: continue #--------------------------------------------------------------------------- def ReplaceCapitals(string): """ Replaces the capital letter in a string with an underscore plus the corresponding lowercase character. **Parameters:** * `string`: the string to be analyzed. """ newString = "" for char in string: if char.isupper(): newString += "_%s"%char.lower() else: newString += char return newString def RemoveHTMLTags(data): """ Removes all the HTML tags from a string. **Parameters:** * `data`: the string to be analyzed. """ p = re.compile(r'<[^<]*?>') return p.sub('', data) def FormatDocs(keyword, values, num): names = sorted(values) headers = (num == 2 and [_eventHeaders] or [_styleHeaders])[0] table = (num == 2 and [_eventTable] or [_styleTable])[0] if num == 3: text = "
" + table%(keyword.lower(), keyword.lower()) + "\n\n" else: text = "
" + table for indx in range(2): text += _headerTable%headers[indx] text += "\n\n" for name in names: text += "\n" description = values[name].strip() pythonValue = name.replace("wx", "wx.") if num == 3: colour = "#ff0000" value = "Unavailable" cutValue = pythonValue[3:] if cutValue in _dirWX: try: val = eval(pythonValue) value = "%s"%hex(val) colour = "#0000ff" except AttributeError: value = "Unavailable" else: for packages in _importList: if cutValue in dir(eval(packages)): val = eval("%s.%s"%(packages, cutValue)) value = "%s"%hex(val) colour = "#0000ff" pythonValue = "%s.%s"%(packages, cutValue) break text += _styleTag%pythonValue + "\n" else: text += _eventTag%pythonValue + "\n" text += _description%FormatDescription(description) + "\n" text += "\n" text += "\n
%s%s%s %s %s%s%s
\n\n

" return text def FormatDescription(description): """ Formats a wxWidgets C++ description in a more wxPython-based way. **Parameters:** * `description`: the string description to be formatted. """ description = description.replace("wx", "wx.") description = description.replace("EVT_COMMAND", "wxEVT_COMMAND") description = description.replace("wx.Widgets", "wxWidgets") return description def FormatImages(appearance): text = "


" + _appearanceTable for indx in range(2): text += "\n\n" for key in _platformNames: if indx == 0: src = appearance[key] alt = key + "Appearance" text += _imageTag%(src, src, alt) else: text += _platformTag%key text += "\n" text += "\n\n\n

" return text def FindWindowStyles(text, originalText, widgetName): """ Finds the windows styles and events in the input text. **Parameters:** * `text`: the wxWidgets C++ docs for a particular widget/event, stripped of all HTML tags; * `originalText`: the wxWidgets C++ docs for a particular widget/event, with all HTML tags. """ winStyles, winEvents, winExtra, winAppearance = {}, {}, {}, {} inStyle = inExtra = inEvent = False for line in text: if "following styles:" in line: inStyle = True continue elif "Event macros" in line: inEvent = True continue if "following extra styles:" in line: inExtra = True continue if "Appearance:" in line: winAppearance = FindImages(originalText, widgetName) continue elif not line.strip(): inStyle = inEvent = inExtra = False continue if inStyle: start = line.index(':') windowStyle = line[0:start] styleDescription = line[start+1:] winStyles[windowStyle] = styleDescription elif inEvent: start = line.index(':') eventName = line[0:start] eventDescription = line[start+1:] winEvents[eventName] = eventDescription elif inExtra: start = line.index(':') styleName = line[0:start] styleDescription = line[start+1:] winExtra[styleName] = styleDescription return winStyles, winEvents, winExtra, winAppearance def FindImages(text, widgetName): """ When the wxWidgets docs contain athe control appearance (a screenshot of the control), this method will try and download the images. **Parameters:** * `text`: the wxWidgets C++ docs for a particular widget/event, with all HTML tags. """ winAppearance = {} start = text.find("class='appearance'") if start < 0: return winAppearance imagesDir = GetDocImagesDir() end = start + text.find("") text = text[start:end] split = text.split() for indx, items in enumerate(split): if "src=" in items: possibleImage = items.replace("src=", "").strip() possibleImage = possibleImage.replace('"', "") with urllib.request.urlopen(_trunkURL + possibleImage) as f: stream = f.read() elif "alt=" in items: plat = items.replace("alt=", "").replace("'", "").strip() path = os.path.join(imagesDir, plat, widgetName + ".png") if not os.path.isfile(path): image = wx.ImageFromStream(BytesIO(stream)) image.SaveFile(path, wx.BITMAP_TYPE_PNG) winAppearance[plat] = path return winAppearance def GetCaretPeriod(win = None): """ Attempts to identify the correct caret blinkrate to use in the Demo Code panel. :pram wx.Window win: a window to pass to wx.SystemSettings.GetMetric. :return: a value in milliseconds that indicates the proper period. :rtype: int :raises: ValueError if unable to resolve a proper caret blink rate. """ if '--no-caret-blink' in sys.argv: return 0 try: onmsec = wx.SystemSettings.GetMetric(wx.SYS_CARET_ON_MSEC, win) offmsec = wx.SystemSettings.GetMetric(wx.SYS_CARET_OFF_MSEC, win) # check values aren't -1 if -1 in (onmsec, offmsec): raise ValueError("Unable to determine caret blink rate.") # attempt to average. # (wx systemsettings allows on and off time, but scintilla just takes a single period.) return int((onmsec + offmsec) / 2.0) except AttributeError: # Issue where wx.SYS_CARET_ON/OFF_MSEC is unavailable. raise ValueError("Unable to determine caret blink rate.") #--------------------------------------------------------------------------- # Set up a thread that will scan the wxWidgets docs for window styles, # events and widgets screenshots class InternetThread(Thread): """ Worker thread class to attempt connection to the internet. """ def __init__(self, notifyWindow, selectedClass): Thread.__init__(self) self.notifyWindow = notifyWindow self.selectedClass = selectedClass self.keepRunning = True self.setDaemon(True) self.start() def run(self): """ Run the worker thread. """ # This is the code executing in the new thread. Simulation of # a long process as a simple urllib call try: url = _docsURL % ReplaceCapitals(self.selectedClass) with urllib.request.urlopen(url) as fid: originalText = fid.read().decode("utf-8") text = RemoveHTMLTags(originalText).split("\n") data = FindWindowStyles(text, originalText, self.selectedClass) if not self.keepRunning: return wx.CallAfter(self.notifyWindow.LoadDocumentation, data) except (IOError, urllib.error.HTTPError): # Unable to get to the internet t, v = sys.exc_info()[:2] message = traceback.format_exception_only(t, v) wx.CallAfter(self.notifyWindow.StopDownload, message) except: # Some other strange error... t, v = sys.exc_info()[:2] message = traceback.format_exception_only(t, v) wx.CallAfter(self.notifyWindow.StopDownload, message) #--------------------------------------------------------------------------- # Show how to derive a custom wxLog class class MyLog(wx.Log): def __init__(self, textCtrl, logTime=0): wx.Log.__init__(self) self.tc = textCtrl self.logTime = logTime def DoLogText(self, message): if self.tc: self.tc.AppendText(message + '\n') #--------------------------------------------------------------------------- # 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, style=wx.BORDER_NONE): PythonSTC.__init__(self, parent, -1, style=style) 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') val = self.GetReadOnly() self.SetReadOnly(False) self.SetText(value) self.EmptyUndoBuffer() self.SetSavePoint() self.SetReadOnly(val) def SetEditable(self, val): self.SetReadOnly(not val) 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 separate 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') elif wx.Platform == '__WXMAC__': # TODO: if this looks fine on Linux too, remove the Mac-specific case # and use this whenever OS != MSW. self.StyleSetSpec(stc.STC_STYLE_DEFAULT, 'fore:#000000,back:#FFFFFF,face:Monaco') else: defsize = wx.SystemSettings.GetFont(wx.SYS_ANSI_FIXED_FONT).GetPointSize() self.StyleSetSpec(stc.STC_STYLE_DEFAULT, 'fore:#000000,back:#FFFFFF,face:Courier,size:%d'%defsize) # 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') # Attempt to set caret blink rate. try: self.SetCaretPeriod(GetCaretPeriod(self)) except ValueError: pass 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.mainFrame.pnl.Freeze() self.ReloadDemo() self.mainFrame.pnl.Thaw() 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 AssertionError except: wx.LogMessage("Error creating demo directory: %s" % GetModifiedDirectory()) return else: wx.LogMessage("Created directory for modified demos: %s" % GetModifiedDirectory()) # Save source = self.editor.GetText() with open(modifiedFilename, "wt") as f: f.write(source) busy = wx.BusyInfo("Reloading demo module...") self.demoModules.LoadFromFile(modModified, modifiedFilename) self.ActiveModuleChanged() self.mainFrame.SetTreeModified(True) 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() self.mainFrame.SetTreeModified(False) #--------------------------------------------------------------------------- def opj(path): """Convert paths to the platform-specific separator""" st = os.path.join(*tuple(path.split('/'))) # HACK: on Linux, a leading / gets lost... if path.startswith('/'): st = '/' + st return st def GetDataDir(): """ Return the standard location on this platform for application data """ sp = wx.StandardPaths.Get() return sp.GetUserDataDir() def GetModifiedDirectory(): """ Returns the directory where modified versions of the demo files are stored """ return os.path.join(GetDataDir(), "modified") def GetModifiedFilename(name): """ Returns the filename of the modified version of the specified demo """ if not name.endswith(".py"): name = name + ".py" return os.path.join(GetModifiedDirectory(), name) def GetOriginalFilename(name): """ Returns the filename of the original version of the specified demo """ if not name.endswith(".py"): name = name + ".py" if os.path.isfile(name): return name originalDir = os.getcwd() listDir = os.listdir(originalDir) # Loop over the content of the demo directory for item in listDir: if not os.path.isdir(item): # Not a directory, continue continue dirFile = os.listdir(item) # See if a file called "name" is there if name in dirFile: return os.path.join(item, name) # We must return a string... return "" def DoesModifiedExist(name): """Returns whether the specified demo has a modified copy""" if os.path.exists(GetModifiedFilename(name)): return True else: return False def GetConfig(): if not os.path.exists(GetDataDir()): os.makedirs(GetDataDir()) config = wx.FileConfig( localFilename=os.path.join(GetDataDir(), "options")) return config def MakeDocDirs(): docDir = os.path.join(GetDataDir(), "docs") if not os.path.exists(docDir): os.makedirs(docDir) for plat in _platformNames: imageDir = os.path.join(docDir, "images", plat) if not os.path.exists(imageDir): os.makedirs(imageDir) def GetDocFile(): docFile = os.path.join(GetDataDir(), "docs", "TrunkDocs.pkl") return docFile def GetDocImagesDir(): MakeDocDirs() return os.path.join(GetDataDir(), "docs", "images") def SearchDemo(name, keyword): """ Returns whether a demo contains the search keyword or not. """ with open(GetOriginalFilename(name), "rt") as fid: fullText = fid.read() if fullText.find(keyword) >= 0: return True return False def HuntExternalDemos(): """ Searches for external demos (i.e. packages like AGW) in the wxPython demo sub-directories. In order to be found, these external packages must have a __demo__.py file in their directory. """ externalDemos = {} originalDir = os.getcwd() listDir = os.listdir(originalDir) # Loop over the content of the demo directory for item in listDir: if not os.path.isdir(item): # Not a directory, continue continue dirFile = os.listdir(item) # See if a __demo__.py file is there if "__demo__.py" in dirFile: # Extend sys.path and import the external demos sys.path.append(item) externalDemos[item] = __import__("__demo__") if not externalDemos: # Nothing to import... return {} # Modify the tree items and icons index = 0 for category, demos in _treeList: # We put the external packages right before the # More Windows/Controls item if category == "More Windows/Controls": break index += 1 # Sort and reverse the external demos keys so that they # come back in alphabetical order keys = sorted(externalDemos, reverse=True) # Loop over all external packages for extern in keys: package = externalDemos[extern] # Insert a new package in the _treeList of demos _treeList.insert(index, package.GetDemos()) # Get the recent additions for this package _treeList[0][1].extend(package.GetRecentAdditions()) # Extend the demo bitmaps and the catalog _demoPngs.insert(index+1, extern) images.catalog[extern] = package.GetDemoBitmap() # That's all folks... return externalDemos def LookForExternals(externalDemos, demoName): """ Checks if a demo name is in any of the external packages (like AGW) or if the user clicked on one of the external packages parent items in the tree, in which case it returns the html overview for the package. """ pkg = overview = None # Loop over all the external demos for key, package in externalDemos.items(): # Get the tree item name for the package and its demos treeName, treeDemos = package.GetDemos() # Get the overview for the package treeOverview = package.GetOverview() if treeName == demoName: # The user clicked on the parent tree item, return the overview return pkg, treeOverview elif demoName in treeDemos: # The user clicked on a real demo, return the package return key, overview # No match found, return None for both return pkg, overview #--------------------------------------------------------------------------- class ModuleDictWrapper(object): """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(object): """ 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 = [[dict(), "" , "" , "" , None], [dict(), "" , "" , "" , None]] for i in [modOriginal, modModified]: self.modules[i][0]['__file__'] = \ os.path.join(os.getcwd(), GetOriginalFilename(name)) # 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 with open(filename, "rt") as file_: self.LoadFromSource(modID, file_.read()) 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][2] try: code = compile(source, description, "exec") exec(code, 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] with open(filename, "wt") as file_: file_.write(source) 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(object): """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 isinstance(excType, type): 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 = "" line = line.strip() self.traceback.append( (filename, lineno, "", line) ) excValue = msg try: self.exception_details = str(excValue) except: self.exception_details = "" & 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( cols=2 ) 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, str(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.EXPAND | 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.InsertItem(x, os.path.basename(data[0])) # Filename list.SetItem(x, 1, str(data[1])) # Line list.SetItem(x, 2, str(data[2])) # Function list.SetItem(x, 3, str(data[3])) # Code # Check whether this entry is from the demo module if data[0] == "" or data[0] == "": # 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.Index 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 MainPanel(wx.Panel): """ Just a simple derived panel where we override Freeze and Thaw to work around an issue on wxGTK. """ def Freeze(self): if 'wxMSW' in wx.PlatformInfo: return super(MainPanel, self).Freeze() def Thaw(self): if 'wxMSW' in wx.PlatformInfo: return super(MainPanel, self).Thaw() #--------------------------------------------------------------------------- class DemoTaskBarIcon(TaskBarIcon): TBMENU_RESTORE = wx.NewIdRef() TBMENU_CLOSE = wx.NewIdRef() TBMENU_CHANGE = wx.NewIdRef() TBMENU_REMOVE = wx.NewIdRef() def __init__(self, frame): TaskBarIcon.__init__(self, wx.adv.TBI_DOCK) # wx.adv.TBI_CUSTOM_STATUSITEM self.frame = frame # Set the image icon = self.MakeIcon(images.WXPdemo.GetImage()) self.SetIcon(icon, "wxPython Demo") self.imgidx = 1 # bind some events self.Bind(wx.adv.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 up to 128x128, so leave the source img alone.... icon = wx.Icon(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): wx.CallAfter(self.frame.Close) def OnTaskBarChange(self, evt): names = [ "WXPdemo", "Mondrian", "Pencil", "Carrot" ] name = names[self.imgidx] eImg = getattr(images, name) self.imgidx += 1 if self.imgidx >= len(names): self.imgidx = 0 icon = self.MakeIcon(eImg.Image) 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, title): wx.Frame.__init__(self, parent, -1, title, size = (970, 720), style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE) self.SetMinSize((640,480)) self.pnl = pnl = MainPanel(self) self.mgr = aui.AuiManager() self.mgr.SetManagedWindow(pnl) self.loaded = False self.cwd = os.getcwd() self.curOverview = "" self.demoPage = None self.codePage = None self.shell = None self.firstTime = True self.finddlg = None icon = images.WXPdemo.GetIcon() self.SetIcon(icon) try: self.tbicon = DemoTaskBarIcon(self) except: self.tbicon = None self.otherWin = None self.allowDocs = False self.downloading = False self.internetThread = None self.downloadImage = 2 self.sendDownloadError = True self.downloadTimer = wx.Timer(self, wx.ID_ANY) 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.Bind(wx.EVT_TIMER, self.OnDownloadTimer, self.downloadTimer) self.Centre(wx.BOTH) self.statusBar = self.CreateStatusBar(2)#, wx.ST_SIZEGRIP self.statusBar.SetStatusWidths([-2, -1]) statusText = "Welcome to wxPython %s" % wx.VERSION_STRING self.statusBar.SetStatusText(statusText, 0) self.downloadGauge = wx.Gauge(self.statusBar, wx.ID_ANY, 50) self.downloadGauge.SetToolTip("Downloading Docs...") self.downloadGauge.Hide() self.sizeChanged = False self.Reposition() self.statusBar.Bind(wx.EVT_SIZE, self.OnStatusBarSize) self.statusBar.Bind(wx.EVT_IDLE, self.OnStatusBarIdle) self.dying = False self.skipLoad = False self.allowAuiFloating = False def EmptyHandler(evt): pass self.ReadConfigurationFile() self.externalDemos = HuntExternalDemos() # Create a Notebook self.nb = wx.Notebook(pnl, -1, style=wx.CLIP_CHILDREN) imgList = wx.ImageList(16, 16) for png in ["overview", "code", "demo"]: bmp = images.catalog[png].GetBitmap() imgList.Add(bmp) for indx in range(9): bmp = images.catalog["spinning_nb%d"%indx].GetBitmap() imgList.Add(bmp) self.nb.AssignImageList(imgList) self.BuildMenuBar() self.finddata = wx.FindReplaceData() self.finddata.SetFlags(wx.FR_DOWN) # Create a TreeCtrl leftPanel = wx.Panel(pnl, style=wx.TAB_TRAVERSAL|wx.CLIP_CHILDREN) self.treeMap = {} self.searchItems = {} self.tree = wxPythonDemoTree(leftPanel) self.filter = wx.SearchCtrl(leftPanel, style=wx.TE_PROCESS_ENTER) self.filter.ShowCancelButton(True) self.filter.Bind(wx.EVT_TEXT, self.RecreateTree) self.filter.Bind(wx.EVT_SEARCHCTRL_CANCEL_BTN, lambda e: self.filter.SetValue('')) self.filter.Bind(wx.EVT_TEXT_ENTER, self.OnSearch) if 'gtk3' in wx.PlatformInfo: # Something is wrong with the bestsize of the SearchCtrl, so for now # let's set it based on the size of a TextCtrl. txt = wx.TextCtrl(leftPanel) bs = txt.GetBestSize() txt.DestroyLater() self.filter.SetMinSize((-1, bs.height+4)) searchMenu = wx.Menu() item = searchMenu.AppendRadioItem(-1, "Sample Name") self.Bind(wx.EVT_MENU, self.OnSearchMenu, item) item = searchMenu.AppendRadioItem(-1, "Sample Content") self.Bind(wx.EVT_MENU, self.OnSearchMenu, item) self.filter.SetMenu(searchMenu) self.RecreateTree() self.tree.SetExpansionState(self.expansionState) self.tree.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded) self.tree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed) self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged) self.tree.Bind(wx.EVT_LEFT_DOWN, self.OnTreeLeftDown) # Set up a wx.html.HtmlWindow on the Overview Notebook page # for displaying info about each sample in the demo. self.ovr = wx.html.HtmlWindow(self.nb, -1, size=(400, 400)) self.nb.AddPage(self.ovr, self.overviewText, imageId=0) if "gtk2" in wx.PlatformInfo or "gtk3" in wx.PlatformInfo: self.ovr.SetStandardFonts() self.SetOverview(self.overviewText, mainOverview) # Set up a log window self.log = wx.TextCtrl(pnl, -1, style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL) if wx.Platform == "__WXMAC__": self.log.MacCheckSpelling(False) # Set the wxWindows log target to be this textctrl #wx.Log.SetActiveTarget(wx.LogTextCtrl(self.log)) # 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.Bind(wx.EVT_ACTIVATE, self.OnActivate) wx.GetApp().Bind(wx.EVT_ACTIVATE_APP, self.OnAppActivate) # add the windows to the splitter and split it. leftBox = wx.BoxSizer(wx.VERTICAL) leftBox.Add(self.tree, 1, wx.EXPAND) leftBox.Add(wx.StaticText(leftPanel, label = "Filter Demos:"), 0, wx.TOP|wx.LEFT, 5) leftBox.Add(self.filter, 0, wx.EXPAND|wx.ALL, 5) if 'wxMac' in wx.PlatformInfo: leftBox.Add((5,5)) # Make sure there is room for the focus ring leftPanel.SetSizer(leftBox) # select initial items self.nb.SetSelection(0) self.tree.SelectItem(self.root) # 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) # Use the aui manager to set up everything self.mgr.AddPane(self.nb, aui.AuiPaneInfo().CenterPane().Name("Notebook")) self.mgr.AddPane(leftPanel, aui.AuiPaneInfo(). Left().Layer(2).BestSize((240, -1)). MinSize((240, -1)). Floatable(self.allowAuiFloating).FloatingSize((240, 700)). Caption("wxPython Demos"). CloseButton(False). Name("DemoTree")) self.mgr.AddPane(self.log, aui.AuiPaneInfo(). Bottom().BestSize((-1, 150)). MinSize((-1, 140)). Floatable(self.allowAuiFloating).FloatingSize((500, 160)). Caption("Demo Log Messages"). CloseButton(False). Name("LogWindow")) self.auiConfigurations[DEFAULT_PERSPECTIVE] = self.mgr.SavePerspective() self.mgr.Update() self.mgr.SetAGWFlags(self.mgr.GetAGWFlags() ^ aui.AUI_MGR_TRANSPARENT_DRAG) def ReadConfigurationFile(self): self.auiConfigurations = {} self.expansionState = [0, 1] config = GetConfig() val = config.Read('ExpansionState') if val: self.expansionState = eval(val) val = config.Read('AUIPerspectives') if val: self.auiConfigurations = eval(val) val = config.Read('AllowDownloads') if val: self.allowDocs = eval(val) val = config.Read('AllowAUIFloating') if val: self.allowAuiFloating = eval(val) MakeDocDirs() pickledFile = GetDocFile() if not os.path.isfile(pickledFile): self.pickledData = {} return with open(pickledFile, "rb") as fid: try: self.pickledData = pickle.load(fid) except: self.pickledData = {} def BuildMenuBar(self): # Make a File menu self.mainmenu = wx.MenuBar() menu = wx.Menu() item = menu.Append(-1, '&Redirect Output', 'Redirect print statements to a window', wx.ITEM_CHECK) self.Bind(wx.EVT_MENU, self.OnToggleRedirect, item) exitItem = wx.MenuItem(menu, wx.ID_EXIT, 'E&xit\tCtrl-Q', 'Get the heck outta here!') exitItem.SetBitmap(images.catalog['exit'].GetBitmap()) menu.Append(exitItem) self.Bind(wx.EVT_MENU, self.OnFileExit, exitItem) self.mainmenu.Append(menu, '&File') # Make a Demo menu menu = wx.Menu() for indx, item in enumerate(_treeList[:-1]): menuItem = wx.MenuItem(menu, -1, item[0]) submenu = wx.Menu() for childItem in item[1]: mi = submenu.Append(-1, childItem) self.Bind(wx.EVT_MENU, self.OnDemoMenu, mi) menuItem.SetBitmap(images.catalog[_demoPngs[indx+1]].GetBitmap()) menuItem.SetSubMenu(submenu) menu.Append(menuItem) self.mainmenu.Append(menu, '&Demo') # Make an Option menu menu = wx.Menu() item = wx.MenuItem(menu, -1, 'Allow download of docs', 'Docs for window styles and events from the web', wx.ITEM_CHECK) menu.Append(item) item.Check(self.allowDocs) self.Bind(wx.EVT_MENU, self.OnAllowDownload, item) item = wx.MenuItem(menu, -1, 'Delete saved docs', 'Deletes the pickle file where docs are stored') item.SetBitmap(images.catalog['deletedocs'].GetBitmap()) menu.Append(item) self.Bind(wx.EVT_MENU, self.OnDeleteDocs, item) menu.AppendSeparator() item = wx.MenuItem(menu, -1, 'Allow floating panes', 'Allows the demo panes to be floated using wxAUI', wx.ITEM_CHECK) menu.Append(item) item.Check(self.allowAuiFloating) self.Bind(wx.EVT_MENU, self.OnAllowAuiFloating, item) auiPerspectives = sorted(self.auiConfigurations) perspectivesMenu = wx.Menu() item = wx.MenuItem(perspectivesMenu, -1, DEFAULT_PERSPECTIVE, "Load startup default perspective", wx.ITEM_RADIO) self.Bind(wx.EVT_MENU, self.OnAUIPerspectives, item) perspectivesMenu.Append(item) for indx, key in enumerate(auiPerspectives): if key == DEFAULT_PERSPECTIVE: continue item = wx.MenuItem(perspectivesMenu, -1, key, "Load user perspective %d"%indx, wx.ITEM_RADIO) perspectivesMenu.Append(item) self.Bind(wx.EVT_MENU, self.OnAUIPerspectives, item) menu.AppendSubMenu(perspectivesMenu, "&AUI Perspectives") self.perspectives_menu = perspectivesMenu item = wx.MenuItem(menu, -1, 'Save Perspective', 'Save AUI perspective') item.SetBitmap(images.catalog['saveperspective'].GetBitmap()) menu.Append(item) self.Bind(wx.EVT_MENU, self.OnSavePerspective, item) item = wx.MenuItem(menu, -1, 'Delete Perspective', 'Delete AUI perspective') item.SetBitmap(images.catalog['deleteperspective'].GetBitmap()) menu.Append(item) self.Bind(wx.EVT_MENU, self.OnDeletePerspective, item) menu.AppendSeparator() item = wx.MenuItem(menu, -1, 'Restore Tree Expansion', 'Restore the initial tree expansion state') item.SetBitmap(images.catalog['expansion'].GetBitmap()) menu.Append(item) self.Bind(wx.EVT_MENU, self.OnTreeExpansion, item) self.mainmenu.Append(menu, '&Options') self.options_menu = menu # Make a Help menu menu = wx.Menu() findItem = wx.MenuItem(menu, -1, '&Find\tCtrl-F', 'Find in the Demo Code') findItem.SetBitmap(images.catalog['find'].GetBitmap()) if 'wxMac' not in wx.PlatformInfo: findNextItem = wx.MenuItem(menu, -1, 'Find &Next\tF3', 'Find Next') else: findNextItem = wx.MenuItem(menu, -1, 'Find &Next\tCtrl-G', 'Find Next') findNextItem.SetBitmap(images.catalog['findnext'].GetBitmap()) menu.Append(findItem) menu.Append(findNextItem) menu.AppendSeparator() shellItem = wx.MenuItem(menu, -1, 'Open Py&Shell Window\tF5', 'An interactive interpreter window with the demo app and frame objects in the namesapce') shellItem.SetBitmap(images.catalog['pyshell'].GetBitmap()) menu.Append(shellItem) inspToolItem = wx.MenuItem(menu, -1, 'Open &Widget Inspector\tF6', 'A tool that lets you browse the live widgets and sizers in an application') inspToolItem.SetBitmap(images.catalog['inspect'].GetBitmap()) menu.Append(inspToolItem) if 'wxMac' not in wx.PlatformInfo: menu.AppendSeparator() helpItem = menu.Append(wx.ID_ABOUT, '&About wxPython Demo', 'wxPython RULES!!!') self.Bind(wx.EVT_MENU, self.OnOpenShellWindow, shellItem) self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, inspToolItem) 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.EnableAUIMenu() if False: # This is another way to set Accelerators, in addition to # using the '\t' syntax in the menu items. aTable = wx.AcceleratorTable([(wx.ACCEL_ALT, ord('X'), exitItem.GetId()), (wx.ACCEL_CTRL, ord('H'), helpItem.GetId()), (wx.ACCEL_CTRL, ord('F'), findItem.GetId()), (wx.ACCEL_NORMAL, wx.WXK_F3, findNextItem.GetId()), (wx.ACCEL_NORMAL, wx.WXK_F9, shellItem.GetId()), ]) self.SetAcceleratorTable(aTable) #--------------------------------------------- def RecreateTree(self, evt=None): # Catch the search type (name or content) searchMenu = self.filter.GetMenu().GetMenuItems() fullSearch = searchMenu[1].IsChecked() if evt: if fullSearch: # Do not`scan all the demo files for every char # the user input, use wx.EVT_TEXT_ENTER instead return expansionState = self.tree.GetExpansionState() current = None item = self.tree.GetSelection() if item: prnt = self.tree.GetItemParent(item) if prnt: current = (self.tree.GetItemText(item), self.tree.GetItemText(prnt)) self.tree.Freeze() self.tree.DeleteAllItems() self.root = self.tree.AddRoot("wxPython Overview") self.tree.SetItemImage(self.root, 0) self.tree.SetItemData(self.root, 0) treeFont = self.tree.GetFont() catFont = self.tree.GetFont() # The native treectrl on MSW has a bug where it doesn't draw # all of the text for an item if the font is larger than the # default. It seems to be clipping the item's label as if it # was the size of the same label in the default font. if USE_CUSTOMTREECTRL or 'wxMSW' not in wx.PlatformInfo: treeFont.SetPointSize(treeFont.GetPointSize()+2) treeFont.SetWeight(wx.FONTWEIGHT_BOLD) catFont.SetWeight(wx.FONTWEIGHT_BOLD) self.tree.SetItemFont(self.root, treeFont) firstChild = None selectItem = None filter = self.filter.GetValue() count = 0 for category, items in _treeList: count += 1 if filter: if fullSearch: items = self.searchItems[category] else: items = [item for item in items if filter.lower() in item.lower()] if items: child = self.tree.AppendItem(self.root, category, image=count) self.tree.SetItemFont(child, catFont) self.tree.SetItemData(child, count) if not firstChild: firstChild = child for childItem in items: image = count if DoesModifiedExist(childItem): image = len(_demoPngs) theDemo = self.tree.AppendItem(child, childItem, image=image) self.tree.SetItemData(theDemo, count) self.treeMap[childItem] = theDemo if current and (childItem, category) == current: selectItem = theDemo self.tree.Expand(self.root) if firstChild: self.tree.Expand(firstChild) if filter: self.tree.ExpandAll() elif expansionState: self.tree.SetExpansionState(expansionState) if selectItem: self.skipLoad = True self.tree.SelectItem(selectItem) self.skipLoad = False self.tree.Thaw() self.searchItems = {} def OnStatusBarSize(self, evt): self.Reposition() # for normal size events # Set a flag so the idle time handler will also do the repositioning. # It is done this way to get around a buglet where GetFieldRect is not # accurate during the EVT_SIZE resulting from a frame maximize. self.sizeChanged = True def OnStatusBarIdle(self, evt): if self.sizeChanged: self.Reposition() # reposition the download gauge def Reposition(self): # rect = self.statusBar.GetFieldRect(1) # self.downloadGauge.SetPosition((rect.x+2, rect.y+2)) # self.downloadGauge.SetSize((rect.width-4, rect.height-4)) self.sizeChanged = False def OnSearchMenu(self, event): # Catch the search type (name or content) searchMenu = self.filter.GetMenu().GetMenuItems() fullSearch = searchMenu[1].IsChecked() if fullSearch: self.OnSearch() else: self.RecreateTree() def OnSearch(self, event=None): value = self.filter.GetValue() if not value: self.RecreateTree() return wx.BeginBusyCursor() for category, items in _treeList: self.searchItems[category] = [] for childItem in items: if SearchDemo(childItem, value): self.searchItems[category].append(childItem) wx.EndBusyCursor() self.RecreateTree() def SetTreeModified(self, modified): item = self.tree.GetSelection() if modified: image = len(_demoPngs) else: image = self.tree.GetItemData(item) self.tree.SetItemImage(item, image) def WriteText(self, text): if text[-1:] == '\n': text = text[:-1] wx.LogMessage(text) def write(self, txt): self.WriteText(txt) #--------------------------------------------- def OnItemExpanded(self, event): item = event.GetItem() wx.LogMessage("OnItemExpanded: %s" % self.tree.GetItemText(item)) event.Skip() #--------------------------------------------- def OnItemCollapsed(self, event): item = event.GetItem() wx.LogMessage("OnItemCollapsed: %s" % self.tree.GetItemText(item)) event.Skip() #--------------------------------------------- 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(): self.SetOverview(self.tree.GetItemText(item)+" Overview", self.curOverview) event.Skip() #--------------------------------------------- def OnSelChanged(self, event): if self.dying or not self.loaded or self.skipLoad: return self.StopDownload() item = event.GetItem() itemText = self.tree.GetItemText(item) self.LoadDemo(itemText) self.StartDownload() #--------------------------------------------- def LoadDemo(self, demoName): try: wx.BeginBusyCursor() self.pnl.Freeze() 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: if os.path.exists(GetOriginalFilename(demoName)): wx.LogMessage("Loading demo %s.py..." % demoName) self.demoModules = DemoModules(demoName) self.LoadDemoSource() else: package, overview = LookForExternals(self.externalDemos, demoName) if package: wx.LogMessage("Loading demo %s.py..." % ("%s/%s"%(package, demoName))) self.demoModules = DemoModules("%s/%s"%(package, demoName)) self.LoadDemoSource() elif overview: self.SetOverview(demoName, overview) self.codePage = None self.UpdateNotebook(0) else: self.SetOverview("wxPython", mainOverview) self.codePage = None self.UpdateNotebook(0) finally: wx.EndBusyCursor() self.pnl.Thaw() #--------------------------------------------- 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) bg = self.nb.GetThemeBackgroundColour() if bg: self.demoPage.SetBackgroundColour(bg) 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: # change 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() #--------------------------------------------- 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 self.pnl.Freeze() 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, imageId=nb.GetPageCount()) if debug: wx.LogMessage("DBG: ADDED %s" % pageText) else: if nb.GetPage(pagePos) != page: # Reload an existing page nb.DeletePage(pagePos) nb.InsertPage(pagePos, page, pageText, imageId=pagePos) 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() UpdatePage(self.codePage, "Demo Code") UpdatePage(self.demoPage, "Demo") if select >= 0 and select < nb.GetPageCount(): nb.SetSelection(select) self.pnl.Thaw() #--------------------------------------------- def SetOverview(self, name, text): self.curOverview = text lead = text[:6] if lead != '' and lead != '': text = '
'.join(text.split('\n')) # if wx.USE_UNICODE: # text = text.decode('iso8859_1') self.ovr.SetPage(text) self.nb.SetPageText(0, os.path.split(name)[1]) #--------------------------------------------- def StartDownload(self): if self.downloading or not self.allowDocs: return item = self.tree.GetSelection() if self.tree.ItemHasChildren(item): return itemText = self.tree.GetItemText(item) if itemText in self.pickledData: self.LoadDocumentation(self.pickledData[itemText]) return text = self.curOverview text += "

Checking for documentation on the wxWidgets website, please stand by...
" lead = text[:6] if lead != '' and lead != '': text = '
'.join(text.split('\n')) self.ovr.SetPage(text) self.downloadTimer.Start(100) self.downloadGauge.Show() self.Reposition() self.downloading = True self.internetThread = InternetThread(self, itemText) #--------------------------------------------- def StopDownload(self, error=None): self.downloadTimer.Stop() if not self.downloading: return if error: if self.sendDownloadError: self.log.AppendText("Warning: problems in downloading documentation from the wxWidgets website.\n") self.log.AppendText("Error message from the documentation downloader was:\n") self.log.AppendText("\n".join(error)) self.sendDownloadError = False self.nb.SetPageImage(0, 0) self.internetThread.keepRunning = False self.internetThread = None self.downloading = False self.downloadGauge.Hide() self.Reposition() text = self.curOverview lead = text[:6] if lead != '' and lead != '': text = '
'.join(text.split('\n')) self.ovr.SetPage(text) #--------------------------------------------- def LoadDocumentation(self, data): text = self.curOverview addHtml = False if '' not in text and '' not in text: text = '
'.join(text.split('\n')) styles, events, extra, appearance = data if appearance: text += FormatImages(appearance) for names, values in zip(["Styles", "Extra Styles", "Events"], [styles, extra, events]): if not values: continue headers = (names == "Events" and [2] or [3])[0] text += "

" + FormatDocs(names, values, headers) item = self.tree.GetSelection() itemText = self.tree.GetItemText(item) self.pickledData[itemText] = data self.StopDownload() self.ovr.SetPage(text) #print("load time: ", time.time() - start) # Menu methods def OnFileExit(self, *event): self.Close() def OnToggleRedirect(self, event): app = wx.GetApp() if event.IsChecked(): 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 OnAllowDownload(self, event): self.allowDocs = event.IsChecked() if self.allowDocs: self.StartDownload() else: self.StopDownload() def OnDeleteDocs(self, event): deleteMsg = "You are about to delete the downloaded documentation.\n" + \ "Do you want to continue?" dlg = wx.MessageDialog(self, deleteMsg, "wxPython Demo", wx.YES_NO | wx.NO_DEFAULT| wx.ICON_QUESTION) result = dlg.ShowModal() if result == wx.ID_NO: dlg.Destroy() return dlg.Destroy() busy = wx.BusyInfo("Deleting downloaded data...") wx.SafeYield() pickledFile = GetDocFile() docDir = os.path.split(pickledFile)[0] if os.path.exists(docDir): shutil.rmtree(docDir, ignore_errors=True) self.pickledData = {} del busy self.sendDownloadError = True def OnAllowAuiFloating(self, event): self.allowAuiFloating = event.IsChecked() for pane in self.mgr.GetAllPanes(): if pane.name != "Notebook": pane.Floatable(self.allowAuiFloating) self.EnableAUIMenu() self.mgr.Update() def EnableAUIMenu(self): menuItems = self.options_menu.GetMenuItems() for indx in range(4, len(menuItems)-1): item = menuItems[indx] item.Enable(self.allowAuiFloating) def OnAUIPerspectives(self, event): perspective = self.perspectives_menu.GetLabel(event.GetId()) self.mgr.LoadPerspective(self.auiConfigurations[perspective]) self.mgr.Update() def OnSavePerspective(self, event): dlg = wx.TextEntryDialog(self, "Enter a name for the new perspective:", "AUI Configuration") dlg.SetValue(("Perspective %d")%(len(self.auiConfigurations)+1)) if dlg.ShowModal() != wx.ID_OK: return perspectiveName = dlg.GetValue() menuItems = self.perspectives_menu.GetMenuItems() for item in menuItems: if item.GetLabel() == perspectiveName: wx.MessageBox("The selected perspective name:\n\n%s\n\nAlready exists."%perspectiveName, "Error", style=wx.ICON_ERROR) return item = wx.MenuItem(self.perspectives_menu, -1, dlg.GetValue(), "Load user perspective %d"%(len(self.auiConfigurations)+1), wx.ITEM_RADIO) self.Bind(wx.EVT_MENU, self.OnAUIPerspectives, item) self.perspectives_menu.Append(item) item.Check(True) self.auiConfigurations.update({dlg.GetValue(): self.mgr.SavePerspective()}) def OnDeletePerspective(self, event): menuItems = self.perspectives_menu.GetMenuItems() lst = [] loadDefault = False for indx, item in enumerate(menuItems): if indx > 0: lst.append(item.GetLabel()) dlg = wx.MultiChoiceDialog(self, "Please select the perspectives\nyou would like to delete:", "Delete AUI Perspectives", lst) if dlg.ShowModal() == wx.ID_OK: selections = dlg.GetSelections() strings = [lst[x] for x in selections] for sel in strings: self.auiConfigurations.pop(sel) item = menuItems[lst.index(sel)+1] if item.IsChecked(): loadDefault = True self.perspectives_menu.GetMenuItems()[0].Check(True) self.perspectives_menu.DeleteItem(item) lst.remove(sel) if loadDefault: self.mgr.LoadPerspective(self.auiConfigurations[DEFAULT_PERSPECTIVE]) self.mgr.Update() def OnTreeExpansion(self, event): self.tree.SetExpansionState(self.expansionState) def OnHelpAbout(self, event): from About import MyAboutBox about = MyAboutBox(self) about.ShowModal() about.Destroy() def OnHelpFind(self, event): if self.finddlg is not None: return self.nb.SetSelection(1) self.finddlg = wx.FindReplaceDialog(self, self.finddata, "Find", wx.FR_NOMATCHCASE | wx.FR_NOWHOLEWORD) self.finddlg.Show(True) def OnUpdateFindItems(self, evt): evt.Enable(self.finddlg is None) def OnFind(self, event): editor = self.codePage.editor self.nb.SetSelection(1) end = editor.GetLastPosition() textstring = editor.GetRange(0, end).lower() findstring = self.finddata.GetFindString().lower() 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 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', wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() if self.finddlg: if loc == -1: self.finddlg.SetFocus() return else: self.finddlg.Destroy() self.finddlg = None editor.ShowPosition(loc) editor.SetSelection(loc, loc + len(findstring)) def OnFindNext(self, event): if self.finddata.GetFindString(): self.OnFind(event) else: self.OnHelpFind(event) 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 OnOpenWidgetInspector(self, evt): # Activate the widget inspection tool, giving it a widget to preselect # in the tree. Use either the one under the cursor, if any, or this # frame. from wx.lib.inspection import InspectionTool wnd = wx.FindWindowAtPointer() if not wnd: wnd = self InspectionTool().Show(wnd, True) #--------------------------------------------- def OnCloseWindow(self, event): self.mgr.UnInit() self.dying = True self.demoPage = None self.codePage = None self.mainmenu = None self.StopDownload() if self.tbicon is not None: self.tbicon.Destroy() config = GetConfig() config.Write('ExpansionState', str(self.tree.GetExpansionState())) config.Write('AUIPerspectives', str(self.auiConfigurations)) config.Write('AllowDownloads', str(self.allowDocs)) config.Write('AllowAUIFloating', str(self.allowAuiFloating)) config.Flush() MakeDocDirs() pickledFile = GetDocFile() with open(pickledFile, "wb") as fid: pickle.dump(self.pickledData, fid, pickle.HIGHEST_PROTOCOL) self.Destroy() #--------------------------------------------- def OnIdle(self, event): if self.otherWin: self.otherWin.Raise() self.demoPage = self.otherWin self.otherWin = None #--------------------------------------------- def OnDownloadTimer(self, event): self.downloadGauge.Pulse() self.downloadImage += 1 if self.downloadImage > 9: self.downloadImage = 3 self.nb.SetPageImage(0, self.downloadImage) ## wx.SafeYield() #--------------------------------------------- def ShowTip(self): config = GetConfig() showTipText = config.Read("tips") if showTipText: showTip, index = eval(showTipText) else: showTip, index = (1, 0) # if showTip: # tp = wx.CreateFileTipProvider(opj("data/tips.txt"), index) # showTip = wx.ShowTip(self, tp) # index = tp.GetCurrentTip() # config.Write("tips", str( (showTip, index) )) # config.Flush() #--------------------------------------------- def OnDemoMenu(self, event): try: selectedDemo = self.treeMap[self.mainmenu.GetLabel(event.GetId())] except: selectedDemo = None if selectedDemo: self.tree.SelectItem(selectedDemo) self.tree.EnsureVisible(selectedDemo) #--------------------------------------------- def OnIconfiy(self, evt): wx.LogMessage("OnIconfiy: %s" % evt.IsIconized()) evt.Skip() #--------------------------------------------- def OnMaximize(self, evt): 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(SplashScreen): def __init__(self): bmp = wx.Image(opj("bitmaps/splash.png")).ConvertToBitmap() SplashScreen.__init__(self, bmp, wx.adv.SPLASH_CENTRE_ON_SCREEN | wx.adv.SPLASH_TIMEOUT, 5000, None, -1) self.Bind(wx.EVT_CLOSE, self.OnClose) self.fc = wx.CallLater(1000, self.ShowMain) def OnClose(self, evt): # 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() wx.CallAfter(frame.ShowTip) #--------------------------------------------------------------------------- from wx.lib.mixins.treemixin import ExpansionState if USE_CUSTOMTREECTRL: import wx.lib.agw.customtreectrl as CT TreeBaseClass = CT.CustomTreeCtrl else: TreeBaseClass = wx.TreeCtrl class wxPythonDemoTree(ExpansionState, TreeBaseClass): def __init__(self, parent): TreeBaseClass.__init__(self, parent, style=wx.TR_DEFAULT_STYLE| wx.TR_HAS_VARIABLE_ROW_HEIGHT) self.BuildTreeImageList() if USE_CUSTOMTREECTRL: self.SetSpacing(10) self.SetWindowStyle(self.GetWindowStyle() & ~wx.TR_LINES_AT_ROOT) self.SetInitialSize((100,80)) def AppendItem(self, parent, text, image=-1, wnd=None): if USE_CUSTOMTREECTRL: item = TreeBaseClass.AppendItem(self, parent, text, image=image, wnd=wnd) else: item = TreeBaseClass.AppendItem(self, parent, text, image=image) return item def BuildTreeImageList(self): imgList = wx.ImageList(16, 16) for png in _demoPngs: imgList.Add(images.catalog[png].GetBitmap()) # add the image for modified demos. imgList.Add(images.catalog["custom"].GetBitmap()) self.AssignImageList(imgList) def GetItemIdentity(self, item): return self.GetItemData(item) #--------------------------------------------------------------------------- class MyApp(wx.App, wx.lib.mixins.inspection.InspectionMixin): def OnInit(self): # Check runtime version if version.VERSION_STRING != wx.VERSION_STRING: wx.MessageBox(caption="Warning", message="You're using version %s of wxPython, but this copy of the demo was written for version %s.\n" "There may be some version incompatibilities..." % (wx.VERSION_STRING, version.VERSION_STRING)) self.InitInspection() # for the InspectionMixin base class # Now that we've warned the user about possible problems, # lets import images import images as i global images images = i # For debugging #self.SetAssertMode(wx.APP_ASSERT_DIALOG|wx.APP_ASSERT_EXCEPTION) wx.SystemOptions.SetOption("mac.window-plain-transition", 1) self.SetAppName("wxPyDemo") # Create and show the splash screen. It will then create and # show the main frame when it is time to do so. Normally when # using a SplashScreen you would create it, show it and then # continue on with the application'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 def InitLocale(self): super().InitLocale() self._locale = wx.Locale(wx.LANGUAGE_ENGLISH) #--------------------------------------------------------------------------- def main(): try: demoPath = os.path.dirname(__file__) os.chdir(demoPath) except: pass app = MyApp(False) app.MainLoop() #--------------------------------------------------------------------------- mainOverview = """

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. Or anyone can contribute fixes or enhancements 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.

This demo is not only a collection of test cases for wxPython, but is also designed to help you learn about and how to use wxPython. Each sample is listed in the tree control on the left. When a sample is selected in the tree then a module is loaded and run (usually in a tab of this notebook,) and the source code of the module is loaded in another tab for you to browse and learn from. """ #---------------------------------------------------------------------------- #---------------------------------------------------------------------------- if __name__ == '__main__': __name__ = 'Main' main() #----------------------------------------------------------------------------