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