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