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