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