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