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