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