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