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