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