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