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