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