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