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