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