]> git.saurik.com Git - wxWidgets.git/blob - wxPython/demo/Main.py
check for wxHAS_HUGE_FILES
[wxWidgets.git] / wxPython / demo / Main.py
1 #!/bin/env python
2 #----------------------------------------------------------------------------
3 # Name: Main.py
4 # Purpose: Testing lots of stuff, controls, window types, etc.
5 #
6 # Author: Robin Dunn
7 #
8 # Created: A long time ago, in a galaxy far, far away...
9 # RCS-ID: $Id$
10 # Copyright: (c) 1999 by Total Control Software
11 # Licence: wxWindows license
12 #----------------------------------------------------------------------------
13
14 # FIXME List:
15 # * Problems with flickering related to ERASE_BACKGROUND
16 # and the splitters. Might be a problem with this 2.5 beta...?
17 # UPDATE: can't see on 2.5.2 GTK - maybe just a faster machine :)
18 # * Demo Code menu?
19 # * Annoying switching between tabs and resulting flicker
20 # how to replace a page in the notebook without deleting/adding?
21 # Where is SetPage!? tried freeze...tried reparent of dummy panel....
22
23 # TODO List:
24 # * UI design more prefessional
25 # * save file positions (new field in demoModules) (@ LoadDemoSource)
26 # * Update main overview
27
28 # * Why don't we move _treeList into a separate module
29
30 import sys, os, time, traceback, types
31
32 import wx # This module uses the new wx namespace
33 import wx.html
34
35 import images
36
37 # For debugging
38 ##wx.Trap();
39 ##print "wx.VERSION_STRING = ", wx.VERSION_STRING
40 ##print os.getpid();
41 ##raw_input("Press Enter...")
42
43
44 #---------------------------------------------------------------------------
45
46
47 _treeList = [
48 # new stuff
49 ('Recent Additions/Updates', [
50 'StockButtons',
51 'Ticker',
52 'Choicebook',
53 ]),
54
55 # managed windows == things with a (optional) caption you can close
56 ('Frames and Dialogs', [
57 'Dialog',
58 'Frame',
59 'MDIWindows',
60 'MiniFrame',
61 'Wizard',
62 ]),
63
64 # the common dialogs
65 ('Common Dialogs', [
66 'ColourDialog',
67 'DirDialog',
68 'FileDialog',
69 'FindReplaceDialog',
70 'FontDialog',
71 'MessageDialog',
72 'PageSetupDialog',
73 'PrintDialog',
74 'ProgressDialog',
75 'SingleChoiceDialog',
76 'TextEntryDialog',
77 ]),
78
79 # dialogs from libraries
80 ('More Dialogs', [
81 'ImageBrowser',
82 'MultipleChoiceDialog',
83 'ScrolledMessageDialog',
84 ]),
85
86 # core controls
87 ('Core Windows/Controls', [
88 'BitmapButton',
89 'Button',
90 'CheckBox',
91 'CheckListBox',
92 'Choice',
93 'Choicebook',
94 'ComboBox',
95 'Gauge',
96 'Grid',
97 'Grid_MegaExample',
98 'ListBox',
99 'ListCtrl',
100 'ListCtrl_virtual',
101 'Listbook',
102 'Menu',
103 'Notebook',
104 'PopupMenu',
105 'PopupWindow',
106 'RadioBox',
107 'RadioButton',
108 'SashWindow',
109 'ScrolledWindow',
110 'Slider',
111 'SpinButton',
112 'SpinCtrl',
113 'SplitterWindow',
114 'StaticBitmap',
115 'StaticText',
116 'StatusBar',
117 'StockButtons',
118 'TextCtrl',
119 'ToggleButton',
120 'ToolBar',
121 'TreeCtrl',
122 'Validator',
123 ]),
124
125 ('Custom Controls', [
126 'AnalogClockWindow',
127 'ColourSelect',
128 'Editor',
129 'GenericButtons',
130 'GenericDirCtrl',
131 'LEDNumberCtrl',
132 'MultiSash',
133 'PopupControl',
134 'PyColourChooser',
135 'TreeListCtrl',
136 ]),
137
138 # controls coming from other libraries
139 ('More Windows/Controls', [
140 'ActiveX_FlashWindow',
141 'ActiveX_IEHtmlWindow',
142 'ActiveX_PDFWindow',
143 #'RightTextCtrl', deprecated as we have wxTE_RIGHT now.
144 'Calendar',
145 'CalendarCtrl',
146 'ContextHelp',
147 'DynamicSashWindow',
148 'EditableListBox',
149 'FancyText',
150 'FileBrowseButton',
151 'FloatBar',
152 'FloatCanvas',
153 'HtmlWindow',
154 'IntCtrl',
155 'MVCTree',
156 'MaskedEditControls',
157 'MaskedNumCtrl',
158 'MimeTypesManager',
159 'PyCrust',
160 'PyPlot',
161 'PyShell',
162 'ScrolledPanel',
163 'SplitTree',
164 'StyledTextCtrl_1',
165 'StyledTextCtrl_2',
166 'TablePrint',
167 'Throbber',
168 'Ticker',
169 'TimeCtrl',
170 'VListBox',
171 ]),
172
173 # How to lay out the controls in a frame/dialog
174 ('Window Layout', [
175 'GridBagSizer',
176 'LayoutAnchors',
177 'LayoutConstraints',
178 'Layoutf',
179 'RowColSizer',
180 'ScrolledPanel',
181 'Sizers',
182 'XmlResource',
183 'XmlResourceHandler',
184 'XmlResourceSubclass',
185 ]),
186
187 # ditto
188 ('Process and Events', [
189 'EventManager',
190 'KeyEvents',
191 'Process',
192 'PythonEvents',
193 'Threads',
194 'Timer',
195 ##'infoframe', # needs better explaination and some fixing
196 ]),
197
198 # Clipboard and DnD
199 ('Clipboard and DnD', [
200 'CustomDragAndDrop',
201 'DragAndDrop',
202 'URLDragAndDrop',
203 ]),
204
205 # Images
206 ('Using Images', [
207 'ArtProvider',
208 'Cursor',
209 'DragImage',
210 'Image',
211 'ImageAlpha',
212 'ImageFromStream',
213 'Mask',
214 'Throbber',
215 ]),
216
217 # Other stuff
218 ('Miscellaneous', [
219 'ColourDB',
220 ##'DialogUnits', # needs more explainations
221 'DrawXXXList',
222 'FileHistory',
223 'FontEnumerator',
224 'Joystick',
225 'OGL',
226 'PrintFramework',
227 'ShapedWindow',
228 'Sound',
229 'Unicode',
230 ]),
231
232 # need libs not coming with the demo
233 ('Samples using an external library', [
234 'GLCanvas',
235 ]),
236
237
238 ('Check out the samples dir too', [
239 ]),
240
241 ]
242
243
244
245 #---------------------------------------------------------------------------
246 # Show how to derive a custom wxLog class
247
248 class MyLog(wx.PyLog):
249 def __init__(self, textCtrl, logTime=0):
250 wx.PyLog.__init__(self)
251 self.tc = textCtrl
252 self.logTime = logTime
253
254 def DoLogString(self, message, timeStamp):
255 if self.logTime:
256 message = time.strftime("%X", time.localtime(timeStamp)) + \
257 ": " + message
258 if self.tc:
259 self.tc.AppendText(message + '\n')
260
261
262 class MyTP(wx.PyTipProvider):
263 def GetTip(self):
264 return "This is my tip"
265
266
267 #---------------------------------------------------------------------------
268 # A class to be used to display source code in the demo. Try using the
269 # wxSTC in the StyledTextCtrl_2 sample first, fall back to wxTextCtrl
270 # if there is an error, such as the stc module not being present.
271 #
272
273 try:
274 ##raise ImportError # for testing the alternate implementation
275 from wx import stc
276 from StyledTextCtrl_2 import PythonSTC
277
278 class DemoCodeEditor(PythonSTC):
279 def __init__(self, parent):
280 PythonSTC.__init__(self, parent, -1, style=wx.BORDER_NONE)
281 self.SetUpEditor()
282
283 # Some methods to make it compatible with how the wxTextCtrl is used
284 def SetValue(self, value):
285 if wx.USE_UNICODE:
286 value = value.decode('iso8859_1')
287 self.SetText(value)
288 self.EmptyUndoBuffer()
289 self.SetSavePoint()
290
291 def IsModified(self):
292 return self.GetModify()
293
294 def Clear(self):
295 self.ClearAll()
296
297 def SetInsertionPoint(self, pos):
298 self.SetCurrentPos(pos)
299 self.SetAnchor(pos)
300
301 def ShowPosition(self, pos):
302 line = self.LineFromPosition(pos)
303 #self.EnsureVisible(line)
304 self.GotoLine(line)
305
306 def GetLastPosition(self):
307 return self.GetLength()
308
309 def GetPositionFromLine(self, line):
310 return self.PositionFromLine(line)
311
312 def GetRange(self, start, end):
313 return self.GetTextRange(start, end)
314
315 def GetSelection(self):
316 return self.GetAnchor(), self.GetCurrentPos()
317
318 def SetSelection(self, start, end):
319 self.SetSelectionStart(start)
320 self.SetSelectionEnd(end)
321
322 def SelectLine(self, line):
323 start = self.PositionFromLine(line)
324 end = self.GetLineEndPosition(line)
325 self.SetSelection(start, end)
326
327 def SetUpEditor(self):
328 """
329 This method carries out the work of setting up the demo editor.
330 It's seperate so as not to clutter up the init code.
331 """
332 import keyword
333
334 self.SetLexer(stc.STC_LEX_PYTHON)
335 self.SetKeyWords(0, " ".join(keyword.kwlist))
336
337 # Enable folding
338 self.SetProperty("fold", "1" )
339
340 # Highlight tab/space mixing (shouldn't be any)
341 self.SetProperty("tab.timmy.whinge.level", "1")
342
343 # Set left and right margins
344 self.SetMargins(2,2)
345
346 # Set up the numbers in the margin for margin #1
347 self.SetMarginType(1, wx.stc.STC_MARGIN_NUMBER)
348 # Reasonable value for, say, 4-5 digits using a mono font (40 pix)
349 self.SetMarginWidth(1, 40)
350
351 # Indentation and tab stuff
352 self.SetIndent(4) # Proscribed indent size for wx
353 self.SetIndentationGuides(True) # Show indent guides
354 self.SetBackSpaceUnIndents(True)# Backspace unindents rather than delete 1 space
355 self.SetTabIndents(True) # Tab key indents
356 self.SetTabWidth(4) # Proscribed tab size for wx
357 self.SetUseTabs(False) # Use spaces rather than tabs, or
358 # TabTimmy will complain!
359 # White space
360 self.SetViewWhiteSpace(False) # Don't view white space
361
362 # EOL: Since we are loading/saving ourselves, and the
363 # strings will always have \n's in them, set the STC to
364 # edit them that way.
365 self.SetEOLMode(wx.stc.STC_EOL_LF)
366 self.SetViewEOL(False)
367
368 # No right-edge mode indicator
369 self.SetEdgeMode(stc.STC_EDGE_NONE)
370
371 # Setup a margin to hold fold markers
372 self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
373 self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
374 self.SetMarginSensitive(2, True)
375 self.SetMarginWidth(2, 12)
376
377 # and now set up the fold markers
378 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_BOXPLUSCONNECTED, "white", "black")
379 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "black")
380 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER, "white", "black")
381 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNER, "white", "black")
382 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "black")
383 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_BOXPLUS, "white", "black")
384 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_BOXMINUS, "white", "black")
385
386 # Global default style
387 if wx.Platform == '__WXMSW__':
388 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
389 'fore:#000000,back:#FFFFFF,face:Courier New,size:9')
390 else:
391 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
392 'fore:#000000,back:#FFFFFF,face:Courier,size:12')
393
394 # Clear styles and revert to default.
395 self.StyleClearAll()
396
397 # Following style specs only indicate differences from default.
398 # The rest remains unchanged.
399
400 # Line numbers in margin
401 self.StyleSetSpec(wx.stc.STC_STYLE_LINENUMBER,'fore:#000000,back:#99A9C2')
402
403 # Highlighted brace
404 self.StyleSetSpec(wx.stc.STC_STYLE_BRACELIGHT,'fore:#00009D,back:#FFFF00')
405 # Unmatched brace
406 self.StyleSetSpec(wx.stc.STC_STYLE_BRACEBAD,'fore:#00009D,back:#FF0000')
407 # Indentation guide
408 self.StyleSetSpec(wx.stc.STC_STYLE_INDENTGUIDE, "fore:#CDCDCD")
409
410 # Python styles
411 self.StyleSetSpec(wx.stc.STC_P_DEFAULT, 'fore:#000000')
412 # Comments
413 self.StyleSetSpec(wx.stc.STC_P_COMMENTLINE, 'fore:#008000,back:#F0FFF0')
414 self.StyleSetSpec(wx.stc.STC_P_COMMENTBLOCK, 'fore:#008000,back:#F0FFF0')
415 # Numbers
416 self.StyleSetSpec(wx.stc.STC_P_NUMBER, 'fore:#008080')
417 # Strings and characters
418 self.StyleSetSpec(wx.stc.STC_P_STRING, 'fore:#800080')
419 self.StyleSetSpec(wx.stc.STC_P_CHARACTER, 'fore:#800080')
420 # Keywords
421 self.StyleSetSpec(wx.stc.STC_P_WORD, 'fore:#000080,bold')
422 # Triple quotes
423 self.StyleSetSpec(wx.stc.STC_P_TRIPLE, 'fore:#800080,back:#FFFFEA')
424 self.StyleSetSpec(wx.stc.STC_P_TRIPLEDOUBLE, 'fore:#800080,back:#FFFFEA')
425 # Class names
426 self.StyleSetSpec(wx.stc.STC_P_CLASSNAME, 'fore:#0000FF,bold')
427 # Function names
428 self.StyleSetSpec(wx.stc.STC_P_DEFNAME, 'fore:#008080,bold')
429 # Operators
430 self.StyleSetSpec(wx.stc.STC_P_OPERATOR, 'fore:#800000,bold')
431 # Identifiers. I leave this as not bold because everything seems
432 # to be an identifier if it doesn't match the above criterae
433 self.StyleSetSpec(wx.stc.STC_P_IDENTIFIER, 'fore:#000000')
434
435 # Caret color
436 self.SetCaretForeground("BLUE")
437 # Selection background
438 self.SetSelBackground(1, '#66CCFF')
439
440 self.SetSelBackground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT))
441 self.SetSelForeground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT))
442
443 def RegisterModifiedEvent(self, eventHandler):
444 self.Bind(wx.stc.EVT_STC_CHANGE, eventHandler)
445
446
447 except ImportError:
448 class DemoCodeEditor(wx.TextCtrl):
449 def __init__(self, parent):
450 wx.TextCtrl.__init__(self, parent, -1, style =
451 wx.TE_MULTILINE | wx.HSCROLL | wx.TE_RICH2 | wx.TE_NOHIDESEL)
452
453 def RegisterModifiedEvent(self, eventHandler):
454 self.Bind(wx.EVT_TEXT, eventHandler)
455
456 def SetReadOnly(self, flag):
457 self.SetEditable(not flag)
458 # NOTE: STC already has this method
459
460 def GetText(self):
461 return self.GetValue()
462
463 def GetPositionFromLine(line):
464 return self.XYToPosition(0,line)
465
466 def GotoLine(self, line):
467 pos = self.editor.GetPositionFromLine(line)
468 self.editor.SetInsertionPoint(pos)
469 self.editor.ShowPosition(pos)
470
471 def SelectLine(self, line):
472 start = self.GetPositionFromLine(line)
473 end = start + self.GetLineLength(line)
474 self.SetSelection(start, end)
475
476
477 #---------------------------------------------------------------------------
478 # Constants for module versions
479
480 modOriginal = 0
481 modModified = 1
482 modDefault = modOriginal
483
484 #---------------------------------------------------------------------------
485
486 class DemoCodePanel(wx.Panel):
487 """Panel for the 'Demo Code' tab"""
488 def __init__(self, parent, mainFrame):
489 wx.Panel.__init__(self, parent, size=(1,1))
490 if 'wxMSW' in wx.PlatformInfo:
491 self.Hide()
492 self.mainFrame = mainFrame
493 self.editor = DemoCodeEditor(self)
494 self.editor.RegisterModifiedEvent(self.OnCodeModified)
495
496 self.btnSave = wx.Button(self, -1, "Save Changes")
497 self.btnRestore = wx.Button(self, -1, "Delete Modified")
498 self.btnSave.Enable(False)
499 self.btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
500 self.btnRestore.Bind(wx.EVT_BUTTON, self.OnRestore)
501
502 self.radioButtons = { modOriginal: wx.RadioButton(self, -1, "Original", style = wx.RB_GROUP),
503 modModified: wx.RadioButton(self, -1, "Modified") }
504
505 self.controlBox = wx.BoxSizer(wx.HORIZONTAL)
506 self.controlBox.Add(wx.StaticText(self, -1, "Active Version:"), 0,
507 wx.RIGHT | wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5)
508 for modID, radioButton in self.radioButtons.items():
509 self.controlBox.Add(radioButton, 0, wx.EXPAND | wx.RIGHT, 5)
510 radioButton.modID = modID # makes it easier for the event handler
511 radioButton.Bind(wx.EVT_RADIOBUTTON, self.OnRadioButton)
512
513 self.controlBox.Add(self.btnSave, 0, wx.RIGHT, 5)
514 self.controlBox.Add(self.btnRestore, 0)
515
516 self.box = wx.BoxSizer(wx.VERTICAL)
517 self.box.Add(self.controlBox, 0, wx.EXPAND)
518 self.box.Add(wx.StaticLine(self), 0, wx.EXPAND)
519 self.box.Add(self.editor, 1, wx.EXPAND)
520
521 self.box.Fit(self)
522 self.SetSizer(self.box)
523
524
525 # Loads a demo from a DemoModules object
526 def LoadDemo(self, demoModules):
527 self.demoModules = demoModules
528 if (modDefault == modModified) and demoModules.Exists(modModified):
529 demoModules.SetActive(modModified)
530 else:
531 demoModules.SetActive(modOriginal)
532 self.radioButtons[demoModules.GetActiveID()].Enable(True)
533 self.ActiveModuleChanged()
534
535
536 def ActiveModuleChanged(self):
537 self.LoadDemoSource(self.demoModules.GetSource())
538 self.UpdateControlState()
539 self.ReloadDemo()
540
541
542 def LoadDemoSource(self, source):
543 self.editor.Clear()
544 self.editor.SetValue(source)
545 self.JumpToLine(0)
546 self.btnSave.Enable(False)
547
548
549 def JumpToLine(self, line, highlight=False):
550 self.editor.GotoLine(line)
551 self.editor.SetFocus()
552 if highlight:
553 self.editor.SelectLine(line)
554
555
556 def UpdateControlState(self):
557 active = self.demoModules.GetActiveID()
558 # Update the radio/restore buttons
559 for moduleID in self.radioButtons:
560 btn = self.radioButtons[moduleID]
561 if moduleID == active:
562 btn.SetValue(True)
563 else:
564 btn.SetValue(False)
565
566 if self.demoModules.Exists(moduleID):
567 btn.Enable(True)
568 if moduleID == modModified:
569 self.btnRestore.Enable(True)
570 else:
571 btn.Enable(False)
572 if moduleID == modModified:
573 self.btnRestore.Enable(False)
574
575
576 def OnRadioButton(self, event):
577 radioSelected = event.GetEventObject()
578 modSelected = radioSelected.modID
579 if modSelected != self.demoModules.GetActiveID():
580 busy = wx.BusyInfo("Reloading demo module...")
581 self.demoModules.SetActive(modSelected)
582 self.ActiveModuleChanged()
583
584
585 def ReloadDemo(self):
586 if self.demoModules.name != __name__:
587 self.mainFrame.RunModule()
588
589
590 def OnCodeModified(self, event):
591 self.btnSave.Enable(self.editor.IsModified())
592
593
594 def OnSave(self, event):
595 if self.demoModules.Exists(modModified):
596 if self.demoModules.GetActiveID() == modOriginal:
597 overwriteMsg = "You are about to overwrite an already existing modified copy\n" + \
598 "Do you want to continue?"
599 dlg = wx.MessageDialog(self, overwriteMsg, "wxPython Demo",
600 wx.YES_NO | wx.NO_DEFAULT| wx.ICON_EXCLAMATION)
601 result = dlg.ShowModal()
602 if result == wx.ID_NO:
603 return
604 dlg.Destroy()
605
606 self.demoModules.SetActive(modModified)
607 modifiedFilename = GetModifiedFilename(self.demoModules.name)
608
609 # Create the demo directory if one doesn't already exist
610 if not os.path.exists(GetModifiedDirectory()):
611 try:
612 os.makedirs(GetModifiedDirectory())
613 if not os.path.exists(GetModifiedDirectory()):
614 wx.LogMessage("BUG: Created demo directory but it still doesn't exit")
615 raise AssetionError
616 except:
617 wx.LogMessage("Error creating demo directory: %s" % GetModifiedDirectory())
618 return
619 else:
620 wx.LogMessage("Created directory for modified demos: %s" % GetModifiedDirectory())
621
622 # Save
623 f = open(modifiedFilename, "wt")
624 source = self.editor.GetText()
625 try:
626 f.write(source)
627 finally:
628 f.close()
629
630 busy = wx.BusyInfo("Reloading demo module...")
631 self.demoModules.LoadFromFile(modModified, modifiedFilename)
632 self.ActiveModuleChanged()
633
634
635 def OnRestore(self, event): # Handles the "Delete Modified" button
636 modifiedFilename = GetModifiedFilename(self.demoModules.name)
637 self.demoModules.Delete(modModified)
638 os.unlink(modifiedFilename) # Delete the modified copy
639 busy = wx.BusyInfo("Reloading demo module...")
640 self.ActiveModuleChanged()
641
642
643 #---------------------------------------------------------------------------
644
645 def opj(path):
646 """Convert paths to the platform-specific separator"""
647 str = apply(os.path.join, tuple(path.split('/')))
648 # HACK: on Linux, a leading / gets lost...
649 if path.startswith('/'):
650 str = '/' + str
651 return str
652
653
654 def GetModifiedDirectory():
655 """
656 Returns the directory where modified versions of the demo files
657 are stored
658 """
659 return opj(wx.GetHomeDir() + "/.wxPyDemo/modified/")
660
661
662 def GetModifiedFilename(name):
663 """
664 Returns the filename of the modified version of the specified demo
665 """
666 if not name.endswith(".py"):
667 name = name + ".py"
668 return GetModifiedDirectory() + name
669
670
671 def GetOriginalFilename(name):
672 """
673 Returns the filename of the original version of the specified demo
674 """
675 if not name.endswith(".py"):
676 name = name + ".py"
677 return name
678
679
680 def DoesModifiedExist(name):
681 """Returns whether the specified demo has a modified copy"""
682 if os.path.exists(GetModifiedFilename(name)):
683 return True
684 else:
685 return False
686
687
688 #---------------------------------------------------------------------------
689
690 class ModuleDictWrapper:
691 """Emulates a module with a dynamically compiled __dict__"""
692 def __init__(self, dict):
693 self.dict = dict
694
695 def __getattr__(self, name):
696 if name in self.dict:
697 return self.dict[name]
698 else:
699 raise AttributeError
700
701 class DemoModules:
702 """
703 Dynamically manages the original/modified versions of a demo
704 module
705 """
706 def __init__(self, name):
707 self.modActive = -1
708 self.name = name
709
710 # (dict , source , filename , description , error information )
711 # ( 0 , 1 , 2 , 3 , 4 )
712 self.modules = [[None, "" , "" , "<original>" , None],
713 [None, "" , "" , "<modified>" , None]]
714
715 # load original module
716 self.LoadFromFile(modOriginal, GetOriginalFilename(name))
717 self.SetActive(modOriginal)
718
719 # load modified module (if one exists)
720 if DoesModifiedExist(name):
721 self.LoadFromFile(modModified, GetModifiedFilename(name))
722
723
724 def LoadFromFile(self, modID, filename):
725 self.modules[modID][2] = filename
726 file = open(filename, "rt")
727 self.LoadFromSource(modID, file.read())
728 file.close()
729
730
731 def LoadFromSource(self, modID, source):
732 self.modules[modID][1] = source
733 self.LoadDict(modID)
734
735
736 def LoadDict(self, modID):
737 if self.name != __name__:
738 source = self.modules[modID][1]
739 description = self.modules[modID][3]
740
741 try:
742 self.modules[modID][0] = {}
743 code = compile(source, description, "exec")
744 exec code in self.modules[modID][0]
745 except:
746 self.modules[modID][4] = DemoError(sys.exc_info())
747 self.modules[modID][0] = None
748 else:
749 self.modules[modID][4] = None
750
751
752 def SetActive(self, modID):
753 if modID != modOriginal and modID != modModified:
754 raise LookupError
755 else:
756 self.modActive = modID
757
758
759 def GetActive(self):
760 dict = self.modules[self.modActive][0]
761 if dict is None:
762 return None
763 else:
764 return ModuleDictWrapper(dict)
765
766
767 def GetActiveID(self):
768 return self.modActive
769
770
771 def GetSource(self, modID = None):
772 if modID is None:
773 modID = self.modActive
774 return self.modules[modID][1]
775
776
777 def GetFilename(self, modID = None):
778 if modID is None:
779 modID = self.modActive
780 return self.modules[self.modActive][2]
781
782
783 def GetErrorInfo(self, modID = None):
784 if modID is None:
785 modID = self.modActive
786 return self.modules[self.modActive][4]
787
788
789 def Exists(self, modID):
790 return self.modules[modID][1] != ""
791
792
793 def UpdateFile(self, modID = None):
794 """Updates the file from which a module was loaded
795 with (possibly updated) source"""
796 if modID is None:
797 modID = self.modActive
798
799 source = self.modules[modID][1]
800 filename = self.modules[modID][2]
801
802 try:
803 file = open(filename, "wt")
804 file.write(source)
805 finally:
806 file.close()
807
808
809 def Delete(self, modID):
810 if self.modActive == modID:
811 self.SetActive(0)
812
813 self.modules[modID][0] = None
814 self.modules[modID][1] = ""
815 self.modules[modID][2] = ""
816
817
818 #---------------------------------------------------------------------------
819
820 class DemoError:
821 """Wraps and stores information about the current exception"""
822 def __init__(self, exc_info):
823 import copy
824
825 excType, excValue = exc_info[:2]
826 # traceback list entries: (filename, line number, function name, text)
827 self.traceback = traceback.extract_tb(exc_info[2])
828
829 # --Based on traceback.py::format_exception_only()--
830 if type(excType) == types.ClassType:
831 self.exception_type = excType.__name__
832 else:
833 self.exception_type = excType
834
835 # If it's a syntax error, extra information needs
836 # to be added to the traceback
837 if excType is SyntaxError:
838 try:
839 msg, (filename, lineno, self.offset, line) = excValue
840 except:
841 pass
842 else:
843 if not filename:
844 filename = "<string>"
845 line = line.strip()
846 self.traceback.append( (filename, lineno, "", line) )
847 excValue = msg
848 try:
849 self.exception_details = str(excValue)
850 except:
851 self.exception_details = "<unprintable %s object>" & type(excValue).__name__
852
853 del exc_info
854
855 def __str__(self):
856 ret = "Type %s \n \
857 Traceback: %s \n \
858 Details : %s" % ( str(self.exception_type), str(self.traceback), self.exception_details )
859 return ret
860
861 #---------------------------------------------------------------------------
862
863 class DemoErrorPanel(wx.Panel):
864 """Panel put into the demo tab when the demo fails to run due to errors"""
865
866 def __init__(self, parent, codePanel, demoError, log):
867 wx.Panel.__init__(self, parent, -1)#, style=wx.NO_FULL_REPAINT_ON_RESIZE)
868 self.codePanel = codePanel
869 self.nb = parent
870 self.log = log
871
872 self.box = wx.BoxSizer(wx.VERTICAL)
873
874 # Main Label
875 self.box.Add(wx.StaticText(self, -1, "An error has occured while trying to run the demo")
876 , 0, wx.ALIGN_CENTER | wx.TOP, 10)
877
878 # Exception Information
879 boxInfo = wx.StaticBox(self, -1, "Exception Info" )
880 boxInfoSizer = wx.StaticBoxSizer(boxInfo, wx.VERTICAL ) # Used to center the grid within the box
881 boxInfoGrid = wx.FlexGridSizer(0, 2, 0, 0)
882 textFlags = wx.ALIGN_RIGHT | wx.LEFT | wx.RIGHT | wx.TOP
883 boxInfoGrid.Add(wx.StaticText(self, -1, "Type: "), 0, textFlags, 5 )
884 boxInfoGrid.Add(wx.StaticText(self, -1, demoError.exception_type) , 0, textFlags, 5 )
885 boxInfoGrid.Add(wx.StaticText(self, -1, "Details: ") , 0, textFlags, 5 )
886 boxInfoGrid.Add(wx.StaticText(self, -1, demoError.exception_details) , 0, textFlags, 5 )
887 boxInfoSizer.Add(boxInfoGrid, 0, wx.ALIGN_CENTRE | wx.ALL, 5 )
888 self.box.Add(boxInfoSizer, 0, wx.ALIGN_CENTER | wx.ALL, 5)
889
890 # Set up the traceback list
891 # This one automatically resizes last column to take up remaining space
892 from ListCtrl import TestListCtrl
893 self.list = TestListCtrl(self, -1, style=wx.LC_REPORT | wx.SUNKEN_BORDER)
894 self.list.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
895 self.list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
896 self.list.InsertColumn(0, "Filename")
897 self.list.InsertColumn(1, "Line", wx.LIST_FORMAT_RIGHT)
898 self.list.InsertColumn(2, "Function")
899 self.list.InsertColumn(3, "Code")
900 self.InsertTraceback(self.list, demoError.traceback)
901 self.list.SetColumnWidth(0, wx.LIST_AUTOSIZE)
902 self.list.SetColumnWidth(2, wx.LIST_AUTOSIZE)
903 self.box.Add(wx.StaticText(self, -1, "Traceback:")
904 , 0, wx.ALIGN_CENTER | wx.TOP, 5)
905 self.box.Add(self.list, 1, wx.GROW | wx.ALIGN_CENTER | wx.ALL, 5)
906 self.box.Add(wx.StaticText(self, -1, "Entries from the demo module are shown in blue\n"
907 + "Double-click on them to go to the offending line")
908 , 0, wx.ALIGN_CENTER | wx.BOTTOM, 5)
909
910 self.box.Fit(self)
911 self.SetSizer(self.box)
912
913
914 def InsertTraceback(self, list, traceback):
915 #Add the traceback data
916 for x in range(len(traceback)):
917 data = traceback[x]
918 list.InsertStringItem(x, os.path.basename(data[0])) # Filename
919 list.SetStringItem(x, 1, str(data[1])) # Line
920 list.SetStringItem(x, 2, str(data[2])) # Function
921 list.SetStringItem(x, 3, str(data[3])) # Code
922
923 # Check whether this entry is from the demo module
924 if data[0] == "<original>" or data[0] == "<modified>": # FIXME: make more generalised
925 self.list.SetItemData(x, int(data[1])) # Store line number for easy access
926 # Give it a blue colour
927 item = self.list.GetItem(x)
928 item.SetTextColour(wx.BLUE)
929 self.list.SetItem(item)
930 else:
931 self.list.SetItemData(x, -1) # Editor can't jump into this one's code
932
933
934 def OnItemSelected(self, event):
935 # This occurs before OnDoubleClick and can be used to set the
936 # currentItem. OnDoubleClick doesn't get a wxListEvent....
937 self.currentItem = event.m_itemIndex
938 event.Skip()
939
940
941 def OnDoubleClick(self, event):
942 # If double-clicking on a demo's entry, jump to the line number
943 line = self.list.GetItemData(self.currentItem)
944 if line != -1:
945 self.nb.SetSelection(1) # Switch to the code viewer tab
946 wx.CallAfter(self.codePanel.JumpToLine, line-1, True)
947 event.Skip()
948
949
950 #---------------------------------------------------------------------------
951
952 class DemoTaskBarIcon(wx.TaskBarIcon):
953 TBMENU_RESTORE = wx.NewId()
954 TBMENU_CLOSE = wx.NewId()
955 TBMENU_CHANGE = wx.NewId()
956 TBMENU_REMOVE = wx.NewId()
957
958 def __init__(self, frame):
959 wx.TaskBarIcon.__init__(self)
960 self.frame = frame
961
962 # Set the image
963 icon = self.MakeIcon(images.getMondrianImage())
964 self.SetIcon(icon, "wxPython Demo")
965
966 # bind some events
967 self.Bind(wx.EVT_TASKBAR_LEFT_DCLICK, self.OnTaskBarActivate)
968 self.Bind(wx.EVT_MENU, self.OnTaskBarActivate, id=self.TBMENU_RESTORE)
969 self.Bind(wx.EVT_MENU, self.OnTaskBarClose, id=self.TBMENU_CLOSE)
970 self.Bind(wx.EVT_MENU, self.OnTaskBarChange, id=self.TBMENU_CHANGE)
971 self.Bind(wx.EVT_MENU, self.OnTaskBarRemove, id=self.TBMENU_REMOVE)
972
973
974 def CreatePopupMenu(self):
975 """
976 This method is called by the base class when it needs to popup
977 the menu for the default EVT_RIGHT_DOWN event. Just create
978 the menu how you want it and return it from this function,
979 the base class takes care of the rest.
980 """
981 menu = wx.Menu()
982 menu.Append(self.TBMENU_RESTORE, "Restore wxPython Demo")
983 menu.Append(self.TBMENU_CLOSE, "Close wxPython Demo")
984 menu.AppendSeparator()
985 menu.Append(self.TBMENU_CHANGE, "Change the TB Icon")
986 menu.Append(self.TBMENU_REMOVE, "Remove the TB Icon")
987 return menu
988
989
990 def MakeIcon(self, img):
991 """
992 The various platforms have different requirements for the
993 icon size...
994 """
995 if "wxMSW" in wx.PlatformInfo:
996 img.Scale(16, 16)
997 elif "wxGTK" in wx.PlatformInfo:
998 img.Scale(20, 20)
999 # wxMac can be any size upto 128.128....
1000 icon = wx.IconFromBitmap(img.ConvertToBitmap() )
1001 return icon
1002
1003
1004 def OnTaskBarActivate(self, evt):
1005 if self.frame.IsIconized():
1006 self.frame.Iconize(False)
1007 if not self.frame.IsShown():
1008 self.frame.Show(True)
1009 self.frame.Raise()
1010
1011
1012 def OnTaskBarClose(self, evt):
1013 self.frame.Close()
1014
1015
1016 def OnTaskBarChange(self, evt):
1017 icon = self.MakeIcon(images.getBlom10MaskedImage())
1018 self.SetIcon(icon, "This is a new icon")
1019
1020
1021 def OnTaskBarRemove(self, evt):
1022 self.RemoveIcon()
1023
1024
1025 #---------------------------------------------------------------------------
1026 class wxPythonDemo(wx.Frame):
1027 overviewText = "wxPython Overview"
1028
1029 def __init__(self, parent, title):
1030 wx.Frame.__init__(self, parent, -1, title, size = (950, 720),
1031 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
1032
1033 self.loaded = False
1034 self.cwd = os.getcwd()
1035 self.curOverview = ""
1036 self.demoPage = None
1037 self.codePage = None
1038 self.shell = None
1039 self.firstTime = True
1040
1041 icon = images.getMondrianIcon()
1042 self.SetIcon(icon)
1043
1044 self.tbicon = DemoTaskBarIcon(self)
1045
1046 wx.CallAfter(self.ShowTip)
1047
1048 self.otherWin = None
1049 self.Bind(wx.EVT_IDLE, self.OnIdle)
1050 self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
1051 self.Bind(wx.EVT_ICONIZE, self.OnIconfiy)
1052 self.Bind(wx.EVT_MAXIMIZE, self.OnMaximize)
1053 self.Bind(wx.EVT_ACTIVATE, self.OnActivate)
1054 wx.GetApp().Bind(wx.EVT_ACTIVATE_APP, self.OnAppActivate)
1055
1056 self.Centre(wx.BOTH)
1057 self.CreateStatusBar(1, wx.ST_SIZEGRIP)
1058
1059 splitter = wx.SplitterWindow(self, -1, style=wx.CLIP_CHILDREN | wx.SP_LIVE_UPDATE | wx.SP_3D)
1060 splitter2 = wx.SplitterWindow(splitter, -1, style=wx.CLIP_CHILDREN | wx.SP_LIVE_UPDATE | wx.SP_3D)
1061
1062 def EmptyHandler(evt): pass
1063 #splitter.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
1064 #splitter2.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
1065
1066 # Prevent TreeCtrl from displaying all items after destruction when True
1067 self.dying = False
1068
1069 # Create a Notebook
1070 self.nb = wx.Notebook(splitter2, -1, style=wx.CLIP_CHILDREN)
1071
1072 # Make a File menu
1073 self.mainmenu = wx.MenuBar()
1074 menu = wx.Menu()
1075 item = menu.Append(-1, '&Redirect Output',
1076 'Redirect print statements to a window',
1077 wx.ITEM_CHECK)
1078 self.Bind(wx.EVT_MENU, self.OnToggleRedirect, item)
1079
1080 item = menu.Append(-1, 'E&xit\tAlt-X', 'Get the heck outta here!')
1081 self.Bind(wx.EVT_MENU, self.OnFileExit, item)
1082 wx.App_SetMacExitMenuItemId(item.GetId())
1083 self.mainmenu.Append(menu, '&File')
1084
1085 # Make a Demo menu
1086 menu = wx.Menu()
1087 for item in _treeList:
1088 submenu = wx.Menu()
1089 for childItem in item[1]:
1090 mi = submenu.Append(-1, childItem)
1091 self.Bind(wx.EVT_MENU, self.OnDemoMenu, mi)
1092 menu.AppendMenu(wx.NewId(), item[0], submenu)
1093 self.mainmenu.Append(menu, '&Demo')
1094
1095 # Make a Demo Code menu
1096 #TODO: Add new menu items
1097 # Like the option-enabled entries to select the
1098 # active module
1099 #TODO: should we bother?
1100
1101 #menu = wx.Menu()
1102 #saveID = wx.NewId()
1103 #restoreID = wx.NewId()
1104 #
1105 #menu.Append(saveID, '&Save\tCtrl-S', 'Save edited demo')
1106 #menu.Append(restoreID, '&Delete Modified\tCtrl-R', 'Delete modified copy')
1107 #self.Bind(wx.EVT_MENU, self.codePage.OnSave, id=saveID)
1108 #self.Bind(wx.EVT_MENU, self.codePage.OnRestore, id=restoreID)
1109 #self.mainmenu.Append(menu, 'Demo &Code')
1110 #
1111
1112 # Make a Help menu
1113 menu = wx.Menu()
1114 findItem = menu.Append(-1, '&Find\tCtrl-F', 'Find in the Demo Code')
1115 findnextItem = menu.Append(-1, 'Find &Next\tF3', 'Find Next')
1116 menu.AppendSeparator()
1117
1118 shellItem = menu.Append(-1, 'Open Py&Shell Window\tF5',
1119 'An interactive interpreter window with the demo app and frame objects in the namesapce')
1120 menu.AppendSeparator()
1121 helpItem = menu.Append(-1, '&About\tCtrl-H', 'wxPython RULES!!!')
1122 wx.App_SetMacAboutMenuItemId(helpItem.GetId())
1123
1124 self.Bind(wx.EVT_MENU, self.OnOpenShellWindow, shellItem)
1125 self.Bind(wx.EVT_MENU, self.OnHelpAbout, helpItem)
1126 self.Bind(wx.EVT_MENU, self.OnHelpFind, findItem)
1127 self.Bind(wx.EVT_MENU, self.OnFindNext, findnextItem)
1128 self.Bind(wx.EVT_COMMAND_FIND, self.OnFind)
1129 self.Bind(wx.EVT_COMMAND_FIND_NEXT, self.OnFind)
1130 self.Bind(wx.EVT_COMMAND_FIND_CLOSE, self.OnFindClose)
1131 self.mainmenu.Append(menu, '&Help')
1132 self.SetMenuBar(self.mainmenu)
1133
1134 self.finddata = wx.FindReplaceData()
1135
1136 if 0:
1137 # This is another way to set Accelerators, in addition to
1138 # using the '\t<key>' syntax in the menu items.
1139 aTable = wx.AcceleratorTable([(wx.ACCEL_ALT, ord('X'), exitID),
1140 (wx.ACCEL_CTRL, ord('H'), helpID),
1141 (wx.ACCEL_CTRL, ord('F'), findID),
1142 (wx.ACCEL_NORMAL, WXK_F3, findnextID)
1143 ])
1144 self.SetAcceleratorTable(aTable)
1145
1146
1147 # Create a TreeCtrl
1148 tID = wx.NewId()
1149 self.treeMap = {}
1150 self.tree = wx.TreeCtrl(splitter, tID, style =
1151 wx.TR_DEFAULT_STYLE #| wx.TR_HAS_VARIABLE_ROW_HEIGHT
1152 )
1153
1154 root = self.tree.AddRoot("wxPython Overview")
1155 firstChild = None
1156 for item in _treeList:
1157 child = self.tree.AppendItem(root, item[0])
1158 if not firstChild: firstChild = child
1159 for childItem in item[1]:
1160 theDemo = self.tree.AppendItem(child, childItem)
1161 self.treeMap[childItem] = theDemo
1162
1163 self.tree.Expand(root)
1164 self.tree.Expand(firstChild)
1165 self.tree.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded, id=tID)
1166 self.tree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed, id=tID)
1167 self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=tID)
1168 self.tree.Bind(wx.EVT_LEFT_DOWN, self.OnTreeLeftDown)
1169
1170 # Set up a wx.html.HtmlWindow on the Overview Notebook page
1171 # we put it in a panel first because there seems to be a
1172 # refresh bug of some sort (wxGTK) when it is directly in
1173 # the notebook...
1174 if 0: # the old way
1175 self.ovr = wx.html.HtmlWindow(self.nb, -1, size=(400, 400))
1176 self.nb.AddPage(self.ovr, self.overviewText)
1177
1178 else: # hopefully I can remove this hacky code soon, see SF bug #216861
1179 panel = wx.Panel(self.nb, -1, style=wx.CLIP_CHILDREN)
1180 self.ovr = wx.html.HtmlWindow(panel, -1, size=(400, 400))
1181 self.nb.AddPage(panel, self.overviewText)
1182
1183 def OnOvrSize(evt, ovr=self.ovr):
1184 ovr.SetSize(evt.GetSize())
1185 panel.Bind(wx.EVT_SIZE, OnOvrSize)
1186 panel.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
1187
1188 if "gtk2" in wx.PlatformInfo:
1189 self.ovr.SetStandardFonts()
1190 self.SetOverview(self.overviewText, mainOverview)
1191
1192
1193 # Set up a log window
1194 self.log = wx.TextCtrl(splitter2, -1,
1195 style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
1196
1197 # Set the wxWindows log target to be this textctrl
1198 #wx.Log_SetActiveTarget(wx.LogTextCtrl(self.log))
1199
1200 # But instead of the above we want to show how to use our own wx.Log class
1201 wx.Log_SetActiveTarget(MyLog(self.log))
1202
1203 # for serious debugging
1204 #wx.Log_SetActiveTarget(wx.LogStderr())
1205 #wx.Log_SetTraceMask(wx.TraceMessages)
1206
1207
1208 # add the windows to the splitter and split it.
1209 splitter2.SplitHorizontally(self.nb, self.log, -160)
1210 splitter.SplitVertically(self.tree, splitter2, 200)
1211
1212 splitter.SetMinimumPaneSize(20)
1213 splitter2.SetMinimumPaneSize(20)
1214
1215 # Make the splitter on the right expand the top window when resized
1216 def SplitterOnSize(evt):
1217 splitter = evt.GetEventObject()
1218 sz = splitter.GetSize()
1219 splitter.SetSashPosition(sz.height - 160, False)
1220 evt.Skip()
1221
1222 splitter2.Bind(wx.EVT_SIZE, SplitterOnSize)
1223
1224 # select initial items
1225 self.nb.SetSelection(0)
1226 self.tree.SelectItem(root)
1227
1228 # Load 'Main' module
1229 self.LoadDemo(self.overviewText)
1230 self.loaded = True
1231
1232 # select some other initial module?
1233 if len(sys.argv) > 1:
1234 arg = sys.argv[1]
1235 if arg.endswith('.py'):
1236 arg = arg[:-3]
1237 selectedDemo = self.treeMap.get(arg, None)
1238 if selectedDemo:
1239 self.tree.SelectItem(selectedDemo)
1240 self.tree.EnsureVisible(selectedDemo)
1241
1242
1243 #---------------------------------------------
1244 def WriteText(self, text):
1245 if text[-1:] == '\n':
1246 text = text[:-1]
1247 wx.LogMessage(text)
1248
1249 def write(self, txt):
1250 self.WriteText(txt)
1251
1252 #---------------------------------------------
1253 def OnItemExpanded(self, event):
1254 item = event.GetItem()
1255 wx.LogMessage("OnItemExpanded: %s" % self.tree.GetItemText(item))
1256 event.Skip()
1257
1258 #---------------------------------------------
1259 def OnItemCollapsed(self, event):
1260 item = event.GetItem()
1261 wx.LogMessage("OnItemCollapsed: %s" % self.tree.GetItemText(item))
1262 event.Skip()
1263
1264 #---------------------------------------------
1265 def OnTreeLeftDown(self, event):
1266 # reset the overview text if the tree item is clicked on again
1267 pt = event.GetPosition();
1268 item, flags = self.tree.HitTest(pt)
1269 if item == self.tree.GetSelection():
1270 self.SetOverview(self.tree.GetItemText(item)+" Overview", self.curOverview)
1271 event.Skip()
1272
1273 #---------------------------------------------
1274 def OnSelChanged(self, event):
1275 if self.dying or not self.loaded:
1276 return
1277
1278 item = event.GetItem()
1279 itemText = self.tree.GetItemText(item)
1280 self.LoadDemo(itemText)
1281
1282 #---------------------------------------------
1283 def LoadDemo(self, demoName):
1284 try:
1285 wx.BeginBusyCursor()
1286
1287 os.chdir(self.cwd)
1288 self.ShutdownDemoModule()
1289
1290 if demoName == self.overviewText:
1291 # User selected the "wxPython Overview" node
1292 # ie: _this_ module
1293 # Changing the main window at runtime not yet supported...
1294 self.demoModules = DemoModules(__name__)
1295 self.SetOverview(self.overviewText, mainOverview)
1296 self.LoadDemoSource()
1297 self.UpdateNotebook(0)
1298 else:
1299 if os.path.exists(GetOriginalFilename(demoName)):
1300 wx.LogMessage("Loading demo %s.py..." % demoName)
1301 self.demoModules = DemoModules(demoName)
1302 self.LoadDemoSource()
1303 self.tree.Refresh()
1304 else:
1305 self.SetOverview("wxPython", mainOverview)
1306 self.codePage = None
1307 self.UpdateNotebook(0)
1308 finally:
1309 wx.EndBusyCursor()
1310
1311 #---------------------------------------------
1312 def LoadDemoSource(self):
1313 self.codePage = None
1314 self.codePage = DemoCodePanel(self.nb, self)
1315 self.codePage.LoadDemo(self.demoModules)
1316
1317 #---------------------------------------------
1318 def RunModule(self):
1319 """Runs the active module"""
1320
1321 module = self.demoModules.GetActive()
1322 self.ShutdownDemoModule()
1323 overviewText = ""
1324
1325 # o The RunTest() for all samples must now return a window that can
1326 # be palced in a tab in the main notebook.
1327 # o If an error occurs (or has occured before) an error tab is created.
1328
1329 if module is not None:
1330 wx.LogMessage("Running demo module...")
1331 if hasattr(module, "overview"):
1332 overviewText = module.overview
1333
1334 try:
1335 self.demoPage = module.runTest(self, self.nb, self)
1336 except:
1337 self.demoPage = DemoErrorPanel(self.nb, self.codePage,
1338 DemoError(sys.exc_info()), self)
1339
1340 assert self.demoPage is not None, "runTest must return a window!"
1341
1342 else:
1343 # There was a previous error in compiling or exec-ing
1344 self.demoPage = DemoErrorPanel(self.nb, self.codePage,
1345 self.demoModules.GetErrorInfo(), self)
1346
1347 self.SetOverview(self.demoModules.name + " Overview", overviewText)
1348
1349 if self.firstTime:
1350 # cahnge to the demo page the first time a module is run
1351 self.UpdateNotebook(2)
1352 self.firstTime = False
1353 else:
1354 # otherwise just stay on the same tab in case the user has changed to another one
1355 self.UpdateNotebook()
1356
1357 #---------------------------------------------
1358 def ShutdownDemoModule(self):
1359 if self.demoPage:
1360 # inform the window that it's time to quit if it cares
1361 if hasattr(self.demoPage, "ShutdownDemo"):
1362 self.demoPage.ShutdownDemo()
1363 wx.YieldIfNeeded() # in case the page has pending events
1364 self.demoPage = None
1365
1366 #---------------------------------------------
1367 def UpdateNotebook(self, select = -1):
1368 nb = self.nb
1369 debug = False
1370
1371 def UpdatePage(page, pageText):
1372 pageExists = False
1373 pagePos = -1
1374 for i in range(nb.GetPageCount()):
1375 if nb.GetPageText(i) == pageText:
1376 pageExists = True
1377 pagePos = i
1378 break
1379
1380 if page:
1381 if not pageExists:
1382 # Add a new page
1383 nb.AddPage(page, pageText)
1384 if debug: wx.LogMessage("DBG: ADDED %s" % pageText)
1385 else:
1386 if nb.GetPage(pagePos) != page:
1387 # Reload an existing page
1388 nb.Freeze()
1389 nb.DeletePage(pagePos)
1390 nb.InsertPage(pagePos, page, pageText)
1391 nb.Thaw()
1392 if debug: wx.LogMessage("DBG: RELOADED %s" % pageText)
1393 else:
1394 # Excellent! No redraw/flicker
1395 if debug: wx.LogMessage("DBG: SAVED from reloading %s" % pageText)
1396 elif pageExists:
1397 # Delete a page
1398 nb.DeletePage(pagePos)
1399 if debug: wx.LogMessage("DBG: DELETED %s" % pageText)
1400 else:
1401 if debug: wx.LogMessage("DBG: STILL GONE - %s" % pageText)
1402
1403 if select == -1:
1404 select = nb.GetSelection()
1405
1406 UpdatePage(self.codePage, "Demo Code")
1407 UpdatePage(self.demoPage, "Demo")
1408
1409 if select >= 0 and select < nb.GetPageCount():
1410 nb.SetSelection(select)
1411
1412 #---------------------------------------------
1413 def SetOverview(self, name, text):
1414 self.curOverview = text
1415 lead = text[:6]
1416 if lead != '<html>' and lead != '<HTML>':
1417 text = '<br>'.join(text.split('\n'))
1418 if wx.USE_UNICODE:
1419 text = text.decode('iso8859_1')
1420 self.ovr.SetPage(text)
1421 self.nb.SetPageText(0, name)
1422
1423 #---------------------------------------------
1424 # Menu methods
1425 def OnFileExit(self, *event):
1426 self.Close()
1427
1428 def OnToggleRedirect(self, event):
1429 app = wx.GetApp()
1430 if event.Checked():
1431 app.RedirectStdio()
1432 print "Print statements and other standard output will now be directed to this window."
1433 else:
1434 app.RestoreStdio()
1435 print "Print statements and other standard output will now be sent to the usual location."
1436
1437 def OnHelpAbout(self, event):
1438 from About import MyAboutBox
1439 about = MyAboutBox(self)
1440 about.ShowModal()
1441 about.Destroy()
1442
1443 def OnHelpFind(self, event):
1444 self.nb.SetSelection(1)
1445 self.finddlg = wx.FindReplaceDialog(self, self.finddata, "Find",
1446 wx.FR_NOUPDOWN |
1447 wx.FR_NOMATCHCASE |
1448 wx.FR_NOWHOLEWORD)
1449 self.finddlg.Show(True)
1450
1451 def OnFind(self, event):
1452 editor = self.codePage.editor
1453 self.nb.SetSelection(1)
1454 end = editor.GetLastPosition()
1455 textstring = editor.GetRange(0, end).lower()
1456 start = editor.GetSelection()[1]
1457 findstring = self.finddata.GetFindString().lower()
1458 loc = textstring.find(findstring, start)
1459 if loc == -1 and start != 0:
1460 # string not found, start at beginning
1461 start = 0
1462 loc = textstring.find(findstring, start)
1463 if loc == -1:
1464 dlg = wx.MessageDialog(self, 'Find String Not Found',
1465 'Find String Not Found in Demo File',
1466 wx.OK | wx.ICON_INFORMATION)
1467 dlg.ShowModal()
1468 dlg.Destroy()
1469 if self.finddlg:
1470 if loc == -1:
1471 self.finddlg.SetFocus()
1472 return
1473 else:
1474 self.finddlg.Destroy()
1475 editor.ShowPosition(loc)
1476 editor.SetSelection(loc, loc + len(findstring))
1477
1478
1479
1480 def OnFindNext(self, event):
1481 if self.finddata.GetFindString():
1482 self.OnFind(event)
1483 else:
1484 self.OnHelpFind(event)
1485
1486 def OnFindClose(self, event):
1487 event.GetDialog().Destroy()
1488
1489
1490 def OnOpenShellWindow(self, evt):
1491 if self.shell:
1492 # if it already exists then just make sure it's visible
1493 s = self.shell
1494 if s.IsIconized():
1495 s.Iconize(False)
1496 s.Raise()
1497 else:
1498 # Make a PyShell window
1499 from wx import py
1500 namespace = { 'wx' : wx,
1501 'app' : wx.GetApp(),
1502 'frame' : self,
1503 }
1504 self.shell = py.shell.ShellFrame(None, locals=namespace)
1505 self.shell.SetSize((640,480))
1506 self.shell.Show()
1507
1508 # Hook the close event of the main frame window so that we
1509 # close the shell at the same time if it still exists
1510 def CloseShell(evt):
1511 if self.shell:
1512 self.shell.Close()
1513 evt.Skip()
1514 self.Bind(wx.EVT_CLOSE, CloseShell)
1515
1516
1517 #---------------------------------------------
1518 def OnCloseWindow(self, event):
1519 self.dying = True
1520 self.demoPage = None
1521 self.codePage = None
1522 self.mainmenu = None
1523 self.tbicon.Destroy()
1524 self.Destroy()
1525
1526
1527 #---------------------------------------------
1528 def OnIdle(self, event):
1529 if self.otherWin:
1530 self.otherWin.Raise()
1531 self.demoPage = self.otherWin
1532 self.otherWin = None
1533
1534
1535 #---------------------------------------------
1536 def ShowTip(self):
1537 try:
1538 showTipText = open(opj("data/showTips")).read()
1539 showTip, index = eval(showTipText)
1540 except IOError:
1541 showTip, index = (1, 0)
1542 if showTip:
1543 tp = wx.CreateFileTipProvider(opj("data/tips.txt"), index)
1544 ##tp = MyTP(0)
1545 showTip = wx.ShowTip(self, tp)
1546 index = tp.GetCurrentTip()
1547 open(opj("data/showTips"), "w").write(str( (showTip, index) ))
1548
1549
1550 #---------------------------------------------
1551 def OnDemoMenu(self, event):
1552 try:
1553 selectedDemo = self.treeMap[self.mainmenu.GetLabel(event.GetId())]
1554 except:
1555 selectedDemo = None
1556 if selectedDemo:
1557 self.tree.SelectItem(selectedDemo)
1558 self.tree.EnsureVisible(selectedDemo)
1559
1560
1561
1562 #---------------------------------------------
1563 def OnIconfiy(self, evt):
1564 wx.LogMessage("OnIconfiy: %s" % evt.Iconized())
1565 evt.Skip()
1566
1567 #---------------------------------------------
1568 def OnMaximize(self, evt):
1569 wx.LogMessage("OnMaximize")
1570 evt.Skip()
1571
1572 #---------------------------------------------
1573 def OnActivate(self, evt):
1574 wx.LogMessage("OnActivate: %s" % evt.GetActive())
1575 evt.Skip()
1576
1577 #---------------------------------------------
1578 def OnAppActivate(self, evt):
1579 wx.LogMessage("OnAppActivate: %s" % evt.GetActive())
1580 evt.Skip()
1581
1582 #---------------------------------------------------------------------------
1583 #---------------------------------------------------------------------------
1584
1585 class MySplashScreen(wx.SplashScreen):
1586 def __init__(self):
1587 bmp = wx.Image(opj("bitmaps/splash.gif")).ConvertToBitmap()
1588 wx.SplashScreen.__init__(self, bmp,
1589 wx.SPLASH_CENTRE_ON_SCREEN | wx.SPLASH_TIMEOUT,
1590 3000, None, -1)
1591 self.Bind(wx.EVT_CLOSE, self.OnClose)
1592
1593 def OnClose(self, evt):
1594 self.Hide()
1595 frame = wxPythonDemo(None, "wxPython: (A Demonstration)")
1596 frame.Show()
1597 evt.Skip() # Make sure the default handler runs too...
1598
1599
1600 class MyApp(wx.App):
1601 def OnInit(self):
1602 """
1603 Create and show the splash screen. It will then create and show
1604 the main frame when it is time to do so.
1605 """
1606
1607 # For debugging
1608 #self.SetAssertMode(wx.PYAPP_ASSERT_DIALOG)
1609
1610 # Normally when using a SplashScreen you would create it, show
1611 # it and then continue on with the applicaiton's
1612 # initialization, finally creating and showing the main
1613 # application window(s). In this case we have nothing else to
1614 # do so we'll delay showing the main frame until later (see
1615 # OnClose above) so the users can see the SplashScreen effect.
1616 splash = MySplashScreen()
1617 splash.Show()
1618
1619 return True
1620
1621
1622
1623 #---------------------------------------------------------------------------
1624
1625 def main():
1626 try:
1627 demoPath = os.path.dirname(__file__)
1628 os.chdir(demoPath)
1629 except:
1630 pass
1631 app = MyApp(False)
1632 app.MainLoop()
1633
1634 #---------------------------------------------------------------------------
1635
1636
1637 mainOverview = """<html><body>
1638 <h2>wxPython</h2>
1639
1640 <p> wxPython is a <b>GUI toolkit</b> for the Python programming
1641 language. It allows Python programmers to create programs with a
1642 robust, highly functional graphical user interface, simply and easily.
1643 It is implemented as a Python extension module (native code) that
1644 wraps the popular wxWindows cross platform GUI library, which is
1645 written in C++.
1646
1647 <p> Like Python and wxWindows, wxPython is <b>Open Source</b> which
1648 means that it is free for anyone to use and the source code is
1649 available for anyone to look at and modify. Or anyone can contribute
1650 fixes or enhancements to the project.
1651
1652 <p> wxPython is a <b>cross-platform</b> toolkit. This means that the
1653 same program will run on multiple platforms without modification.
1654 Currently supported platforms are 32-bit Microsoft Windows, most Unix
1655 or unix-like systems, and Macintosh OS X. Since the language is
1656 Python, wxPython programs are <b>simple, easy</b> to write and easy to
1657 understand.
1658
1659 <p> <b>This demo</b> is not only a collection of test cases for
1660 wxPython, but is also designed to help you learn about and how to use
1661 wxPython. Each sample is listed in the tree control on the left.
1662 When a sample is selected in the tree then a module is loaded and run
1663 (usually in a tab of this notebook,) and the source code of the module
1664 is loaded in another tab for you to browse and learn from.
1665
1666 """
1667
1668
1669 #----------------------------------------------------------------------------
1670 #----------------------------------------------------------------------------
1671
1672 if __name__ == '__main__':
1673 __name__ = 'Main'
1674 main()
1675
1676 #----------------------------------------------------------------------------
1677
1678
1679
1680
1681
1682
1683