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