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