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