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