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