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