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