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