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