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