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