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