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