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