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