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