]> git.saurik.com Git - wxWidgets.git/blame - wxPython/demo/Main.py
A little cleanup
[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();
ef8b9c3e 39##print "wx.VERSION_STRING = ", wx.VERSION_STRING
4276dc52 40##print 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', [
42f5333f 50 'FoldPanelBar',
2e5aa9c4 51 'GIFAnimationCtrl',
a6987674 52 'HyperLinkCtrl',
9c67cbec
RD
53 ]),
54
1e4a197e 55 # managed windows == things with a (optional) caption you can close
03a604a6 56 ('Frames and Dialogs', [
299647ac
RD
57 'Dialog',
58 'Frame',
59 'MDIWindows',
60 'MiniFrame',
61 'Wizard',
9c67cbec
RD
62 ]),
63
64 # the common dialogs
65 ('Common Dialogs', [
299647ac
RD
66 'ColourDialog',
67 'DirDialog',
68 'FileDialog',
299647ac
RD
69 'FindReplaceDialog',
70 'FontDialog',
71 'MessageDialog',
89eb18ec 72 'MultiChoiceDialog',
299647ac
RD
73 'PageSetupDialog',
74 'PrintDialog',
75 'ProgressDialog',
76 'SingleChoiceDialog',
77 'TextEntryDialog',
9c67cbec
RD
78 ]),
79
af83019e 80 # dialogs from libraries
9c67cbec 81 ('More Dialogs', [
9c67cbec 82 'ImageBrowser',
299647ac
RD
83 'MultipleChoiceDialog',
84 'ScrolledMessageDialog',
9c67cbec
RD
85 ]),
86
87 # core controls
88 ('Core Windows/Controls', [
8b7fa2d9 89 'BitmapButton',
299647ac
RD
90 'Button',
91 'CheckBox',
92 'CheckListBox',
93 'Choice',
9694b21a 94 'Choicebook',
299647ac
RD
95 'ComboBox',
96 'Gauge',
97 'Grid',
98 'Grid_MegaExample',
99 'ListBox',
100 'ListCtrl',
101 'ListCtrl_virtual',
15513a80 102 'ListCtrl_edit',
299647ac
RD
103 'Listbook',
104 'Menu',
105 'Notebook',
1fded56b 106 'PopupMenu',
299647ac
RD
107 'PopupWindow',
108 'RadioBox',
109 'RadioButton',
110 'SashWindow',
111 'ScrolledWindow',
112 'Slider',
113 'SpinButton',
114 'SpinCtrl',
115 'SplitterWindow',
116 'StaticBitmap',
e79c8a7c 117 'StaticBox',
299647ac
RD
118 'StaticText',
119 'StatusBar',
f2b4a4ea 120 'StockButtons',
299647ac
RD
121 'TextCtrl',
122 'ToggleButton',
123 'ToolBar',
124 'TreeCtrl',
125 'Validator',
9c67cbec
RD
126 ]),
127
d14a1e28
RD
128 ('Custom Controls', [
129 'AnalogClockWindow',
130 'ColourSelect',
299647ac 131 'Editor',
d14a1e28 132 'GenericButtons',
299647ac
RD
133 'GenericDirCtrl',
134 'LEDNumberCtrl',
135 'MultiSash',
136 'PopupControl',
137 'PyColourChooser',
138 'TreeListCtrl',
d14a1e28
RD
139 ]),
140
8b9a4190 141 # controls coming from other libraries
9c67cbec 142 ('More Windows/Controls', [
b7c75283
RD
143 'ActiveX_FlashWindow',
144 'ActiveX_IEHtmlWindow',
145 'ActiveX_PDFWindow',
299647ac
RD
146 #'RightTextCtrl', deprecated as we have wxTE_RIGHT now.
147 'Calendar',
148 'CalendarCtrl',
9c67cbec 149 'ContextHelp',
91334b48 150 'DatePickerCtrl',
299647ac
RD
151 'DynamicSashWindow',
152 'EditableListBox',
9c67cbec
RD
153 'FancyText',
154 'FileBrowseButton',
299647ac
RD
155 'FloatBar',
156 'FloatCanvas',
42f5333f 157 'FoldPanelBar',
2e5aa9c4 158 'GIFAnimationCtrl',
299647ac 159 'HtmlWindow',
a6987674 160 'HyperLinkCtrl',
299647ac 161 'IntCtrl',
256c055e 162 'MediaCtrl',
299647ac 163 'MVCTree',
1fded56b 164 'MaskedEditControls',
299647ac
RD
165 'MaskedNumCtrl',
166 'MimeTypesManager',
9c67cbec 167 'PyCrust',
80b27b4e 168 'PyPlot',
299647ac
RD
169 'PyShell',
170 'ScrolledPanel',
9c67cbec 171 'SplitTree',
299647ac
RD
172 'StyledTextCtrl_1',
173 'StyledTextCtrl_2',
9c67cbec 174 'TablePrint',
1e4a197e 175 'Throbber',
cccce1a6 176 'Ticker',
299647ac
RD
177 'TimeCtrl',
178 'VListBox',
9c67cbec
RD
179 ]),
180
181 # How to lay out the controls in a frame/dialog
182 ('Window Layout', [
299647ac 183 'GridBagSizer',
9c67cbec 184 'LayoutAnchors',
299647ac 185 'LayoutConstraints',
9c67cbec
RD
186 'Layoutf',
187 'RowColSizer',
299647ac 188 'ScrolledPanel',
9c67cbec 189 'Sizers',
299647ac
RD
190 'XmlResource',
191 'XmlResourceHandler',
192 'XmlResourceSubclass',
9c67cbec
RD
193 ]),
194
195 # ditto
196 ('Process and Events', [
1e4a197e 197 'EventManager',
299647ac 198 'KeyEvents',
299647ac 199 'Process',
9c67cbec
RD
200 'PythonEvents',
201 'Threads',
299647ac 202 'Timer',
c163da42 203 ##'infoframe', # needs better explanation and some fixing
9c67cbec
RD
204 ]),
205
206 # Clipboard and DnD
207 ('Clipboard and DnD', [
208 'CustomDragAndDrop',
209 'DragAndDrop',
210 'URLDragAndDrop',
211 ]),
212
213 # Images
1e4a197e 214 ('Using Images', [
299647ac 215 'ArtProvider',
76343679 216 'Cursor',
299647ac 217 'DragImage',
2e5aa9c4 218 'GIFAnimationCtrl',
299647ac 219 'Image',
8a88769e 220 'ImageAlpha',
299647ac
RD
221 'ImageFromStream',
222 'Mask',
1e4a197e 223 'Throbber',
9c67cbec
RD
224 ]),
225
226 # Other stuff
227 ('Miscellaneous', [
228 'ColourDB',
c163da42 229 ##'DialogUnits', # needs more explanations
9c67cbec 230 'DrawXXXList',
299647ac 231 'FileHistory',
9c67cbec 232 'FontEnumerator',
299647ac 233 'Joystick',
51c5e1f2 234 'MouseGestures',
299647ac 235 'OGL',
9c67cbec 236 'PrintFramework',
3628e088 237 'ShapedWindow',
78862f24 238 'Sound',
62038e59 239 'StandardPaths',
9c67cbec 240 'Unicode',
9c67cbec
RD
241 ]),
242
243 # need libs not coming with the demo
d7403ad2 244 ('Samples using an external library', [
299647ac 245 'GLCanvas',
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]
804 description = self.modules[modID][3]
805
806 try:
807 self.modules[modID][0] = {}
808 code = compile(source, description, "exec")
809 exec code in self.modules[modID][0]
810 except:
811 self.modules[modID][4] = DemoError(sys.exc_info())
812 self.modules[modID][0] = None
813 else:
814 self.modules[modID][4] = None
815
816
817 def SetActive(self, modID):
818 if modID != modOriginal and modID != modModified:
819 raise LookupError
820 else:
821 self.modActive = modID
822
823
824 def GetActive(self):
825 dict = self.modules[self.modActive][0]
826 if dict is None:
827 return None
828 else:
829 return ModuleDictWrapper(dict)
830
831
832 def GetActiveID(self):
833 return self.modActive
834
835
836 def GetSource(self, modID = None):
837 if modID is None:
838 modID = self.modActive
839 return self.modules[modID][1]
840
841
842 def GetFilename(self, modID = None):
843 if modID is None:
844 modID = self.modActive
845 return self.modules[self.modActive][2]
846
847
848 def GetErrorInfo(self, modID = None):
849 if modID is None:
850 modID = self.modActive
851 return self.modules[self.modActive][4]
852
853
854 def Exists(self, modID):
855 return self.modules[modID][1] != ""
856
857
858 def UpdateFile(self, modID = None):
859 """Updates the file from which a module was loaded
860 with (possibly updated) source"""
861 if modID is None:
862 modID = self.modActive
863
864 source = self.modules[modID][1]
865 filename = self.modules[modID][2]
866
867 try:
887a1bd9 868 file = open(filename, "wt")
08ecc920
RD
869 file.write(source)
870 finally:
871 file.close()
872
873
874 def Delete(self, modID):
875 if self.modActive == modID:
876 self.SetActive(0)
877
878 self.modules[modID][0] = None
879 self.modules[modID][1] = ""
880 self.modules[modID][2] = ""
881
882
08ecc920
RD
883#---------------------------------------------------------------------------
884
885class DemoError:
886 """Wraps and stores information about the current exception"""
887 def __init__(self, exc_info):
888 import copy
889
890 excType, excValue = exc_info[:2]
891 # traceback list entries: (filename, line number, function name, text)
892 self.traceback = traceback.extract_tb(exc_info[2])
893
894 # --Based on traceback.py::format_exception_only()--
895 if type(excType) == types.ClassType:
896 self.exception_type = excType.__name__
897 else:
898 self.exception_type = excType
899
900 # If it's a syntax error, extra information needs
901 # to be added to the traceback
902 if excType is SyntaxError:
903 try:
904 msg, (filename, lineno, self.offset, line) = excValue
905 except:
906 pass
907 else:
908 if not filename:
909 filename = "<string>"
910 line = line.strip()
911 self.traceback.append( (filename, lineno, "", line) )
912 excValue = msg
913 try:
914 self.exception_details = str(excValue)
915 except:
916 self.exception_details = "<unprintable %s object>" & type(excValue).__name__
917
918 del exc_info
919
920 def __str__(self):
921 ret = "Type %s \n \
922 Traceback: %s \n \
923 Details : %s" % ( str(self.exception_type), str(self.traceback), self.exception_details )
924 return ret
925
926#---------------------------------------------------------------------------
927
928class DemoErrorPanel(wx.Panel):
929 """Panel put into the demo tab when the demo fails to run due to errors"""
930
931 def __init__(self, parent, codePanel, demoError, log):
932 wx.Panel.__init__(self, parent, -1)#, style=wx.NO_FULL_REPAINT_ON_RESIZE)
933 self.codePanel = codePanel
934 self.nb = parent
935 self.log = log
936
937 self.box = wx.BoxSizer(wx.VERTICAL)
938
939 # Main Label
d6922577 940 self.box.Add(wx.StaticText(self, -1, "An error has occurred while trying to run the demo")
08ecc920
RD
941 , 0, wx.ALIGN_CENTER | wx.TOP, 10)
942
943 # Exception Information
944 boxInfo = wx.StaticBox(self, -1, "Exception Info" )
945 boxInfoSizer = wx.StaticBoxSizer(boxInfo, wx.VERTICAL ) # Used to center the grid within the box
946 boxInfoGrid = wx.FlexGridSizer(0, 2, 0, 0)
947 textFlags = wx.ALIGN_RIGHT | wx.LEFT | wx.RIGHT | wx.TOP
948 boxInfoGrid.Add(wx.StaticText(self, -1, "Type: "), 0, textFlags, 5 )
949 boxInfoGrid.Add(wx.StaticText(self, -1, demoError.exception_type) , 0, textFlags, 5 )
950 boxInfoGrid.Add(wx.StaticText(self, -1, "Details: ") , 0, textFlags, 5 )
951 boxInfoGrid.Add(wx.StaticText(self, -1, demoError.exception_details) , 0, textFlags, 5 )
952 boxInfoSizer.Add(boxInfoGrid, 0, wx.ALIGN_CENTRE | wx.ALL, 5 )
953 self.box.Add(boxInfoSizer, 0, wx.ALIGN_CENTER | wx.ALL, 5)
954
955 # Set up the traceback list
956 # This one automatically resizes last column to take up remaining space
957 from ListCtrl import TestListCtrl
958 self.list = TestListCtrl(self, -1, style=wx.LC_REPORT | wx.SUNKEN_BORDER)
959 self.list.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
960 self.list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
961 self.list.InsertColumn(0, "Filename")
962 self.list.InsertColumn(1, "Line", wx.LIST_FORMAT_RIGHT)
963 self.list.InsertColumn(2, "Function")
964 self.list.InsertColumn(3, "Code")
965 self.InsertTraceback(self.list, demoError.traceback)
966 self.list.SetColumnWidth(0, wx.LIST_AUTOSIZE)
967 self.list.SetColumnWidth(2, wx.LIST_AUTOSIZE)
968 self.box.Add(wx.StaticText(self, -1, "Traceback:")
969 , 0, wx.ALIGN_CENTER | wx.TOP, 5)
970 self.box.Add(self.list, 1, wx.GROW | wx.ALIGN_CENTER | wx.ALL, 5)
971 self.box.Add(wx.StaticText(self, -1, "Entries from the demo module are shown in blue\n"
972 + "Double-click on them to go to the offending line")
973 , 0, wx.ALIGN_CENTER | wx.BOTTOM, 5)
974
975 self.box.Fit(self)
976 self.SetSizer(self.box)
977
978
979 def InsertTraceback(self, list, traceback):
980 #Add the traceback data
981 for x in range(len(traceback)):
982 data = traceback[x]
983 list.InsertStringItem(x, os.path.basename(data[0])) # Filename
984 list.SetStringItem(x, 1, str(data[1])) # Line
985 list.SetStringItem(x, 2, str(data[2])) # Function
986 list.SetStringItem(x, 3, str(data[3])) # Code
987
988 # Check whether this entry is from the demo module
989 if data[0] == "<original>" or data[0] == "<modified>": # FIXME: make more generalised
990 self.list.SetItemData(x, int(data[1])) # Store line number for easy access
991 # Give it a blue colour
992 item = self.list.GetItem(x)
993 item.SetTextColour(wx.BLUE)
994 self.list.SetItem(item)
995 else:
996 self.list.SetItemData(x, -1) # Editor can't jump into this one's code
997
998
999 def OnItemSelected(self, event):
1000 # This occurs before OnDoubleClick and can be used to set the
1001 # currentItem. OnDoubleClick doesn't get a wxListEvent....
1002 self.currentItem = event.m_itemIndex
1003 event.Skip()
1004
1005
1006 def OnDoubleClick(self, event):
1007 # If double-clicking on a demo's entry, jump to the line number
1008 line = self.list.GetItemData(self.currentItem)
1009 if line != -1:
1010 self.nb.SetSelection(1) # Switch to the code viewer tab
1011 wx.CallAfter(self.codePanel.JumpToLine, line-1, True)
1012 event.Skip()
1013
6c5ae2d2 1014
76bfdc78
RD
1015#---------------------------------------------------------------------------
1016
1cc18e15
RD
1017class DemoTaskBarIcon(wx.TaskBarIcon):
1018 TBMENU_RESTORE = wx.NewId()
1019 TBMENU_CLOSE = wx.NewId()
1020 TBMENU_CHANGE = wx.NewId()
1021 TBMENU_REMOVE = wx.NewId()
1022
1023 def __init__(self, frame):
1024 wx.TaskBarIcon.__init__(self)
1025 self.frame = frame
1026
1027 # Set the image
8596dc0b 1028 icon = self.MakeIcon(images.getWXPdemoImage())
1cc18e15 1029 self.SetIcon(icon, "wxPython Demo")
8596dc0b 1030 self.imgidx = 1
1cc18e15
RD
1031
1032 # bind some events
1033 self.Bind(wx.EVT_TASKBAR_LEFT_DCLICK, self.OnTaskBarActivate)
1034 self.Bind(wx.EVT_MENU, self.OnTaskBarActivate, id=self.TBMENU_RESTORE)
1035 self.Bind(wx.EVT_MENU, self.OnTaskBarClose, id=self.TBMENU_CLOSE)
1036 self.Bind(wx.EVT_MENU, self.OnTaskBarChange, id=self.TBMENU_CHANGE)
1037 self.Bind(wx.EVT_MENU, self.OnTaskBarRemove, id=self.TBMENU_REMOVE)
1038
1039
1040 def CreatePopupMenu(self):
1041 """
1042 This method is called by the base class when it needs to popup
1043 the menu for the default EVT_RIGHT_DOWN event. Just create
1044 the menu how you want it and return it from this function,
1045 the base class takes care of the rest.
1046 """
1047 menu = wx.Menu()
1048 menu.Append(self.TBMENU_RESTORE, "Restore wxPython Demo")
1049 menu.Append(self.TBMENU_CLOSE, "Close wxPython Demo")
1050 menu.AppendSeparator()
1051 menu.Append(self.TBMENU_CHANGE, "Change the TB Icon")
1052 menu.Append(self.TBMENU_REMOVE, "Remove the TB Icon")
1053 return menu
1054
1055
1056 def MakeIcon(self, img):
1057 """
1058 The various platforms have different requirements for the
1059 icon size...
1060 """
1061 if "wxMSW" in wx.PlatformInfo:
8596dc0b 1062 img = img.Scale(16, 16)
1cc18e15 1063 elif "wxGTK" in wx.PlatformInfo:
8596dc0b
RD
1064 img = img.Scale(22, 22)
1065 # wxMac can be any size upto 128x128, so leave the source img alone....
1cc18e15
RD
1066 icon = wx.IconFromBitmap(img.ConvertToBitmap() )
1067 return icon
1068
1069
1070 def OnTaskBarActivate(self, evt):
1071 if self.frame.IsIconized():
1072 self.frame.Iconize(False)
1073 if not self.frame.IsShown():
1074 self.frame.Show(True)
1075 self.frame.Raise()
1076
1077
1078 def OnTaskBarClose(self, evt):
1079 self.frame.Close()
1080
1cc18e15
RD
1081
1082 def OnTaskBarChange(self, evt):
6c75a4cf 1083 names = [ "WXPdemo", "Mondrian", "Pencil", "Carrot" ]
8596dc0b
RD
1084 name = names[self.imgidx]
1085
1086 getFunc = getattr(images, "get%sImage" % name)
1087 self.imgidx += 1
1088 if self.imgidx >= len(names):
1089 self.imgidx = 0
1090
1091 icon = self.MakeIcon(getFunc())
1092 self.SetIcon(icon, "This is a new icon: " + name)
1cc18e15
RD
1093
1094
1095 def OnTaskBarRemove(self, evt):
1096 self.RemoveIcon()
1097
1098
1099#---------------------------------------------------------------------------
1fded56b 1100class wxPythonDemo(wx.Frame):
e9159fe8 1101 overviewText = "wxPython Overview"
c368d904 1102
08ecc920 1103 def __init__(self, parent, title):
8ee202f3 1104 wx.Frame.__init__(self, parent, -1, title, size = (950, 720),
08ecc920 1105 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
2f90df85 1106
f290d1c7
RD
1107 self.SetMinSize((640,480))
1108
08ecc920 1109 self.loaded = False
694759cf 1110 self.cwd = os.getcwd()
3ca6a5f0 1111 self.curOverview = ""
08ecc920
RD
1112 self.demoPage = None
1113 self.codePage = None
887a1bd9 1114 self.shell = None
a6780fa2 1115 self.firstTime = True
df5204e0 1116 self.finddlg = None
694759cf 1117
8596dc0b 1118 icon = images.getWXPdemoIcon()
96bfd053 1119 self.SetIcon(icon)
c368d904 1120
1cc18e15 1121 self.tbicon = DemoTaskBarIcon(self)
c368d904 1122
1fded56b 1123 wx.CallAfter(self.ShowTip)
cf694132
RD
1124
1125 self.otherWin = None
80b27b4e
RD
1126 self.Bind(wx.EVT_IDLE, self.OnIdle)
1127 self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
1128 self.Bind(wx.EVT_ICONIZE, self.OnIconfiy)
1129 self.Bind(wx.EVT_MAXIMIZE, self.OnMaximize)
cf694132 1130
1fded56b
RD
1131 self.Centre(wx.BOTH)
1132 self.CreateStatusBar(1, wx.ST_SIZEGRIP)
5a7823f5 1133
0c8f2860
RD
1134 splitter = wx.SplitterWindow(self, -1, style=wx.CLIP_CHILDREN | wx.SP_LIVE_UPDATE | wx.SP_3D)
1135 splitter2 = wx.SplitterWindow(splitter, -1, style=wx.CLIP_CHILDREN | wx.SP_LIVE_UPDATE | wx.SP_3D)
5a7823f5 1136
d56cebe7 1137 def EmptyHandler(evt): pass
80b27b4e
RD
1138 #splitter.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
1139 #splitter2.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
cf694132 1140
1e4a197e
RD
1141 # Prevent TreeCtrl from displaying all items after destruction when True
1142 self.dying = False
cf694132 1143
08ecc920
RD
1144 # Create a Notebook
1145 self.nb = wx.Notebook(splitter2, -1, style=wx.CLIP_CHILDREN)
1146
cf694132 1147 # Make a File menu
1fded56b
RD
1148 self.mainmenu = wx.MenuBar()
1149 menu = wx.Menu()
330af869
RD
1150 item = menu.Append(-1, '&Redirect Output',
1151 'Redirect print statements to a window',
1152 wx.ITEM_CHECK)
1153 self.Bind(wx.EVT_MENU, self.OnToggleRedirect, item)
08ecc920 1154
c8000995
RD
1155 item = menu.Append(-1, 'E&xit\tAlt-X', 'Get the heck outta here!')
1156 self.Bind(wx.EVT_MENU, self.OnFileExit, item)
05871e13 1157 wx.App.SetMacExitMenuItemId(item.GetId())
cf694132
RD
1158 self.mainmenu.Append(menu, '&File')
1159
ec3e670f 1160 # Make a Demo menu
1fded56b 1161 menu = wx.Menu()
ec3e670f 1162 for item in _treeList:
1fded56b 1163 submenu = wx.Menu()
ec3e670f 1164 for childItem in item[1]:
c8000995
RD
1165 mi = submenu.Append(-1, childItem)
1166 self.Bind(wx.EVT_MENU, self.OnDemoMenu, mi)
1fded56b 1167 menu.AppendMenu(wx.NewId(), item[0], submenu)
ec3e670f
RD
1168 self.mainmenu.Append(menu, '&Demo')
1169
08ecc920
RD
1170 # Make a Demo Code menu
1171 #TODO: Add new menu items
1172 # Like the option-enabled entries to select the
1173 # active module
1174 #TODO: should we bother?
1175
1176 #menu = wx.Menu()
1177 #saveID = wx.NewId()
1178 #restoreID = wx.NewId()
1179 #
1180 #menu.Append(saveID, '&Save\tCtrl-S', 'Save edited demo')
1181 #menu.Append(restoreID, '&Delete Modified\tCtrl-R', 'Delete modified copy')
1182 #self.Bind(wx.EVT_MENU, self.codePage.OnSave, id=saveID)
1183 #self.Bind(wx.EVT_MENU, self.codePage.OnRestore, id=restoreID)
1184 #self.mainmenu.Append(menu, 'Demo &Code')
1185 #
ec3e670f 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()
c8000995 1196 helpItem = menu.Append(-1, '&About\tCtrl-H', '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()
1e4a197e
RD
1212
1213 if 0:
1214 # This is another way to set Accelerators, in addition to
1215 # using the '\t<key>' syntax in the menu items.
1fded56b
RD
1216 aTable = wx.AcceleratorTable([(wx.ACCEL_ALT, ord('X'), exitID),
1217 (wx.ACCEL_CTRL, ord('H'), helpID),
1218 (wx.ACCEL_CTRL, ord('F'), findID),
1219 (wx.ACCEL_NORMAL, WXK_F3, findnextID)
1220 ])
1e4a197e 1221 self.SetAcceleratorTable(aTable)
2f90df85 1222
bb0054cd 1223
cf694132 1224 # Create a TreeCtrl
1fded56b 1225 tID = wx.NewId()
f6bcfd97 1226 self.treeMap = {}
3628e088
RD
1227 self.tree = wx.TreeCtrl(splitter, tID, style =
1228 wx.TR_DEFAULT_STYLE #| wx.TR_HAS_VARIABLE_ROW_HEIGHT
eb0f373c 1229 )
afb810d9 1230
e9159fe8 1231 root = self.tree.AddRoot("wxPython Overview")
f6bcfd97
BP
1232 firstChild = None
1233 for item in _treeList:
1234 child = self.tree.AppendItem(root, item[0])
1235 if not firstChild: firstChild = child
1236 for childItem in item[1]:
1237 theDemo = self.tree.AppendItem(child, childItem)
1238 self.treeMap[childItem] = theDemo
1239
1240 self.tree.Expand(root)
1241 self.tree.Expand(firstChild)
80b27b4e
RD
1242 self.tree.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded, id=tID)
1243 self.tree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed, id=tID)
1244 self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=tID)
1245 self.tree.Bind(wx.EVT_LEFT_DOWN, self.OnTreeLeftDown)
cf694132 1246
1fded56b 1247 # Set up a wx.html.HtmlWindow on the Overview Notebook page
0bdca46d
RD
1248 # we put it in a panel first because there seems to be a
1249 # refresh bug of some sort (wxGTK) when it is directly in
1250 # the notebook...
1251 if 0: # the old way
1fded56b 1252 self.ovr = wx.html.HtmlWindow(self.nb, -1, size=(400, 400))
0bdca46d
RD
1253 self.nb.AddPage(self.ovr, self.overviewText)
1254
1fded56b
RD
1255 else: # hopefully I can remove this hacky code soon, see SF bug #216861
1256 panel = wx.Panel(self.nb, -1, style=wx.CLIP_CHILDREN)
1257 self.ovr = wx.html.HtmlWindow(panel, -1, size=(400, 400))
0bdca46d
RD
1258 self.nb.AddPage(panel, self.overviewText)
1259
1260 def OnOvrSize(evt, ovr=self.ovr):
1261 ovr.SetSize(evt.GetSize())
80b27b4e
RD
1262 panel.Bind(wx.EVT_SIZE, OnOvrSize)
1263 panel.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
0bdca46d 1264
08ecc920 1265 if "gtk2" in wx.PlatformInfo:
385721a8 1266 self.ovr.SetStandardFonts()
08ecc920 1267 self.SetOverview(self.overviewText, mainOverview)
c368d904 1268
cf694132 1269
08ecc920
RD
1270 # Set up a log window
1271 self.log = wx.TextCtrl(splitter2, -1,
1272 style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
1273
1274 # Set the wxWindows log target to be this textctrl
1275 #wx.Log_SetActiveTarget(wx.LogTextCtrl(self.log))
cf694132 1276
08ecc920
RD
1277 # But instead of the above we want to show how to use our own wx.Log class
1278 wx.Log_SetActiveTarget(MyLog(self.log))
1279
1280 # for serious debugging
1281 #wx.Log_SetActiveTarget(wx.LogStderr())
1282 #wx.Log_SetTraceMask(wx.TraceMessages)
cf694132
RD
1283
1284
e44b5196
RD
1285 self.Bind(wx.EVT_ACTIVATE, self.OnActivate)
1286 wx.GetApp().Bind(wx.EVT_ACTIVATE_APP, self.OnAppActivate)
1287
f6bcfd97 1288 # add the windows to the splitter and split it.
08ecc920
RD
1289 splitter2.SplitHorizontally(self.nb, self.log, -160)
1290 splitter.SplitVertically(self.tree, splitter2, 200)
afb810d9 1291
f290d1c7
RD
1292 splitter.SetMinimumPaneSize(120)
1293 splitter2.SetMinimumPaneSize(60)
afb810d9 1294
11926a78 1295 # Make the splitter on the right expand the top window when resized
2f0f3b0f
RD
1296 def SplitterOnSize(evt):
1297 splitter = evt.GetEventObject()
1298 sz = splitter.GetSize()
08ecc920 1299 splitter.SetSashPosition(sz.height - 160, False)
2f0f3b0f 1300 evt.Skip()
80b27b4e
RD
1301
1302 splitter2.Bind(wx.EVT_SIZE, SplitterOnSize)
2f0f3b0f 1303
bb0054cd
RD
1304 # select initial items
1305 self.nb.SetSelection(0)
f6bcfd97 1306 self.tree.SelectItem(root)
ec3e670f 1307
08ecc920
RD
1308 # Load 'Main' module
1309 self.LoadDemo(self.overviewText)
1310 self.loaded = True
1311
1312 # select some other initial module?
1313 if len(sys.argv) > 1:
1314 arg = sys.argv[1]
1315 if arg.endswith('.py'):
1316 arg = arg[:-3]
1317 selectedDemo = self.treeMap.get(arg, None)
f6bcfd97 1318 if selectedDemo:
ec3e670f
RD
1319 self.tree.SelectItem(selectedDemo)
1320 self.tree.EnsureVisible(selectedDemo)
1321
bb0054cd 1322
cf694132
RD
1323 #---------------------------------------------
1324 def WriteText(self, text):
f6bcfd97
BP
1325 if text[-1:] == '\n':
1326 text = text[:-1]
1fded56b 1327 wx.LogMessage(text)
f6bcfd97 1328
cf694132
RD
1329 def write(self, txt):
1330 self.WriteText(txt)
1331
1332 #---------------------------------------------
1333 def OnItemExpanded(self, event):
1334 item = event.GetItem()
1fded56b 1335 wx.LogMessage("OnItemExpanded: %s" % self.tree.GetItemText(item))
c368d904 1336 event.Skip()
cf694132
RD
1337
1338 #---------------------------------------------
1339 def OnItemCollapsed(self, event):
1340 item = event.GetItem()
1fded56b 1341 wx.LogMessage("OnItemCollapsed: %s" % self.tree.GetItemText(item))
c368d904 1342 event.Skip()
f6bcfd97
BP
1343
1344 #---------------------------------------------
1345 def OnTreeLeftDown(self, event):
08ecc920 1346 # reset the overview text if the tree item is clicked on again
f6bcfd97
BP
1347 pt = event.GetPosition();
1348 item, flags = self.tree.HitTest(pt)
1349 if item == self.tree.GetSelection():
d975da9b 1350 self.SetOverview(self.tree.GetItemText(item)+" Overview", self.curOverview)
185d7c3e 1351 event.Skip()
cf694132
RD
1352
1353 #---------------------------------------------
1354 def OnSelChanged(self, event):
08ecc920 1355 if self.dying or not self.loaded:
cf694132
RD
1356 return
1357
5a7823f5
RD
1358 item = event.GetItem()
1359 itemText = self.tree.GetItemText(item)
08ecc920 1360 self.LoadDemo(itemText)
5a7823f5
RD
1361
1362 #---------------------------------------------
08ecc920
RD
1363 def LoadDemo(self, demoName):
1364 try:
1365 wx.BeginBusyCursor()
1366
1367 os.chdir(self.cwd)
1368 self.ShutdownDemoModule()
1369
1370 if demoName == self.overviewText:
1371 # User selected the "wxPython Overview" node
1372 # ie: _this_ module
1373 # Changing the main window at runtime not yet supported...
1374 self.demoModules = DemoModules(__name__)
1375 self.SetOverview(self.overviewText, mainOverview)
1376 self.LoadDemoSource()
1377 self.UpdateNotebook(0)
1378 else:
1379 if os.path.exists(GetOriginalFilename(demoName)):
1380 wx.LogMessage("Loading demo %s.py..." % demoName)
1381 self.demoModules = DemoModules(demoName)
a6780fa2 1382 self.LoadDemoSource()
08ecc920
RD
1383 self.tree.Refresh()
1384 else:
1385 self.SetOverview("wxPython", mainOverview)
1386 self.codePage = None
1387 self.UpdateNotebook(0)
1388 finally:
1389 wx.EndBusyCursor()
cf694132 1390
08ecc920
RD
1391 #---------------------------------------------
1392 def LoadDemoSource(self):
1393 self.codePage = None
1394 self.codePage = DemoCodePanel(self.nb, self)
1395 self.codePage.LoadDemo(self.demoModules)
1396
1397 #---------------------------------------------
a6780fa2 1398 def RunModule(self):
08ecc920 1399 """Runs the active module"""
cf694132 1400
08ecc920
RD
1401 module = self.demoModules.GetActive()
1402 self.ShutdownDemoModule()
1403 overviewText = ""
1404
8412feb0
RD
1405 # o The RunTest() for all samples must now return a window that can
1406 # be palced in a tab in the main notebook.
d6922577 1407 # o If an error occurs (or has occurred before) an error tab is created.
08ecc920
RD
1408
1409 if module is not None:
1410 wx.LogMessage("Running demo module...")
1411 if hasattr(module, "overview"):
1412 overviewText = module.overview
cf694132 1413
08ecc920
RD
1414 try:
1415 self.demoPage = module.runTest(self, self.nb, self)
08ecc920 1416 except:
317a64e5 1417 self.demoPage = DemoErrorPanel(self.nb, self.codePage,
7d0f8766
RD
1418 DemoError(sys.exc_info()), self)
1419
1420 assert self.demoPage is not None, "runTest must return a window!"
1421
08ecc920
RD
1422 else:
1423 # There was a previous error in compiling or exec-ing
317a64e5
RD
1424 self.demoPage = DemoErrorPanel(self.nb, self.codePage,
1425 self.demoModules.GetErrorInfo(), self)
a6780fa2 1426
08ecc920 1427 self.SetOverview(self.demoModules.name + " Overview", overviewText)
a6780fa2
RD
1428
1429 if self.firstTime:
1430 # cahnge to the demo page the first time a module is run
1431 self.UpdateNotebook(2)
1432 self.firstTime = False
1433 else:
1434 # otherwise just stay on the same tab in case the user has changed to another one
1435 self.UpdateNotebook()
cf694132
RD
1436
1437 #---------------------------------------------
08ecc920
RD
1438 def ShutdownDemoModule(self):
1439 if self.demoPage:
1440 # inform the window that it's time to quit if it cares
1441 if hasattr(self.demoPage, "ShutdownDemo"):
1442 self.demoPage.ShutdownDemo()
1443 wx.YieldIfNeeded() # in case the page has pending events
1444 self.demoPage = None
1445
1446 #---------------------------------------------
1447 def UpdateNotebook(self, select = -1):
1448 nb = self.nb
1449 debug = False
1450
1451 def UpdatePage(page, pageText):
1452 pageExists = False
1453 pagePos = -1
1454 for i in range(nb.GetPageCount()):
1455 if nb.GetPageText(i) == pageText:
1456 pageExists = True
1457 pagePos = i
1458 break
1459
1460 if page:
1461 if not pageExists:
1462 # Add a new page
08ecc920
RD
1463 nb.AddPage(page, pageText)
1464 if debug: wx.LogMessage("DBG: ADDED %s" % pageText)
1465 else:
08ecc920
RD
1466 if nb.GetPage(pagePos) != page:
1467 # Reload an existing page
1468 nb.Freeze()
08ecc920
RD
1469 nb.DeletePage(pagePos)
1470 nb.InsertPage(pagePos, page, pageText)
1471 nb.Thaw()
1472 if debug: wx.LogMessage("DBG: RELOADED %s" % pageText)
1473 else:
1474 # Excellent! No redraw/flicker
1475 if debug: wx.LogMessage("DBG: SAVED from reloading %s" % pageText)
1476 elif pageExists:
1477 # Delete a page
1478 nb.DeletePage(pagePos)
1479 if debug: wx.LogMessage("DBG: DELETED %s" % pageText)
1480 else:
1481 if debug: wx.LogMessage("DBG: STILL GONE - %s" % pageText)
1482
1483 if select == -1:
1484 select = nb.GetSelection()
cf694132 1485
08ecc920
RD
1486 UpdatePage(self.codePage, "Demo Code")
1487 UpdatePage(self.demoPage, "Demo")
cf694132 1488
887a1bd9 1489 if select >= 0 and select < nb.GetPageCount():
08ecc920
RD
1490 nb.SetSelection(select)
1491
cf694132
RD
1492 #---------------------------------------------
1493 def SetOverview(self, name, text):
f6bcfd97
BP
1494 self.curOverview = text
1495 lead = text[:6]
1496 if lead != '<html>' and lead != '<HTML>':
1e4a197e 1497 text = '<br>'.join(text.split('\n'))
6c7d1792
RD
1498 if wx.USE_UNICODE:
1499 text = text.decode('iso8859_1')
f6bcfd97 1500 self.ovr.SetPage(text)
cf694132 1501 self.nb.SetPageText(0, name)
cf694132
RD
1502
1503 #---------------------------------------------
1504 # Menu methods
c368d904 1505 def OnFileExit(self, *event):
cf694132
RD
1506 self.Close()
1507
330af869
RD
1508 def OnToggleRedirect(self, event):
1509 app = wx.GetApp()
1510 if event.Checked():
1511 app.RedirectStdio()
1512 print "Print statements and other standard output will now be directed to this window."
1513 else:
1514 app.RestoreStdio()
1515 print "Print statements and other standard output will now be sent to the usual location."
08ecc920 1516
cf694132 1517 def OnHelpAbout(self, event):
e166644c 1518 from About import MyAboutBox
ec3e670f 1519 about = MyAboutBox(self)
cf694132
RD
1520 about.ShowModal()
1521 about.Destroy()
1522
1e4a197e 1523 def OnHelpFind(self, event):
df5204e0
RD
1524 if self.finddlg != None:
1525 return
1526
1e4a197e 1527 self.nb.SetSelection(1)
1fded56b
RD
1528 self.finddlg = wx.FindReplaceDialog(self, self.finddata, "Find",
1529 wx.FR_NOUPDOWN |
1530 wx.FR_NOMATCHCASE |
1531 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()
1544 start = editor.GetSelection()[1]
1e4a197e
RD
1545 findstring = self.finddata.GetFindString().lower()
1546 loc = textstring.find(findstring, start)
1547 if loc == -1 and start != 0:
1548 # string not found, start at beginning
1549 start = 0
1550 loc = textstring.find(findstring, start)
1551 if loc == -1:
1fded56b 1552 dlg = wx.MessageDialog(self, 'Find String Not Found',
1e4a197e 1553 'Find String Not Found in Demo File',
1fded56b 1554 wx.OK | wx.ICON_INFORMATION)
1e4a197e
RD
1555 dlg.ShowModal()
1556 dlg.Destroy()
1557 if self.finddlg:
1558 if loc == -1:
1559 self.finddlg.SetFocus()
1560 return
1561 else:
1562 self.finddlg.Destroy()
df5204e0 1563 self.finddlg = None
08ecc920
RD
1564 editor.ShowPosition(loc)
1565 editor.SetSelection(loc, loc + len(findstring))
1e4a197e
RD
1566
1567
1568
1569 def OnFindNext(self, event):
1570 if self.finddata.GetFindString():
1571 self.OnFind(event)
1572 else:
1573 self.OnHelpFind(event)
1574
1575 def OnFindClose(self, event):
1576 event.GetDialog().Destroy()
df5204e0 1577 self.finddlg = None
1e4a197e 1578
cf694132 1579
887a1bd9
RD
1580 def OnOpenShellWindow(self, evt):
1581 if self.shell:
1582 # if it already exists then just make sure it's visible
1583 s = self.shell
1584 if s.IsIconized():
1585 s.Iconize(False)
1586 s.Raise()
1587 else:
1588 # Make a PyShell window
1589 from wx import py
1590 namespace = { 'wx' : wx,
1591 'app' : wx.GetApp(),
1592 'frame' : self,
1593 }
1594 self.shell = py.shell.ShellFrame(None, locals=namespace)
1595 self.shell.SetSize((640,480))
1596 self.shell.Show()
1597
1598 # Hook the close event of the main frame window so that we
1599 # close the shell at the same time if it still exists
1600 def CloseShell(evt):
1601 if self.shell:
1602 self.shell.Close()
1603 evt.Skip()
1604 self.Bind(wx.EVT_CLOSE, CloseShell)
1605
1606
cf694132
RD
1607 #---------------------------------------------
1608 def OnCloseWindow(self, event):
1e4a197e 1609 self.dying = True
08ecc920
RD
1610 self.demoPage = None
1611 self.codePage = None
26197023 1612 self.mainmenu = None
1cc18e15 1613 self.tbicon.Destroy()
cf694132
RD
1614 self.Destroy()
1615
c368d904 1616
cf694132
RD
1617 #---------------------------------------------
1618 def OnIdle(self, event):
1619 if self.otherWin:
1620 self.otherWin.Raise()
08ecc920 1621 self.demoPage = self.otherWin
cf694132
RD
1622 self.otherWin = None
1623
ccf691a4
RD
1624
1625 #---------------------------------------------
1626 def ShowTip(self):
1627 try:
1628 showTipText = open(opj("data/showTips")).read()
1629 showTip, index = eval(showTipText)
1630 except IOError:
1631 showTip, index = (1, 0)
1632 if showTip:
1fded56b 1633 tp = wx.CreateFileTipProvider(opj("data/tips.txt"), index)
861a0196 1634 ##tp = MyTP(0)
1fded56b 1635 showTip = wx.ShowTip(self, tp)
ccf691a4
RD
1636 index = tp.GetCurrentTip()
1637 open(opj("data/showTips"), "w").write(str( (showTip, index) ))
1638
1639
ec3e670f
RD
1640 #---------------------------------------------
1641 def OnDemoMenu(self, event):
f6bcfd97
BP
1642 try:
1643 selectedDemo = self.treeMap[self.mainmenu.GetLabel(event.GetId())]
1644 except:
1645 selectedDemo = None
1646 if selectedDemo:
1647 self.tree.SelectItem(selectedDemo)
1648 self.tree.EnsureVisible(selectedDemo)
ec3e670f 1649
c368d904 1650
c368d904 1651
f3d9dc1d
RD
1652 #---------------------------------------------
1653 def OnIconfiy(self, evt):
8412feb0 1654 wx.LogMessage("OnIconfiy: %s" % evt.Iconized())
f3d9dc1d
RD
1655 evt.Skip()
1656
1657 #---------------------------------------------
1658 def OnMaximize(self, evt):
1fded56b 1659 wx.LogMessage("OnMaximize")
f3d9dc1d
RD
1660 evt.Skip()
1661
8412feb0
RD
1662 #---------------------------------------------
1663 def OnActivate(self, evt):
1664 wx.LogMessage("OnActivate: %s" % evt.GetActive())
1665 evt.Skip()
f3d9dc1d 1666
8412feb0
RD
1667 #---------------------------------------------
1668 def OnAppActivate(self, evt):
1669 wx.LogMessage("OnAppActivate: %s" % evt.GetActive())
1670 evt.Skip()
f3d9dc1d 1671
cf694132
RD
1672#---------------------------------------------------------------------------
1673#---------------------------------------------------------------------------
1674
1fded56b 1675class MySplashScreen(wx.SplashScreen):
b5a5d647 1676 def __init__(self):
6c75a4cf 1677 bmp = wx.Image(opj("bitmaps/splash.png")).ConvertToBitmap()
1fded56b 1678 wx.SplashScreen.__init__(self, bmp,
a253aa20 1679 wx.SPLASH_CENTRE_ON_SCREEN | wx.SPLASH_TIMEOUT,
6c75a4cf 1680 5000, None, -1)
80b27b4e 1681 self.Bind(wx.EVT_CLOSE, self.OnClose)
6f696172
RD
1682 self.fc = wx.FutureCall(2000, self.ShowMain)
1683
b5a5d647
RD
1684
1685 def OnClose(self, evt):
6f696172
RD
1686 # Make sure the default handler runs too so this window gets
1687 # destroyed
1688 evt.Skip()
8eca4fef 1689 self.Hide()
6f696172
RD
1690
1691 # if the timer is still running then go ahead and show the
1692 # main frame now
1693 if self.fc.IsRunning():
1694 self.fc.Stop()
1695 self.ShowMain()
1696
cf694132 1697
6c75a4cf
RD
1698 def ShowMain(self):
1699 frame = wxPythonDemo(None, "wxPython: (A Demonstration)")
1700 frame.Show()
6f696172
RD
1701 if self.fc.IsRunning():
1702 self.Raise()
6c75a4cf 1703
b5a5d647 1704
1fded56b 1705class MyApp(wx.App):
b5a5d647
RD
1706 def OnInit(self):
1707 """
68320e40 1708 Create and show the splash screen. It will then create and show
b5a5d647
RD
1709 the main frame when it is time to do so.
1710 """
1e4a197e 1711
7b85d630
RD
1712 wx.SystemOptions.SetOptionInt("mac.window-plain-transition", 1)
1713
35ee3288
RD
1714 # For debugging
1715 #self.SetAssertMode(wx.PYAPP_ASSERT_DIALOG)
1716
a253aa20
RD
1717 # Normally when using a SplashScreen you would create it, show
1718 # it and then continue on with the applicaiton's
1719 # initialization, finally creating and showing the main
1720 # application window(s). In this case we have nothing else to
1721 # do so we'll delay showing the main frame until later (see
6c75a4cf 1722 # ShowMain above) so the users can see the SplashScreen effect.
b5a5d647
RD
1723 splash = MySplashScreen()
1724 splash.Show()
a253aa20 1725
1e4a197e 1726 return True
b5a5d647
RD
1727
1728
1729
cf694132
RD
1730#---------------------------------------------------------------------------
1731
1732def main():
e02c03a4 1733 try:
d56cebe7 1734 demoPath = os.path.dirname(__file__)
e02c03a4
RD
1735 os.chdir(demoPath)
1736 except:
1737 pass
03a604a6 1738 app = MyApp(False)
cf694132
RD
1739 app.MainLoop()
1740
cf694132
RD
1741#---------------------------------------------------------------------------
1742
1743
08ecc920 1744mainOverview = """<html><body>
1fded56b
RD
1745<h2>wxPython</h2>
1746
f6063c19
RD
1747<p> wxPython is a <b>GUI toolkit</b> for the Python programming
1748language. It allows Python programmers to create programs with a
1749robust, highly functional graphical user interface, simply and easily.
1750It is implemented as a Python extension module (native code) that
1751wraps the popular wxWindows cross platform GUI library, which is
1752written in C++.
1fded56b
RD
1753
1754<p> Like Python and wxWindows, wxPython is <b>Open Source</b> which
1755means that it is free for anyone to use and the source code is
1756available for anyone to look at and modify. Or anyone can contribute
8b9a4190 1757fixes or enhancements to the project.
1fded56b
RD
1758
1759<p> wxPython is a <b>cross-platform</b> toolkit. This means that the
1760same program will run on multiple platforms without modification.
1761Currently supported platforms are 32-bit Microsoft Windows, most Unix
1762or unix-like systems, and Macintosh OS X. Since the language is
1763Python, wxPython programs are <b>simple, easy</b> to write and easy to
1764understand.
1765
1766<p> <b>This demo</b> is not only a collection of test cases for
1767wxPython, but is also designed to help you learn about and how to use
1768wxPython. Each sample is listed in the tree control on the left.
1769When a sample is selected in the tree then a module is loaded and run
1770(usually in a tab of this notebook,) and the source code of the module
1771is loaded in another tab for you to browse and learn from.
1772
1773"""
cf694132
RD
1774
1775
1776#----------------------------------------------------------------------------
1777#----------------------------------------------------------------------------
1778
1779if __name__ == '__main__':
08ecc920 1780 __name__ = 'Main'
cf694132
RD
1781 main()
1782
1783#----------------------------------------------------------------------------
1784
1785
1786
1787
1788
1789
1790