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