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