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