]> git.saurik.com Git - wxWidgets.git/blob - wxPython/demo/Main.py
Update the notebook before running the sample in case it is a modal
[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 ('Base 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 self.ReloadDemo()
535
536
537 def LoadDemoSource(self, source):
538 self.editor.Clear()
539 self.editor.SetValue(source)
540 self.JumpToLine(0)
541 self.btnSave.Enable(False)
542
543
544 def JumpToLine(self, line, highlight=False):
545 self.editor.GotoLine(line)
546 self.editor.SetFocus()
547 if highlight:
548 self.editor.SelectLine(line)
549
550
551 def UpdateControlState(self):
552 active = self.demoModules.GetActiveID()
553 # Update the radio/restore buttons
554 for moduleID in self.radioButtons:
555 btn = self.radioButtons[moduleID]
556 if moduleID == active:
557 btn.SetValue(True)
558 else:
559 btn.SetValue(False)
560
561 if self.demoModules.Exists(moduleID):
562 btn.Enable(True)
563 if moduleID == modModified:
564 self.btnRestore.Enable(True)
565 else:
566 btn.Enable(False)
567 if moduleID == modModified:
568 self.btnRestore.Enable(False)
569
570
571 def OnRadioButton(self, event):
572 radioSelected = event.GetEventObject()
573 modSelected = radioSelected.modID
574 if modSelected != self.demoModules.GetActiveID():
575 busy = wx.BusyInfo("Reloading demo module...")
576 self.demoModules.SetActive(modSelected)
577 self.ActiveModuleChanged()
578
579
580 def ReloadDemo(self):
581 if self.demoModules.name != __name__:
582 self.mainFrame.RunModule()
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
629
630 def OnRestore(self, event): # Handles the "Delete Modified" button
631 modifiedFilename = GetModifiedFilename(self.demoModules.name)
632 self.demoModules.Delete(modModified)
633 os.unlink(modifiedFilename) # Delete the modified copy
634 busy = wx.BusyInfo("Reloading demo module...")
635 self.ActiveModuleChanged()
636
637
638 #---------------------------------------------------------------------------
639
640 def opj(path):
641 """Convert paths to the platform-specific separator"""
642 str = apply(os.path.join, tuple(path.split('/')))
643 # HACK: on Linux, a leading / gets lost...
644 if path.startswith('/'):
645 str = '/' + str
646 return str
647
648
649 def GetModifiedDirectory():
650 """
651 Returns the directory where modified versions of the demo files
652 are stored
653 """
654 return opj(wx.GetHomeDir() + "/.wxPyDemo/modified/")
655
656
657 def GetModifiedFilename(name):
658 """
659 Returns the filename of the modified version of the specified demo
660 """
661 if not name.endswith(".py"):
662 name = name + ".py"
663 return GetModifiedDirectory() + name
664
665
666 def GetOriginalFilename(name):
667 """
668 Returns the filename of the original version of the specified demo
669 """
670 if not name.endswith(".py"):
671 name = name + ".py"
672 return name
673
674
675 def DoesModifiedExist(name):
676 """Returns whether the specified demo has a modified copy"""
677 if os.path.exists(GetModifiedFilename(name)):
678 return True
679 else:
680 return False
681
682
683 #---------------------------------------------------------------------------
684
685 class ModuleDictWrapper:
686 """Emulates a module with a dynamically compiled __dict__"""
687 def __init__(self, dict):
688 self.dict = dict
689
690 def __getattr__(self, name):
691 if name in self.dict:
692 return self.dict[name]
693 else:
694 raise AttributeError
695
696 class DemoModules:
697 """
698 Dynamically manages the original/modified versions of a demo
699 module
700 """
701 def __init__(self, name):
702 self.modActive = -1
703 self.name = name
704
705 # (dict , source , filename , description , error information )
706 # ( 0 , 1 , 2 , 3 , 4 )
707 self.modules = [[None, "" , "" , "<original>" , None],
708 [None, "" , "" , "<modified>" , None]]
709
710 # load original module
711 self.LoadFromFile(modOriginal, GetOriginalFilename(name))
712
713 # load modified module (if one exists)
714 if DoesModifiedExist(name):
715 self.LoadFromFile(modModified, GetModifiedFilename(name))
716
717
718 def LoadFromFile(self, modID, filename):
719 self.modules[modID][2] = filename
720 file = open(filename, "rt")
721 self.LoadFromSource(modID, file.read())
722 file.close()
723
724
725 def LoadFromSource(self, modID, source):
726 self.modules[modID][1] = source
727 self.LoadDict(modID)
728
729
730 def LoadDict(self, modID):
731 if self.name != __name__:
732 source = self.modules[modID][1]
733 description = self.modules[modID][3]
734
735 try:
736 self.modules[modID][0] = {}
737 code = compile(source, description, "exec")
738 exec code in self.modules[modID][0]
739 except:
740 self.modules[modID][4] = DemoError(sys.exc_info())
741 self.modules[modID][0] = None
742 else:
743 self.modules[modID][4] = None
744
745
746 def SetActive(self, modID):
747 if modID != modOriginal and modID != modModified:
748 raise LookupError
749 else:
750 self.modActive = modID
751
752
753 def GetActive(self):
754 dict = self.modules[self.modActive][0]
755 if dict is None:
756 return None
757 else:
758 return ModuleDictWrapper(dict)
759
760
761 def GetActiveID(self):
762 return self.modActive
763
764
765 def GetSource(self, modID = None):
766 if modID is None:
767 modID = self.modActive
768 return self.modules[modID][1]
769
770
771 def GetFilename(self, modID = None):
772 if modID is None:
773 modID = self.modActive
774 return self.modules[self.modActive][2]
775
776
777 def GetErrorInfo(self, modID = None):
778 if modID is None:
779 modID = self.modActive
780 return self.modules[self.modActive][4]
781
782
783 def Exists(self, modID):
784 return self.modules[modID][1] != ""
785
786
787 def UpdateFile(self, modID = None):
788 """Updates the file from which a module was loaded
789 with (possibly updated) source"""
790 if modID is None:
791 modID = self.modActive
792
793 source = self.modules[modID][1]
794 filename = self.modules[modID][2]
795
796 try:
797 file = open(filename, "wt")
798 file.write(source)
799 finally:
800 file.close()
801
802
803 def Delete(self, modID):
804 if self.modActive == modID:
805 self.SetActive(0)
806
807 self.modules[modID][0] = None
808 self.modules[modID][1] = ""
809 self.modules[modID][2] = ""
810
811
812 #---------------------------------------------------------------------------
813 class ReloadDemoPanel(wx.Panel):
814 """
815 Panel put into the demo tab when the demo just shows some
816 top-level window. Enables the demo to be reloaded after being
817 closed.
818 """
819
820 infoText = "This demo runs outside the main window"
821
822 def __init__(self, parent, codePanel, log):
823 wx.Panel.__init__(self, parent, -1)
824 self.codePanel = codePanel
825 self.log = log
826
827 self.label = wx.StaticText(self, -1, self.infoText)
828 self.btnReload = wx.Button(self, -1, "Reload Demo")
829 self.btnReload.Bind(wx.EVT_BUTTON, self.OnReload)
830
831 self.box = wx.BoxSizer(wx.VERTICAL)
832 self.box.Add(self.label, 0, wx.ALIGN_CENTER | wx.ALL, 10)
833 self.box.Add(self.btnReload, 0, wx.ALIGN_CENTER | wx.ALL, 10)
834
835 self.box.Fit(self)
836 self.SetSizer(self.box)
837
838 def OnReload(self, event):
839 self.codePanel.ReloadDemo()
840
841 #---------------------------------------------------------------------------
842
843 class DemoError:
844 """Wraps and stores information about the current exception"""
845 def __init__(self, exc_info):
846 import copy
847
848 excType, excValue = exc_info[:2]
849 # traceback list entries: (filename, line number, function name, text)
850 self.traceback = traceback.extract_tb(exc_info[2])
851
852 # --Based on traceback.py::format_exception_only()--
853 if type(excType) == types.ClassType:
854 self.exception_type = excType.__name__
855 else:
856 self.exception_type = excType
857
858 # If it's a syntax error, extra information needs
859 # to be added to the traceback
860 if excType is SyntaxError:
861 try:
862 msg, (filename, lineno, self.offset, line) = excValue
863 except:
864 pass
865 else:
866 if not filename:
867 filename = "<string>"
868 line = line.strip()
869 self.traceback.append( (filename, lineno, "", line) )
870 excValue = msg
871 try:
872 self.exception_details = str(excValue)
873 except:
874 self.exception_details = "<unprintable %s object>" & type(excValue).__name__
875
876 del exc_info
877
878 def __str__(self):
879 ret = "Type %s \n \
880 Traceback: %s \n \
881 Details : %s" % ( str(self.exception_type), str(self.traceback), self.exception_details )
882 return ret
883
884 #---------------------------------------------------------------------------
885
886 class DemoErrorPanel(wx.Panel):
887 """Panel put into the demo tab when the demo fails to run due to errors"""
888
889 def __init__(self, parent, codePanel, demoError, log):
890 wx.Panel.__init__(self, parent, -1)#, style=wx.NO_FULL_REPAINT_ON_RESIZE)
891 self.codePanel = codePanel
892 self.nb = parent
893 self.log = log
894
895 self.box = wx.BoxSizer(wx.VERTICAL)
896
897 # Main Label
898 self.box.Add(wx.StaticText(self, -1, "An error has occured while trying to run the demo")
899 , 0, wx.ALIGN_CENTER | wx.TOP, 10)
900
901 # Exception Information
902 boxInfo = wx.StaticBox(self, -1, "Exception Info" )
903 boxInfoSizer = wx.StaticBoxSizer(boxInfo, wx.VERTICAL ) # Used to center the grid within the box
904 boxInfoGrid = wx.FlexGridSizer(0, 2, 0, 0)
905 textFlags = wx.ALIGN_RIGHT | wx.LEFT | wx.RIGHT | wx.TOP
906 boxInfoGrid.Add(wx.StaticText(self, -1, "Type: "), 0, textFlags, 5 )
907 boxInfoGrid.Add(wx.StaticText(self, -1, demoError.exception_type) , 0, textFlags, 5 )
908 boxInfoGrid.Add(wx.StaticText(self, -1, "Details: ") , 0, textFlags, 5 )
909 boxInfoGrid.Add(wx.StaticText(self, -1, demoError.exception_details) , 0, textFlags, 5 )
910 boxInfoSizer.Add(boxInfoGrid, 0, wx.ALIGN_CENTRE | wx.ALL, 5 )
911 self.box.Add(boxInfoSizer, 0, wx.ALIGN_CENTER | wx.ALL, 5)
912
913 # Set up the traceback list
914 # This one automatically resizes last column to take up remaining space
915 from ListCtrl import TestListCtrl
916 self.list = TestListCtrl(self, -1, style=wx.LC_REPORT | wx.SUNKEN_BORDER)
917 self.list.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
918 self.list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
919 self.list.InsertColumn(0, "Filename")
920 self.list.InsertColumn(1, "Line", wx.LIST_FORMAT_RIGHT)
921 self.list.InsertColumn(2, "Function")
922 self.list.InsertColumn(3, "Code")
923 self.InsertTraceback(self.list, demoError.traceback)
924 self.list.SetColumnWidth(0, wx.LIST_AUTOSIZE)
925 self.list.SetColumnWidth(2, wx.LIST_AUTOSIZE)
926 self.box.Add(wx.StaticText(self, -1, "Traceback:")
927 , 0, wx.ALIGN_CENTER | wx.TOP, 5)
928 self.box.Add(self.list, 1, wx.GROW | wx.ALIGN_CENTER | wx.ALL, 5)
929 self.box.Add(wx.StaticText(self, -1, "Entries from the demo module are shown in blue\n"
930 + "Double-click on them to go to the offending line")
931 , 0, wx.ALIGN_CENTER | wx.BOTTOM, 5)
932
933 self.box.Fit(self)
934 self.SetSizer(self.box)
935
936
937 def InsertTraceback(self, list, traceback):
938 #Add the traceback data
939 for x in range(len(traceback)):
940 data = traceback[x]
941 list.InsertStringItem(x, os.path.basename(data[0])) # Filename
942 list.SetStringItem(x, 1, str(data[1])) # Line
943 list.SetStringItem(x, 2, str(data[2])) # Function
944 list.SetStringItem(x, 3, str(data[3])) # Code
945
946 # Check whether this entry is from the demo module
947 if data[0] == "<original>" or data[0] == "<modified>": # FIXME: make more generalised
948 self.list.SetItemData(x, int(data[1])) # Store line number for easy access
949 # Give it a blue colour
950 item = self.list.GetItem(x)
951 item.SetTextColour(wx.BLUE)
952 self.list.SetItem(item)
953 else:
954 self.list.SetItemData(x, -1) # Editor can't jump into this one's code
955
956
957 def OnItemSelected(self, event):
958 # This occurs before OnDoubleClick and can be used to set the
959 # currentItem. OnDoubleClick doesn't get a wxListEvent....
960 self.currentItem = event.m_itemIndex
961 event.Skip()
962
963
964 def OnDoubleClick(self, event):
965 # If double-clicking on a demo's entry, jump to the line number
966 line = self.list.GetItemData(self.currentItem)
967 if line != -1:
968 self.nb.SetSelection(1) # Switch to the code viewer tab
969 wx.CallAfter(self.codePanel.JumpToLine, line-1, True)
970 event.Skip()
971
972
973 #---------------------------------------------------------------------------
974
975 class wxPythonDemo(wx.Frame):
976 overviewText = "wxPython Overview"
977
978 def __init__(self, parent, title):
979 wx.Frame.__init__(self, parent, -1, title, size = (950, 750),
980 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
981
982 self.loaded = False
983 self.cwd = os.getcwd()
984 self.curOverview = ""
985 self.demoPage = None
986 self.codePage = None
987 self.useModified = False
988 self.shell = None
989
990 icon = images.getMondrianIcon()
991 self.SetIcon(icon)
992
993 if wx.Platform != '__WXMAC__':
994 # setup a taskbar icon, and catch some events from it
995 dim = 16 # (may want to use 22 on wxGTK, but 16 looks okay too)
996 icon = wx.IconFromBitmap(
997 images.getMondrianImage().Scale(dim,dim).ConvertToBitmap() )
998 #icon = wx.Icon('bmp_source/mondrian.ico', wx.BITMAP_TYPE_ICO)
999 #icon = images.getMondrianIcon()
1000 self.tbicon = wx.TaskBarIcon()
1001 self.tbicon.SetIcon(icon, "wxPython Demo")
1002 self.tbicon.Bind(wx.EVT_TASKBAR_LEFT_DCLICK, self.OnTaskBarActivate)
1003 self.tbicon.Bind(wx.EVT_TASKBAR_RIGHT_UP, self.OnTaskBarMenu)
1004 self.tbicon.Bind(wx.EVT_MENU, self.OnTaskBarActivate, id=self.TBMENU_RESTORE)
1005 self.tbicon.Bind(wx.EVT_MENU, self.OnTaskBarClose, id=self.TBMENU_CLOSE)
1006
1007 wx.CallAfter(self.ShowTip)
1008
1009 self.otherWin = None
1010 self.Bind(wx.EVT_IDLE, self.OnIdle)
1011 self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
1012 self.Bind(wx.EVT_ICONIZE, self.OnIconfiy)
1013 self.Bind(wx.EVT_MAXIMIZE, self.OnMaximize)
1014
1015 self.Centre(wx.BOTH)
1016 self.CreateStatusBar(1, wx.ST_SIZEGRIP)
1017
1018 splitter = wx.SplitterWindow(self, -1, style=wx.CLIP_CHILDREN | wx.SP_LIVE_UPDATE | wx.SP_3D)
1019 splitter2 = wx.SplitterWindow(splitter, -1, style=wx.CLIP_CHILDREN | wx.SP_LIVE_UPDATE | wx.SP_3D)
1020
1021 def EmptyHandler(evt): pass
1022 #splitter.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
1023 #splitter2.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
1024
1025 # Prevent TreeCtrl from displaying all items after destruction when True
1026 self.dying = False
1027
1028 # Create a Notebook
1029 self.nb = wx.Notebook(splitter2, -1, style=wx.CLIP_CHILDREN)
1030
1031 # Make a File menu
1032 self.mainmenu = wx.MenuBar()
1033 menu = wx.Menu()
1034 item = menu.Append(-1, '&Redirect Output',
1035 'Redirect print statements to a window',
1036 wx.ITEM_CHECK)
1037 self.Bind(wx.EVT_MENU, self.OnToggleRedirect, item)
1038
1039 item = menu.Append(-1, 'E&xit\tAlt-X', 'Get the heck outta here!')
1040 self.Bind(wx.EVT_MENU, self.OnFileExit, item)
1041 wx.App_SetMacExitMenuItemId(item.GetId())
1042 self.mainmenu.Append(menu, '&File')
1043
1044 # Make a Demo menu
1045 menu = wx.Menu()
1046 for item in _treeList:
1047 submenu = wx.Menu()
1048 for childItem in item[1]:
1049 mi = submenu.Append(-1, childItem)
1050 self.Bind(wx.EVT_MENU, self.OnDemoMenu, mi)
1051 menu.AppendMenu(wx.NewId(), item[0], submenu)
1052 self.mainmenu.Append(menu, '&Demo')
1053
1054 # Make a Demo Code menu
1055 #TODO: Add new menu items
1056 # Like the option-enabled entries to select the
1057 # active module
1058 #TODO: should we bother?
1059
1060 #menu = wx.Menu()
1061 #saveID = wx.NewId()
1062 #restoreID = wx.NewId()
1063 #
1064 #menu.Append(saveID, '&Save\tCtrl-S', 'Save edited demo')
1065 #menu.Append(restoreID, '&Delete Modified\tCtrl-R', 'Delete modified copy')
1066 #self.Bind(wx.EVT_MENU, self.codePage.OnSave, id=saveID)
1067 #self.Bind(wx.EVT_MENU, self.codePage.OnRestore, id=restoreID)
1068 #self.mainmenu.Append(menu, 'Demo &Code')
1069 #
1070
1071 # Make a Help menu
1072 menu = wx.Menu()
1073 findItem = menu.Append(-1, '&Find\tCtrl-F', 'Find in the Demo Code')
1074 findnextItem = menu.Append(-1, 'Find &Next\tF3', 'Find Next')
1075 menu.AppendSeparator()
1076
1077 shellItem = menu.Append(-1, 'Open Py&Shell Window\tF5',
1078 'An interactive interpreter window with the demo app and frame objects in the namesapce')
1079 menu.AppendSeparator()
1080 helpItem = menu.Append(-1, '&About\tCtrl-H', 'wxPython RULES!!!')
1081 wx.App_SetMacAboutMenuItemId(helpItem.GetId())
1082
1083 self.Bind(wx.EVT_MENU, self.OnOpenShellWindow, shellItem)
1084 self.Bind(wx.EVT_MENU, self.OnHelpAbout, helpItem)
1085 self.Bind(wx.EVT_MENU, self.OnHelpFind, findItem)
1086 self.Bind(wx.EVT_MENU, self.OnFindNext, findnextItem)
1087 self.Bind(wx.EVT_COMMAND_FIND, self.OnFind)
1088 self.Bind(wx.EVT_COMMAND_FIND_NEXT, self.OnFind)
1089 self.Bind(wx.EVT_COMMAND_FIND_CLOSE, self.OnFindClose)
1090 self.mainmenu.Append(menu, '&Help')
1091 self.SetMenuBar(self.mainmenu)
1092
1093 self.finddata = wx.FindReplaceData()
1094
1095 if 0:
1096 # This is another way to set Accelerators, in addition to
1097 # using the '\t<key>' syntax in the menu items.
1098 aTable = wx.AcceleratorTable([(wx.ACCEL_ALT, ord('X'), exitID),
1099 (wx.ACCEL_CTRL, ord('H'), helpID),
1100 (wx.ACCEL_CTRL, ord('F'), findID),
1101 (wx.ACCEL_NORMAL, WXK_F3, findnextID)
1102 ])
1103 self.SetAcceleratorTable(aTable)
1104
1105
1106 # Create a TreeCtrl
1107 tID = wx.NewId()
1108 self.treeMap = {}
1109 self.tree = wx.TreeCtrl(splitter, tID, style =
1110 wx.TR_DEFAULT_STYLE #| wx.TR_HAS_VARIABLE_ROW_HEIGHT
1111 )
1112
1113 root = self.tree.AddRoot("wxPython Overview")
1114 firstChild = None
1115 for item in _treeList:
1116 child = self.tree.AppendItem(root, item[0])
1117 if not firstChild: firstChild = child
1118 for childItem in item[1]:
1119 theDemo = self.tree.AppendItem(child, childItem)
1120 self.treeMap[childItem] = theDemo
1121
1122 self.tree.Expand(root)
1123 self.tree.Expand(firstChild)
1124 self.tree.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded, id=tID)
1125 self.tree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed, id=tID)
1126 self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=tID)
1127 self.tree.Bind(wx.EVT_LEFT_DOWN, self.OnTreeLeftDown)
1128
1129 # Set up a wx.html.HtmlWindow on the Overview Notebook page
1130 # we put it in a panel first because there seems to be a
1131 # refresh bug of some sort (wxGTK) when it is directly in
1132 # the notebook...
1133 if 0: # the old way
1134 self.ovr = wx.html.HtmlWindow(self.nb, -1, size=(400, 400))
1135 self.nb.AddPage(self.ovr, self.overviewText)
1136
1137 else: # hopefully I can remove this hacky code soon, see SF bug #216861
1138 panel = wx.Panel(self.nb, -1, style=wx.CLIP_CHILDREN)
1139 self.ovr = wx.html.HtmlWindow(panel, -1, size=(400, 400))
1140 self.nb.AddPage(panel, self.overviewText)
1141
1142 def OnOvrSize(evt, ovr=self.ovr):
1143 ovr.SetSize(evt.GetSize())
1144 panel.Bind(wx.EVT_SIZE, OnOvrSize)
1145 panel.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
1146
1147 if "gtk2" in wx.PlatformInfo:
1148 self.ovr.NormalizeFontSizes()
1149 self.SetOverview(self.overviewText, mainOverview)
1150
1151
1152 # Set up a log window
1153 self.log = wx.TextCtrl(splitter2, -1,
1154 style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
1155
1156 # Set the wxWindows log target to be this textctrl
1157 #wx.Log_SetActiveTarget(wx.LogTextCtrl(self.log))
1158
1159 # But instead of the above we want to show how to use our own wx.Log class
1160 wx.Log_SetActiveTarget(MyLog(self.log))
1161
1162 # for serious debugging
1163 #wx.Log_SetActiveTarget(wx.LogStderr())
1164 #wx.Log_SetTraceMask(wx.TraceMessages)
1165
1166
1167 # add the windows to the splitter and split it.
1168 splitter2.SplitHorizontally(self.nb, self.log, -160)
1169 splitter.SplitVertically(self.tree, splitter2, 200)
1170
1171 splitter.SetMinimumPaneSize(20)
1172 splitter2.SetMinimumPaneSize(20)
1173
1174 # Make the splitter on the right expand the top window when resized
1175 def SplitterOnSize(evt):
1176 splitter = evt.GetEventObject()
1177 sz = splitter.GetSize()
1178 splitter.SetSashPosition(sz.height - 160, False)
1179 evt.Skip()
1180
1181 splitter2.Bind(wx.EVT_SIZE, SplitterOnSize)
1182
1183 # select initial items
1184 self.nb.SetSelection(0)
1185 self.tree.SelectItem(root)
1186
1187 # Load 'Main' module
1188 self.LoadDemo(self.overviewText)
1189 self.loaded = True
1190
1191 # select some other initial module?
1192 if len(sys.argv) > 1:
1193 arg = sys.argv[1]
1194 if arg.endswith('.py'):
1195 arg = arg[:-3]
1196 selectedDemo = self.treeMap.get(arg, None)
1197 if selectedDemo:
1198 self.tree.SelectItem(selectedDemo)
1199 self.tree.EnsureVisible(selectedDemo)
1200
1201
1202 #---------------------------------------------
1203 def WriteText(self, text):
1204 if text[-1:] == '\n':
1205 text = text[:-1]
1206 wx.LogMessage(text)
1207
1208 def write(self, txt):
1209 self.WriteText(txt)
1210
1211 #---------------------------------------------
1212 def OnItemExpanded(self, event):
1213 item = event.GetItem()
1214 wx.LogMessage("OnItemExpanded: %s" % self.tree.GetItemText(item))
1215 event.Skip()
1216
1217 #---------------------------------------------
1218 def OnItemCollapsed(self, event):
1219 item = event.GetItem()
1220 wx.LogMessage("OnItemCollapsed: %s" % self.tree.GetItemText(item))
1221 event.Skip()
1222
1223 #---------------------------------------------
1224 def OnTreeLeftDown(self, event):
1225 # reset the overview text if the tree item is clicked on again
1226 pt = event.GetPosition();
1227 item, flags = self.tree.HitTest(pt)
1228 if item == self.tree.GetSelection():
1229 self.SetOverview(self.tree.GetItemText(item)+" Overview", self.curOverview)
1230 event.Skip()
1231
1232 #---------------------------------------------
1233 def OnSelChanged(self, event):
1234 if self.dying or not self.loaded:
1235 return
1236
1237 item = event.GetItem()
1238 itemText = self.tree.GetItemText(item)
1239 self.LoadDemo(itemText)
1240
1241 #---------------------------------------------
1242 def LoadDemo(self, demoName):
1243 try:
1244 wx.BeginBusyCursor()
1245
1246 os.chdir(self.cwd)
1247 self.ShutdownDemoModule()
1248
1249 if demoName == self.overviewText:
1250 # User selected the "wxPython Overview" node
1251 # ie: _this_ module
1252 # Changing the main window at runtime not yet supported...
1253 self.demoModules = DemoModules(__name__)
1254 self.SetOverview(self.overviewText, mainOverview)
1255 self.LoadDemoSource()
1256 self.UpdateNotebook(0)
1257 else:
1258 if os.path.exists(GetOriginalFilename(demoName)):
1259 wx.LogMessage("Loading demo %s.py..." % demoName)
1260 self.demoModules = DemoModules(demoName)
1261 self.LoadDemoSource()
1262 self.tree.Refresh()
1263 else:
1264 self.SetOverview("wxPython", mainOverview)
1265 self.codePage = None
1266 self.UpdateNotebook(0)
1267 finally:
1268 wx.EndBusyCursor()
1269
1270 #---------------------------------------------
1271 def LoadDemoSource(self):
1272 self.codePage = None
1273 self.codePage = DemoCodePanel(self.nb, self)
1274 self.codePage.LoadDemo(self.demoModules)
1275
1276 #---------------------------------------------
1277 def RunModule(self):
1278 """Runs the active module"""
1279
1280 module = self.demoModules.GetActive()
1281 self.ShutdownDemoModule()
1282 overviewText = ""
1283 prevSelect = -1
1284
1285 # o If the demo returns a window it is placed in a tab.
1286 # o Otherwise, a placeholder tab is created, informing the user that the
1287 # demo runs outside the main window, and allowing it to be reloaded.
1288 # o If an error occurs (or has occured before) an error tab is created.
1289
1290 if module is not None:
1291 wx.LogMessage("Running demo module...")
1292 if hasattr(module, "overview"):
1293 overviewText = module.overview
1294
1295 # in case runTest is modal, make sure things look right
1296 # before it starts...
1297 prevSelect = self.UpdateNotebook()
1298 wx.YieldIfNeeded()
1299
1300 try:
1301 self.demoPage = module.runTest(self, self.nb, self)
1302 if self.demoPage is None:
1303 self.demoPage = ReloadDemoPanel(self.nb, self.codePage, self)
1304 except:
1305 self.demoPage = DemoErrorPanel(self.nb, self.codePage, 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, self.demoModules.GetErrorInfo(), self)
1309
1310 self.SetOverview(self.demoModules.name + " Overview", overviewText)
1311 self.UpdateNotebook(prevSelect)
1312
1313 #---------------------------------------------
1314 def ShutdownDemoModule(self):
1315 if self.demoPage:
1316 # inform the window that it's time to quit if it cares
1317 if hasattr(self.demoPage, "ShutdownDemo"):
1318 self.demoPage.ShutdownDemo()
1319 wx.YieldIfNeeded() # in case the page has pending events
1320 self.demoPage = None
1321
1322 #---------------------------------------------
1323 def UpdateNotebook(self, select = -1):
1324 nb = self.nb
1325 debug = False
1326
1327 def UpdatePage(page, pageText):
1328 pageExists = False
1329 pagePos = -1
1330 for i in range(nb.GetPageCount()):
1331 if nb.GetPageText(i) == pageText:
1332 pageExists = True
1333 pagePos = i
1334 break
1335
1336 if page:
1337 if not pageExists:
1338 # Add a new page
1339
1340 # panel = wx.Panel(nb, -1)
1341 # page.Reparent(panel)
1342 # panel.page = page
1343 # nb.AddPage(panel, pageText)
1344 nb.AddPage(page, pageText)
1345 if debug: wx.LogMessage("DBG: ADDED %s" % pageText)
1346 else:
1347 # if nb.GetPage(pagePos).page != page:
1348 if nb.GetPage(pagePos) != page:
1349 # Reload an existing page
1350 nb.Freeze()
1351
1352 # panel = nb.GetPage(pagePos)
1353 # panel.page = page
1354 # page.Reparent(panel)
1355
1356 nb.DeletePage(pagePos)
1357 nb.InsertPage(pagePos, page, pageText)
1358 nb.Thaw()
1359 if debug: wx.LogMessage("DBG: RELOADED %s" % pageText)
1360 else:
1361 # Excellent! No redraw/flicker
1362 if debug: wx.LogMessage("DBG: SAVED from reloading %s" % pageText)
1363 elif pageExists:
1364 # Delete a page
1365 nb.DeletePage(pagePos)
1366 if debug: wx.LogMessage("DBG: DELETED %s" % pageText)
1367 else:
1368 if debug: wx.LogMessage("DBG: STILL GONE - %s" % pageText)
1369
1370 if select == -1:
1371 select = nb.GetSelection()
1372
1373 UpdatePage(self.codePage, "Demo Code")
1374 UpdatePage(self.demoPage, "Demo")
1375
1376 if select >= 0 and select < nb.GetPageCount():
1377 nb.SetSelection(select)
1378
1379 return select
1380
1381 #---------------------------------------------
1382 def SetOverview(self, name, text):
1383 self.curOverview = text
1384 lead = text[:6]
1385 if lead != '<html>' and lead != '<HTML>':
1386 text = '<br>'.join(text.split('\n'))
1387 if wx.USE_UNICODE:
1388 text = text.decode('iso8859_1')
1389 self.ovr.SetPage(text)
1390 self.nb.SetPageText(0, name)
1391
1392 #---------------------------------------------
1393 # Menu methods
1394 def OnFileExit(self, *event):
1395 self.Close()
1396
1397 def OnToggleRedirect(self, event):
1398 app = wx.GetApp()
1399 if event.Checked():
1400 app.RedirectStdio()
1401 print "Print statements and other standard output will now be directed to this window."
1402 else:
1403 app.RestoreStdio()
1404 print "Print statements and other standard output will now be sent to the usual location."
1405
1406 def OnHelpAbout(self, event):
1407 from About import MyAboutBox
1408 about = MyAboutBox(self)
1409 about.ShowModal()
1410 about.Destroy()
1411
1412 def OnHelpFind(self, event):
1413 self.nb.SetSelection(1)
1414 self.finddlg = wx.FindReplaceDialog(self, self.finddata, "Find",
1415 wx.FR_NOUPDOWN |
1416 wx.FR_NOMATCHCASE |
1417 wx.FR_NOWHOLEWORD)
1418 self.finddlg.Show(True)
1419
1420 def OnFind(self, event):
1421 editor = self.codePage.editor
1422 self.nb.SetSelection(1)
1423 end = editor.GetLastPosition()
1424 textstring = editor.GetRange(0, end).lower()
1425 start = editor.GetSelection()[1]
1426 findstring = self.finddata.GetFindString().lower()
1427 loc = textstring.find(findstring, start)
1428 if loc == -1 and start != 0:
1429 # string not found, start at beginning
1430 start = 0
1431 loc = textstring.find(findstring, start)
1432 if loc == -1:
1433 dlg = wx.MessageDialog(self, 'Find String Not Found',
1434 'Find String Not Found in Demo File',
1435 wx.OK | wx.ICON_INFORMATION)
1436 dlg.ShowModal()
1437 dlg.Destroy()
1438 if self.finddlg:
1439 if loc == -1:
1440 self.finddlg.SetFocus()
1441 return
1442 else:
1443 self.finddlg.Destroy()
1444 editor.ShowPosition(loc)
1445 editor.SetSelection(loc, loc + len(findstring))
1446
1447
1448
1449 def OnFindNext(self, event):
1450 if self.finddata.GetFindString():
1451 self.OnFind(event)
1452 else:
1453 self.OnHelpFind(event)
1454
1455 def OnFindClose(self, event):
1456 event.GetDialog().Destroy()
1457
1458
1459 def OnOpenShellWindow(self, evt):
1460 if self.shell:
1461 # if it already exists then just make sure it's visible
1462 s = self.shell
1463 if s.IsIconized():
1464 s.Iconize(False)
1465 s.Raise()
1466 else:
1467 # Make a PyShell window
1468 from wx import py
1469 namespace = { 'wx' : wx,
1470 'app' : wx.GetApp(),
1471 'frame' : self,
1472 }
1473 self.shell = py.shell.ShellFrame(None, locals=namespace)
1474 self.shell.SetSize((640,480))
1475 self.shell.Show()
1476
1477 # Hook the close event of the main frame window so that we
1478 # close the shell at the same time if it still exists
1479 def CloseShell(evt):
1480 if self.shell:
1481 self.shell.Close()
1482 evt.Skip()
1483 self.Bind(wx.EVT_CLOSE, CloseShell)
1484
1485
1486 #---------------------------------------------
1487 def OnCloseWindow(self, event):
1488 self.dying = True
1489 self.demoPage = None
1490 self.codePage = None
1491 self.mainmenu = None
1492 self.Destroy()
1493
1494
1495 #---------------------------------------------
1496 def OnIdle(self, event):
1497 if self.otherWin:
1498 self.otherWin.Raise()
1499 self.demoPage = self.otherWin
1500 self.otherWin = None
1501
1502
1503 #---------------------------------------------
1504 def ShowTip(self):
1505 try:
1506 showTipText = open(opj("data/showTips")).read()
1507 showTip, index = eval(showTipText)
1508 except IOError:
1509 showTip, index = (1, 0)
1510 if showTip:
1511 tp = wx.CreateFileTipProvider(opj("data/tips.txt"), index)
1512 ##tp = MyTP(0)
1513 showTip = wx.ShowTip(self, tp)
1514 index = tp.GetCurrentTip()
1515 open(opj("data/showTips"), "w").write(str( (showTip, index) ))
1516
1517
1518 #---------------------------------------------
1519 def OnDemoMenu(self, event):
1520 try:
1521 selectedDemo = self.treeMap[self.mainmenu.GetLabel(event.GetId())]
1522 except:
1523 selectedDemo = None
1524 if selectedDemo:
1525 self.tree.SelectItem(selectedDemo)
1526 self.tree.EnsureVisible(selectedDemo)
1527
1528
1529 #---------------------------------------------
1530 def OnTaskBarActivate(self, evt):
1531 if self.IsIconized():
1532 self.Iconize(False)
1533 if not self.IsShown():
1534 self.Show(True)
1535 self.Raise()
1536
1537 #---------------------------------------------
1538
1539 TBMENU_RESTORE = 1000
1540 TBMENU_CLOSE = 1001
1541
1542 def OnTaskBarMenu(self, evt):
1543 menu = wx.Menu()
1544 menu.Append(self.TBMENU_RESTORE, "Restore wxPython Demo")
1545 menu.Append(self.TBMENU_CLOSE, "Close")
1546 self.tbicon.PopupMenu(menu)
1547 menu.Destroy()
1548
1549 #---------------------------------------------
1550 def OnTaskBarClose(self, evt):
1551 self.Close()
1552
1553 # because of the way wx.TaskBarIcon.PopupMenu is implemented we have to
1554 # prod the main idle handler a bit to get the window to actually close
1555 wx.GetApp().ProcessIdle()
1556
1557
1558 #---------------------------------------------
1559 def OnIconfiy(self, evt):
1560 wx.LogMessage("OnIconfiy: %d" % evt.Iconized())
1561 evt.Skip()
1562
1563 #---------------------------------------------
1564 def OnMaximize(self, evt):
1565 wx.LogMessage("OnMaximize")
1566 evt.Skip()
1567
1568
1569
1570
1571 #---------------------------------------------------------------------------
1572 #---------------------------------------------------------------------------
1573
1574 class MySplashScreen(wx.SplashScreen):
1575 def __init__(self):
1576 bmp = wx.Image(opj("bitmaps/splash.gif")).ConvertToBitmap()
1577 wx.SplashScreen.__init__(self, bmp,
1578 wx.SPLASH_CENTRE_ON_SCREEN | wx.SPLASH_TIMEOUT,
1579 3000, None, -1)
1580 self.Bind(wx.EVT_CLOSE, self.OnClose)
1581
1582 def OnClose(self, evt):
1583 self.Hide()
1584 frame = wxPythonDemo(None, "wxPython: (A Demonstration)")
1585 frame.Show()
1586 evt.Skip() # Make sure the default handler runs too...
1587
1588
1589 class MyApp(wx.App):
1590 def OnInit(self):
1591 """
1592 Create and show the splash screen. It will then create and show
1593 the main frame when it is time to do so.
1594 """
1595
1596 # For debugging
1597 #self.SetAssertMode(wx.PYAPP_ASSERT_DIALOG)
1598
1599 # Normally when using a SplashScreen you would create it, show
1600 # it and then continue on with the applicaiton's
1601 # initialization, finally creating and showing the main
1602 # application window(s). In this case we have nothing else to
1603 # do so we'll delay showing the main frame until later (see
1604 # OnClose above) so the users can see the SplashScreen effect.
1605 splash = MySplashScreen()
1606 splash.Show()
1607
1608 return True
1609
1610
1611
1612 #---------------------------------------------------------------------------
1613
1614 def main():
1615 try:
1616 demoPath = os.path.dirname(__file__)
1617 os.chdir(demoPath)
1618 except:
1619 pass
1620 app = MyApp(0) ##wx.Platform == "__WXMAC__")
1621 app.MainLoop()
1622
1623 #---------------------------------------------------------------------------
1624
1625
1626 mainOverview = """<html><body>
1627 <h2>wxPython</h2>
1628
1629 <p> wxPython is a <b>GUI toolkit</b> for the Python programming
1630 language. It allows Python programmers to create programs with a
1631 robust, highly functional graphical user interface, simply and easily.
1632 It is implemented as a Python extension module (native code) that
1633 wraps the popular wxWindows cross platform GUI library, which is
1634 written in C++.
1635
1636 <p> Like Python and wxWindows, wxPython is <b>Open Source</b> which
1637 means that it is free for anyone to use and the source code is
1638 available for anyone to look at and modify. Or anyone can contribute
1639 fixes or enhancements to the project.
1640
1641 <p> wxPython is a <b>cross-platform</b> toolkit. This means that the
1642 same program will run on multiple platforms without modification.
1643 Currently supported platforms are 32-bit Microsoft Windows, most Unix
1644 or unix-like systems, and Macintosh OS X. Since the language is
1645 Python, wxPython programs are <b>simple, easy</b> to write and easy to
1646 understand.
1647
1648 <p> <b>This demo</b> is not only a collection of test cases for
1649 wxPython, but is also designed to help you learn about and how to use
1650 wxPython. Each sample is listed in the tree control on the left.
1651 When a sample is selected in the tree then a module is loaded and run
1652 (usually in a tab of this notebook,) and the source code of the module
1653 is loaded in another tab for you to browse and learn from.
1654
1655 """
1656
1657
1658 #----------------------------------------------------------------------------
1659 #----------------------------------------------------------------------------
1660
1661 if __name__ == '__main__':
1662 __name__ = 'Main'
1663 main()
1664
1665 #----------------------------------------------------------------------------
1666
1667
1668
1669
1670
1671
1672