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