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