]> git.saurik.com Git - wxWidgets.git/blob - wxPython/demo/Main.py
fix wxUniv/MSW compilation with wxUSE_MENUS==0 (patch 1744277)
[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 if 'wxMac' not in wx.PlatformInfo:
1446 findNextItem = wx.MenuItem(menu, -1, 'Find &Next\tF3', 'Find Next')
1447 else:
1448 findNextItem = wx.MenuItem(menu, -1, 'Find &Next\tCtrl-G', 'Find Next')
1449 findNextItem.SetBitmap(images.catalog['findnext'].getBitmap())
1450 menu.AppendItem(findItem)
1451 menu.AppendItem(findNextItem)
1452 menu.AppendSeparator()
1453
1454 shellItem = wx.MenuItem(menu, -1, 'Open Py&Shell Window\tF5',
1455 'An interactive interpreter window with the demo app and frame objects in the namesapce')
1456 shellItem.SetBitmap(images.catalog['pyshell'].getBitmap())
1457 menu.AppendItem(shellItem)
1458 inspToolItem = wx.MenuItem(menu, -1, 'Open &Widget Inspector\tF6',
1459 'A tool that lets you browse the live widgets and sizers in an application')
1460 inspToolItem.SetBitmap(images.catalog['inspect'].getBitmap())
1461 menu.AppendItem(inspToolItem)
1462 if 'wxMac' not in wx.PlatformInfo:
1463 menu.AppendSeparator()
1464 helpItem = menu.Append(-1, '&About wxPython Demo', 'wxPython RULES!!!')
1465 wx.App.SetMacAboutMenuItemId(helpItem.GetId())
1466
1467 self.Bind(wx.EVT_MENU, self.OnOpenShellWindow, shellItem)
1468 self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, inspToolItem)
1469 self.Bind(wx.EVT_MENU, self.OnHelpAbout, helpItem)
1470 self.Bind(wx.EVT_MENU, self.OnHelpFind, findItem)
1471 self.Bind(wx.EVT_MENU, self.OnFindNext, findNextItem)
1472 self.Bind(wx.EVT_FIND, self.OnFind)
1473 self.Bind(wx.EVT_FIND_NEXT, self.OnFind)
1474 self.Bind(wx.EVT_FIND_CLOSE, self.OnFindClose)
1475 self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateFindItems, findItem)
1476 self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateFindItems, findNextItem)
1477 self.mainmenu.Append(menu, '&Help')
1478 self.SetMenuBar(self.mainmenu)
1479
1480 if False:
1481 # This is another way to set Accelerators, in addition to
1482 # using the '\t<key>' syntax in the menu items.
1483 aTable = wx.AcceleratorTable([(wx.ACCEL_ALT, ord('X'), exitItem.GetId()),
1484 (wx.ACCEL_CTRL, ord('H'), helpItem.GetId()),
1485 (wx.ACCEL_CTRL, ord('F'), findItem.GetId()),
1486 (wx.ACCEL_NORMAL, wx.WXK_F3, findnextItem.GetId()),
1487 (wx.ACCEL_NORMAL, wx.WXK_F9, shellItem.GetId()),
1488 ])
1489 self.SetAcceleratorTable(aTable)
1490
1491
1492 #---------------------------------------------
1493 def RecreateTree(self, evt=None):
1494 # Catch the search type (name or content)
1495 searchMenu = self.filter.GetMenu().GetMenuItems()
1496 fullSearch = searchMenu[1].IsChecked()
1497
1498 if evt:
1499 if fullSearch:
1500 # Do not`scan all the demo files for every char
1501 # the user input, use wx.EVT_TEXT_ENTER instead
1502 return
1503
1504 expansionState = self.tree.GetExpansionState()
1505
1506 current = None
1507 item = self.tree.GetSelection()
1508 if item:
1509 prnt = self.tree.GetItemParent(item)
1510 if prnt:
1511 current = (self.tree.GetItemText(item),
1512 self.tree.GetItemText(prnt))
1513
1514 self.tree.Freeze()
1515 self.tree.DeleteAllItems()
1516 self.root = self.tree.AddRoot("wxPython Overview")
1517 self.tree.SetItemImage(self.root, 0)
1518 self.tree.SetItemPyData(self.root, 0)
1519
1520 treeFont = self.tree.GetFont()
1521 catFont = self.tree.GetFont()
1522
1523 # The old native treectrl on MSW has a bug where it doesn't
1524 # draw all of the text for an item if the font is larger than
1525 # the default. It seems to be clipping the item's label as if
1526 # it was the size of the same label in the default font.
1527 if 'wxMSW' not in wx.PlatformInfo or wx.GetApp().GetComCtl32Version() >= 600:
1528 treeFont.SetPointSize(treeFont.GetPointSize()+2)
1529 treeFont.SetWeight(wx.BOLD)
1530 catFont.SetWeight(wx.BOLD)
1531
1532 self.tree.SetItemFont(self.root, treeFont)
1533
1534 firstChild = None
1535 selectItem = None
1536 filter = self.filter.GetValue()
1537 count = 0
1538
1539 for category, items in _treeList:
1540 count += 1
1541 if filter:
1542 if fullSearch:
1543 items = self.searchItems[category]
1544 else:
1545 items = [item for item in items if filter.lower() in item.lower()]
1546 if items:
1547 child = self.tree.AppendItem(self.root, category, image=count)
1548 self.tree.SetItemFont(child, catFont)
1549 self.tree.SetItemPyData(child, count)
1550 if not firstChild: firstChild = child
1551 for childItem in items:
1552 image = count
1553 if DoesModifiedExist(childItem):
1554 image = len(_demoPngs)
1555 theDemo = self.tree.AppendItem(child, childItem, image=image)
1556 self.tree.SetItemPyData(theDemo, count)
1557 self.treeMap[childItem] = theDemo
1558 if current and (childItem, category) == current:
1559 selectItem = theDemo
1560
1561
1562 self.tree.Expand(self.root)
1563 if firstChild:
1564 self.tree.Expand(firstChild)
1565 if filter:
1566 self.tree.ExpandAll()
1567 elif expansionState:
1568 self.tree.SetExpansionState(expansionState)
1569 if selectItem:
1570 self.skipLoad = True
1571 self.tree.SelectItem(selectItem)
1572 self.skipLoad = False
1573
1574 self.tree.Thaw()
1575 self.searchItems = {}
1576
1577
1578 def OnSearchMenu(self, event):
1579
1580 # Catch the search type (name or content)
1581 searchMenu = self.filter.GetMenu().GetMenuItems()
1582 fullSearch = searchMenu[1].IsChecked()
1583
1584 if fullSearch:
1585 self.OnSearch()
1586 else:
1587 self.RecreateTree()
1588
1589
1590 def OnSearch(self, event=None):
1591
1592 value = self.filter.GetValue()
1593 if not value:
1594 self.RecreateTree()
1595 return
1596
1597 wx.BeginBusyCursor()
1598
1599 for category, items in _treeList:
1600 self.searchItems[category] = []
1601 for childItem in items:
1602 if SearchDemo(childItem, value):
1603 self.searchItems[category].append(childItem)
1604
1605 wx.EndBusyCursor()
1606 self.RecreateTree()
1607
1608
1609 def SetTreeModified(self, modified):
1610 item = self.tree.GetSelection()
1611 if modified:
1612 image = len(_demoPngs)
1613 else:
1614 image = self.tree.GetItemPyData(item)
1615 self.tree.SetItemImage(item, image)
1616
1617
1618 def WriteText(self, text):
1619 if text[-1:] == '\n':
1620 text = text[:-1]
1621 wx.LogMessage(text)
1622
1623 def write(self, txt):
1624 self.WriteText(txt)
1625
1626 #---------------------------------------------
1627 def OnItemExpanded(self, event):
1628 item = event.GetItem()
1629 wx.LogMessage("OnItemExpanded: %s" % self.tree.GetItemText(item))
1630 event.Skip()
1631
1632 #---------------------------------------------
1633 def OnItemCollapsed(self, event):
1634 item = event.GetItem()
1635 wx.LogMessage("OnItemCollapsed: %s" % self.tree.GetItemText(item))
1636 event.Skip()
1637
1638 #---------------------------------------------
1639 def OnTreeLeftDown(self, event):
1640 # reset the overview text if the tree item is clicked on again
1641 pt = event.GetPosition();
1642 item, flags = self.tree.HitTest(pt)
1643 if item == self.tree.GetSelection():
1644 self.SetOverview(self.tree.GetItemText(item)+" Overview", self.curOverview)
1645 event.Skip()
1646
1647 #---------------------------------------------
1648 def OnSelChanged(self, event):
1649 if self.dying or not self.loaded or self.skipLoad:
1650 return
1651
1652 item = event.GetItem()
1653 itemText = self.tree.GetItemText(item)
1654 self.LoadDemo(itemText)
1655
1656 #---------------------------------------------
1657 def LoadDemo(self, demoName):
1658 try:
1659 wx.BeginBusyCursor()
1660 self.Freeze()
1661
1662 os.chdir(self.cwd)
1663 self.ShutdownDemoModule()
1664
1665 if demoName == self.overviewText:
1666 # User selected the "wxPython Overview" node
1667 # ie: _this_ module
1668 # Changing the main window at runtime not yet supported...
1669 self.demoModules = DemoModules(__name__)
1670 self.SetOverview(self.overviewText, mainOverview)
1671 self.LoadDemoSource()
1672 self.UpdateNotebook(0)
1673 else:
1674 if os.path.exists(GetOriginalFilename(demoName)):
1675 wx.LogMessage("Loading demo %s.py..." % demoName)
1676 self.demoModules = DemoModules(demoName)
1677 self.LoadDemoSource()
1678 else:
1679 self.SetOverview("wxPython", mainOverview)
1680 self.codePage = None
1681 self.UpdateNotebook(0)
1682 finally:
1683 wx.EndBusyCursor()
1684 self.Thaw()
1685
1686 #---------------------------------------------
1687 def LoadDemoSource(self):
1688 self.codePage = None
1689 self.codePage = DemoCodePanel(self.nb, self)
1690 self.codePage.LoadDemo(self.demoModules)
1691
1692 #---------------------------------------------
1693 def RunModule(self):
1694 """Runs the active module"""
1695
1696 module = self.demoModules.GetActive()
1697 self.ShutdownDemoModule()
1698 overviewText = ""
1699
1700 # o The RunTest() for all samples must now return a window that can
1701 # be palced in a tab in the main notebook.
1702 # o If an error occurs (or has occurred before) an error tab is created.
1703
1704 if module is not None:
1705 wx.LogMessage("Running demo module...")
1706 if hasattr(module, "overview"):
1707 overviewText = module.overview
1708
1709 try:
1710 self.demoPage = module.runTest(self, self.nb, self)
1711 except:
1712 self.demoPage = DemoErrorPanel(self.nb, self.codePage,
1713 DemoError(sys.exc_info()), self)
1714
1715 bg = self.nb.GetThemeBackgroundColour()
1716 if bg:
1717 self.demoPage.SetBackgroundColour(bg)
1718
1719 assert self.demoPage is not None, "runTest must return a window!"
1720
1721 else:
1722 # There was a previous error in compiling or exec-ing
1723 self.demoPage = DemoErrorPanel(self.nb, self.codePage,
1724 self.demoModules.GetErrorInfo(), self)
1725
1726 self.SetOverview(self.demoModules.name + " Overview", overviewText)
1727
1728 if self.firstTime:
1729 # change to the demo page the first time a module is run
1730 self.UpdateNotebook(2)
1731 self.firstTime = False
1732 else:
1733 # otherwise just stay on the same tab in case the user has changed to another one
1734 self.UpdateNotebook()
1735
1736 #---------------------------------------------
1737 def ShutdownDemoModule(self):
1738 if self.demoPage:
1739 # inform the window that it's time to quit if it cares
1740 if hasattr(self.demoPage, "ShutdownDemo"):
1741 self.demoPage.ShutdownDemo()
1742 wx.YieldIfNeeded() # in case the page has pending events
1743 self.demoPage = None
1744
1745 #---------------------------------------------
1746 def UpdateNotebook(self, select = -1):
1747 nb = self.nb
1748 debug = False
1749 self.Freeze()
1750
1751 def UpdatePage(page, pageText):
1752 pageExists = False
1753 pagePos = -1
1754 for i in range(nb.GetPageCount()):
1755 if nb.GetPageText(i) == pageText:
1756 pageExists = True
1757 pagePos = i
1758 break
1759
1760 if page:
1761 if not pageExists:
1762 # Add a new page
1763 nb.AddPage(page, pageText, imageId=nb.GetPageCount())
1764 if debug: wx.LogMessage("DBG: ADDED %s" % pageText)
1765 else:
1766 if nb.GetPage(pagePos) != page:
1767 # Reload an existing page
1768 nb.DeletePage(pagePos)
1769 nb.InsertPage(pagePos, page, pageText, imageId=pagePos)
1770 if debug: wx.LogMessage("DBG: RELOADED %s" % pageText)
1771 else:
1772 # Excellent! No redraw/flicker
1773 if debug: wx.LogMessage("DBG: SAVED from reloading %s" % pageText)
1774 elif pageExists:
1775 # Delete a page
1776 nb.DeletePage(pagePos)
1777 if debug: wx.LogMessage("DBG: DELETED %s" % pageText)
1778 else:
1779 if debug: wx.LogMessage("DBG: STILL GONE - %s" % pageText)
1780
1781 if select == -1:
1782 select = nb.GetSelection()
1783
1784 UpdatePage(self.codePage, "Demo Code")
1785 UpdatePage(self.demoPage, "Demo")
1786
1787 if select >= 0 and select < nb.GetPageCount():
1788 nb.SetSelection(select)
1789
1790 self.Thaw()
1791
1792 #---------------------------------------------
1793 def SetOverview(self, name, text):
1794 self.curOverview = text
1795 lead = text[:6]
1796 if lead != '<html>' and lead != '<HTML>':
1797 text = '<br>'.join(text.split('\n'))
1798 if wx.USE_UNICODE:
1799 text = text.decode('iso8859_1')
1800 self.ovr.SetPage(text)
1801 self.nb.SetPageText(0, name)
1802
1803 #---------------------------------------------
1804 # Menu methods
1805 def OnFileExit(self, *event):
1806 self.Close()
1807
1808 def OnToggleRedirect(self, event):
1809 app = wx.GetApp()
1810 if event.Checked():
1811 app.RedirectStdio()
1812 print "Print statements and other standard output will now be directed to this window."
1813 else:
1814 app.RestoreStdio()
1815 print "Print statements and other standard output will now be sent to the usual location."
1816
1817
1818 def OnAUIPerspectives(self, event):
1819 perspective = self.perspectives_menu.GetLabel(event.GetId())
1820 self.mgr.LoadPerspective(self.auiConfigurations[perspective])
1821 self.mgr.Update()
1822
1823
1824 def OnSavePerspective(self, event):
1825 dlg = wx.TextEntryDialog(self, "Enter a name for the new perspective:", "AUI Configuration")
1826
1827 dlg.SetValue(("Perspective %d")%(len(self.auiConfigurations)+1))
1828 if dlg.ShowModal() != wx.ID_OK:
1829 return
1830
1831 perspectiveName = dlg.GetValue()
1832 menuItems = self.perspectives_menu.GetMenuItems()
1833 for item in menuItems:
1834 if item.GetLabel() == perspectiveName:
1835 wx.MessageBox("The selected perspective name:\n\n%s\n\nAlready exists."%perspectiveName,
1836 "Error", style=wx.ICON_ERROR)
1837 return
1838
1839 item = wx.MenuItem(self.perspectives_menu, -1, dlg.GetValue(),
1840 "Load user perspective %d"%(len(self.auiConfigurations)+1),
1841 wx.ITEM_RADIO)
1842 self.Bind(wx.EVT_MENU, self.OnAUIPerspectives, item)
1843 self.perspectives_menu.AppendItem(item)
1844 item.Check(True)
1845 self.auiConfigurations.update({dlg.GetValue(): self.mgr.SavePerspective()})
1846
1847
1848 def OnDeletePerspective(self, event):
1849 menuItems = self.perspectives_menu.GetMenuItems()[1:]
1850 lst = []
1851 loadDefault = False
1852
1853 for item in menuItems:
1854 lst.append(item.GetLabel())
1855
1856 dlg = wx.MultiChoiceDialog(self,
1857 "Please select the perspectives\nyou would like to delete:",
1858 "Delete AUI Perspectives", lst)
1859
1860 if dlg.ShowModal() == wx.ID_OK:
1861 selections = dlg.GetSelections()
1862 strings = [lst[x] for x in selections]
1863 for sel in strings:
1864 self.auiConfigurations.pop(sel)
1865 item = menuItems[lst.index(sel)]
1866 if item.IsChecked():
1867 loadDefault = True
1868 self.perspectives_menu.GetMenuItems()[0].Check(True)
1869 self.perspectives_menu.DeleteItem(item)
1870 lst.remove(sel)
1871
1872 if loadDefault:
1873 self.mgr.LoadPerspective(self.auiConfigurations[DEFAULT_PERSPECTIVE])
1874 self.mgr.Update()
1875
1876
1877 def OnTreeExpansion(self, event):
1878 self.tree.SetExpansionState(self.expansionState)
1879
1880
1881 def OnHelpAbout(self, event):
1882 from About import MyAboutBox
1883 about = MyAboutBox(self)
1884 about.ShowModal()
1885 about.Destroy()
1886
1887 def OnHelpFind(self, event):
1888 if self.finddlg != None:
1889 return
1890
1891 self.nb.SetSelection(1)
1892 self.finddlg = wx.FindReplaceDialog(self, self.finddata, "Find",
1893 wx.FR_NOMATCHCASE | wx.FR_NOWHOLEWORD)
1894 self.finddlg.Show(True)
1895
1896
1897 def OnUpdateFindItems(self, evt):
1898 evt.Enable(self.finddlg == None)
1899
1900
1901 def OnFind(self, event):
1902 editor = self.codePage.editor
1903 self.nb.SetSelection(1)
1904 end = editor.GetLastPosition()
1905 textstring = editor.GetRange(0, end).lower()
1906 findstring = self.finddata.GetFindString().lower()
1907 backward = not (self.finddata.GetFlags() & wx.FR_DOWN)
1908 if backward:
1909 start = editor.GetSelection()[0]
1910 loc = textstring.rfind(findstring, 0, start)
1911 else:
1912 start = editor.GetSelection()[1]
1913 loc = textstring.find(findstring, start)
1914 if loc == -1 and start != 0:
1915 # string not found, start at beginning
1916 if backward:
1917 start = end
1918 loc = textstring.rfind(findstring, 0, start)
1919 else:
1920 start = 0
1921 loc = textstring.find(findstring, start)
1922 if loc == -1:
1923 dlg = wx.MessageDialog(self, 'Find String Not Found',
1924 'Find String Not Found in Demo File',
1925 wx.OK | wx.ICON_INFORMATION)
1926 dlg.ShowModal()
1927 dlg.Destroy()
1928 if self.finddlg:
1929 if loc == -1:
1930 self.finddlg.SetFocus()
1931 return
1932 else:
1933 self.finddlg.Destroy()
1934 self.finddlg = None
1935 editor.ShowPosition(loc)
1936 editor.SetSelection(loc, loc + len(findstring))
1937
1938
1939
1940 def OnFindNext(self, event):
1941 if self.finddata.GetFindString():
1942 self.OnFind(event)
1943 else:
1944 self.OnHelpFind(event)
1945
1946 def OnFindClose(self, event):
1947 event.GetDialog().Destroy()
1948 self.finddlg = None
1949
1950
1951 def OnOpenShellWindow(self, evt):
1952 if self.shell:
1953 # if it already exists then just make sure it's visible
1954 s = self.shell
1955 if s.IsIconized():
1956 s.Iconize(False)
1957 s.Raise()
1958 else:
1959 # Make a PyShell window
1960 from wx import py
1961 namespace = { 'wx' : wx,
1962 'app' : wx.GetApp(),
1963 'frame' : self,
1964 }
1965 self.shell = py.shell.ShellFrame(None, locals=namespace)
1966 self.shell.SetSize((640,480))
1967 self.shell.Show()
1968
1969 # Hook the close event of the main frame window so that we
1970 # close the shell at the same time if it still exists
1971 def CloseShell(evt):
1972 if self.shell:
1973 self.shell.Close()
1974 evt.Skip()
1975 self.Bind(wx.EVT_CLOSE, CloseShell)
1976
1977
1978 def OnOpenWidgetInspector(self, evt):
1979 # Activate the widget inspection tool
1980 from wx.lib.inspection import InspectionTool
1981 if not InspectionTool().initialized:
1982 InspectionTool().Init()
1983
1984 # Find a widget to be selected in the tree. Use either the
1985 # one under the cursor, if any, or this frame.
1986 wnd = wx.FindWindowAtPointer()
1987 if not wnd:
1988 wnd = self
1989 InspectionTool().Show(wnd, True)
1990
1991
1992 #---------------------------------------------
1993 def OnCloseWindow(self, event):
1994 self.dying = True
1995 self.demoPage = None
1996 self.codePage = None
1997 self.mainmenu = None
1998 if self.tbicon is not None:
1999 self.tbicon.Destroy()
2000
2001 config = GetConfig()
2002 config.Write('ExpansionState', str(self.tree.GetExpansionState()))
2003 config.Write('AUIPerspectives', str(self.auiConfigurations))
2004 config.Flush()
2005
2006 self.Destroy()
2007
2008
2009 #---------------------------------------------
2010 def OnIdle(self, event):
2011 if self.otherWin:
2012 self.otherWin.Raise()
2013 self.demoPage = self.otherWin
2014 self.otherWin = None
2015
2016
2017 #---------------------------------------------
2018 def ShowTip(self):
2019 config = GetConfig()
2020 showTipText = config.Read("tips")
2021 if showTipText:
2022 showTip, index = eval(showTipText)
2023 else:
2024 showTip, index = (1, 0)
2025
2026 if showTip:
2027 tp = wx.CreateFileTipProvider(opj("data/tips.txt"), index)
2028 ##tp = MyTP(0)
2029 showTip = wx.ShowTip(self, tp)
2030 index = tp.GetCurrentTip()
2031 config.Write("tips", str( (showTip, index) ))
2032 config.Flush()
2033
2034 #---------------------------------------------
2035 def OnDemoMenu(self, event):
2036 try:
2037 selectedDemo = self.treeMap[self.mainmenu.GetLabel(event.GetId())]
2038 except:
2039 selectedDemo = None
2040 if selectedDemo:
2041 self.tree.SelectItem(selectedDemo)
2042 self.tree.EnsureVisible(selectedDemo)
2043
2044
2045
2046 #---------------------------------------------
2047 def OnIconfiy(self, evt):
2048 wx.LogMessage("OnIconfiy: %s" % evt.Iconized())
2049 evt.Skip()
2050
2051 #---------------------------------------------
2052 def OnMaximize(self, evt):
2053 wx.LogMessage("OnMaximize")
2054 evt.Skip()
2055
2056 #---------------------------------------------
2057 def OnActivate(self, evt):
2058 wx.LogMessage("OnActivate: %s" % evt.GetActive())
2059 evt.Skip()
2060
2061 #---------------------------------------------
2062 def OnAppActivate(self, evt):
2063 wx.LogMessage("OnAppActivate: %s" % evt.GetActive())
2064 evt.Skip()
2065
2066 #---------------------------------------------------------------------------
2067 #---------------------------------------------------------------------------
2068
2069 class MySplashScreen(wx.SplashScreen):
2070 def __init__(self):
2071 bmp = wx.Image(opj("bitmaps/splash.png")).ConvertToBitmap()
2072 wx.SplashScreen.__init__(self, bmp,
2073 wx.SPLASH_CENTRE_ON_SCREEN | wx.SPLASH_TIMEOUT,
2074 5000, None, -1)
2075 self.Bind(wx.EVT_CLOSE, self.OnClose)
2076 self.fc = wx.FutureCall(2000, self.ShowMain)
2077
2078
2079 def OnClose(self, evt):
2080 # Make sure the default handler runs too so this window gets
2081 # destroyed
2082 evt.Skip()
2083 self.Hide()
2084
2085 # if the timer is still running then go ahead and show the
2086 # main frame now
2087 if self.fc.IsRunning():
2088 self.fc.Stop()
2089 self.ShowMain()
2090
2091
2092 def ShowMain(self):
2093 frame = wxPythonDemo(None, "wxPython: (A Demonstration)")
2094 frame.Show()
2095 if self.fc.IsRunning():
2096 self.Raise()
2097 wx.CallAfter(frame.ShowTip)
2098
2099
2100
2101
2102 #---------------------------------------------------------------------------
2103
2104 from wx.lib.mixins.treemixin import ExpansionState
2105 if USE_CUSTOMTREECTRL:
2106 import wx.lib.customtreectrl as CT
2107 TreeBaseClass = CT.CustomTreeCtrl
2108 else:
2109 TreeBaseClass = wx.TreeCtrl
2110
2111
2112 class wxPythonDemoTree(ExpansionState, TreeBaseClass):
2113 def __init__(self, parent):
2114 TreeBaseClass.__init__(self, parent, style=wx.TR_DEFAULT_STYLE|
2115 wx.TR_HAS_VARIABLE_ROW_HEIGHT)
2116 self.BuildTreeImageList()
2117 if USE_CUSTOMTREECTRL:
2118 self.SetSpacing(10)
2119 self.SetWindowStyle(self.GetWindowStyle() & ~wx.TR_LINES_AT_ROOT)
2120
2121 def AppendItem(self, parent, text, image=-1, wnd=None):
2122 if USE_CUSTOMTREECTRL:
2123 item = TreeBaseClass.AppendItem(self, parent, text, image=image, wnd=wnd)
2124 else:
2125 item = TreeBaseClass.AppendItem(self, parent, text, image=image)
2126 return item
2127
2128 def BuildTreeImageList(self):
2129 imgList = wx.ImageList(16, 16)
2130 for png in _demoPngs:
2131 imgList.Add(images.catalog[png].getBitmap())
2132
2133 # add the image for modified demos.
2134 imgList.Add(images.catalog["custom"].getBitmap())
2135
2136 self.AssignImageList(imgList)
2137
2138
2139 def GetItemIdentity(self, item):
2140 return self.GetPyData(item)
2141
2142
2143 #---------------------------------------------------------------------------
2144
2145 class MyApp(wx.App):
2146 def OnInit(self):
2147 """
2148 Create and show the splash screen. It will then create and show
2149 the main frame when it is time to do so.
2150 """
2151
2152 wx.SystemOptions.SetOptionInt("mac.window-plain-transition", 1)
2153 self.SetAppName("wxPyDemo")
2154
2155 # For debugging
2156 #self.SetAssertMode(wx.PYAPP_ASSERT_DIALOG)
2157
2158 # Normally when using a SplashScreen you would create it, show
2159 # it and then continue on with the applicaiton's
2160 # initialization, finally creating and showing the main
2161 # application window(s). In this case we have nothing else to
2162 # do so we'll delay showing the main frame until later (see
2163 # ShowMain above) so the users can see the SplashScreen effect.
2164 splash = MySplashScreen()
2165 splash.Show()
2166
2167 return True
2168
2169
2170
2171 #---------------------------------------------------------------------------
2172
2173 def main():
2174 try:
2175 demoPath = os.path.dirname(__file__)
2176 os.chdir(demoPath)
2177 except:
2178 pass
2179 app = MyApp(False)
2180 app.MainLoop()
2181
2182 #---------------------------------------------------------------------------
2183
2184
2185 mainOverview = """<html><body>
2186 <h2>wxPython</h2>
2187
2188 <p> wxPython is a <b>GUI toolkit</b> for the Python programming
2189 language. It allows Python programmers to create programs with a
2190 robust, highly functional graphical user interface, simply and easily.
2191 It is implemented as a Python extension module (native code) that
2192 wraps the popular wxWindows cross platform GUI library, which is
2193 written in C++.
2194
2195 <p> Like Python and wxWindows, wxPython is <b>Open Source</b> which
2196 means that it is free for anyone to use and the source code is
2197 available for anyone to look at and modify. Or anyone can contribute
2198 fixes or enhancements to the project.
2199
2200 <p> wxPython is a <b>cross-platform</b> toolkit. This means that the
2201 same program will run on multiple platforms without modification.
2202 Currently supported platforms are 32-bit Microsoft Windows, most Unix
2203 or unix-like systems, and Macintosh OS X. Since the language is
2204 Python, wxPython programs are <b>simple, easy</b> to write and easy to
2205 understand.
2206
2207 <p> <b>This demo</b> is not only a collection of test cases for
2208 wxPython, but is also designed to help you learn about and how to use
2209 wxPython. Each sample is listed in the tree control on the left.
2210 When a sample is selected in the tree then a module is loaded and run
2211 (usually in a tab of this notebook,) and the source code of the module
2212 is loaded in another tab for you to browse and learn from.
2213
2214 """
2215
2216
2217 #----------------------------------------------------------------------------
2218 #----------------------------------------------------------------------------
2219
2220 if __name__ == '__main__':
2221 __name__ = 'Main'
2222 main()
2223
2224 #----------------------------------------------------------------------------
2225
2226
2227
2228
2229
2230
2231