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