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