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