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