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