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