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