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