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