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