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