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