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