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