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