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