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