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