]> git.saurik.com Git - wxWidgets.git/blame - wxPython/demo/Main.py
give focus to show top level windows
[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
76bfdc78 14import sys, os, time
cf694132 15from wxPython.wx import *
9fb56e0c 16from wxPython.lib.splashscreen import SplashScreen
f6bcfd97 17from wxPython.html import wxHtmlWindow
cf694132 18
96bfd053
RD
19import images
20
cf694132
RD
21#---------------------------------------------------------------------------
22
23
24_treeList = [
4f3449b4 25 ('New since last release', ['ContextHelp',
c7e7022c 26 'PyCrust',
fea018f8 27 'PyCrustWithFilling',
6d19860f
RD
28 'VirtualListCtrl',
29 'wxListCtrl',
53fe40ba 30 'TablePrint',
f6bcfd97 31 ]),
e395c057 32
6d19860f 33 ('Windows', ['wxFrame', 'wxDialog', 'wxMiniFrame',
6d19860f
RD
34 'wxGrid', 'wxSashWindow',
35 'wxScrolledWindow', 'wxSplitterWindow',
36 'wxStatusBar', 'wxNotebook',
37 'wxHtmlWindow',
38 'wxStyledTextCtrl_1', 'wxStyledTextCtrl_2',]),
cf694132
RD
39
40 ('Common Dialogs', ['wxColourDialog', 'wxDirDialog', 'wxFileDialog',
41 'wxSingleChoiceDialog', 'wxTextEntryDialog',
42 'wxFontDialog', 'wxPageSetupDialog', 'wxPrintDialog',
bb0054cd 43 'wxMessageDialog', 'wxProgressDialog']),
cf694132
RD
44
45 ('Controls', ['wxButton', 'wxCheckBox', 'wxCheckListBox', 'wxChoice',
46 'wxComboBox', 'wxGauge', 'wxListBox', 'wxListCtrl', 'wxTextCtrl',
f6bcfd97
BP
47 'wxTreeCtrl', 'wxSpinButton', 'wxSpinCtrl', 'wxStaticText',
48 'wxStaticBitmap', 'wxRadioBox', 'wxSlider', 'wxToolBar',
d1679124 49 'wxCalendarCtrl', 'wxToggleButton',
6999b0d8 50 ]),
cf694132 51
4f3449b4 52 ('Window Layout', ['wxLayoutConstraints', 'LayoutAnchors', 'Sizers', 'XML_Resource']),
cf694132 53
b1462dfa
RD
54 ('Miscellaneous', [ 'DragAndDrop', 'CustomDragAndDrop', 'FontEnumerator',
55 'wxTimer', 'wxValidator', 'wxGLCanvas', 'DialogUnits',
9b3d3bc4 56 'wxImage', 'wxMask', 'PrintFramework', 'wxOGL',
f6bcfd97
BP
57 'PythonEvents', 'Threads',
58 'ActiveXWrapper_Acrobat', 'ActiveXWrapper_IE',
493f1553 59 'wxDragImage', "wxProcess", "FancyText", "OOR", "wxWave",
ac346f50 60 'wxJoystick',
f6bcfd97 61 ]),
cf694132 62
e0473f5f 63 ('wxPython Library', ['Layoutf', 'wxScrolledMessageDialog',
f0261a72 64 'wxMultipleChoiceDialog', 'wxPlotCanvas', 'wxFloatBar',
c7e7022c 65 'wxCalendar', 'wxMVCTree', 'wxVTKRenderWindow',
c368d904 66 'FileBrowseButton', 'GenericButtons', 'wxEditor',
c7e7022c 67 'ColourSelect', 'ImageBrowser',
53fe40ba 68 'infoframe', 'ColourDB', 'PyCrust', 'TablePrint',
c368d904 69 ]),
cf694132 70
8bf5d46e 71 ('Cool Contribs', ['pyTree', 'hangman', 'SlashDot', 'XMLtreeview']),
cf694132
RD
72
73 ]
74
75#---------------------------------------------------------------------------
76
76bfdc78
RD
77class MyLog(wxPyLog):
78 def __init__(self, textCtrl, logTime=0):
79 wxPyLog.__init__(self)
80 self.tc = textCtrl
81 self.logTime = logTime
82
83 def DoLogString(self, message, timeStamp):
84 if self.logTime:
85 message = time.strftime("%X", time.localtime(timeStamp)) + \
86 ": " + message
87 self.tc.AppendText(message + '\n')
88
89
90#---------------------------------------------------------------------------
91
cf694132 92class wxPythonDemo(wxFrame):
c368d904 93
cf694132 94 def __init__(self, parent, id, title):
f6bcfd97
BP
95 wxFrame.__init__(self, parent, -1, title, size = (800, 600),
96 style=wxDEFAULT_FRAME_STYLE|wxNO_FULL_REPAINT_ON_RESIZE)
2f90df85 97
694759cf 98 self.cwd = os.getcwd()
3ca6a5f0 99 self.curOverview = ""
694759cf 100
b6962846
RD
101 if 1:
102 icon = wxIconFromXPMData(images.getMondrianData())
103 else:
104 # another way to do it
105 bmp = images.getMondrianBitmap()
106 icon = wxEmptyIcon()
107 icon.CopyFromBitmap(bmp)
108
96bfd053 109 self.SetIcon(icon)
c368d904 110
96bfd053 111 if wxPlatform == '__WXMSW__':
c368d904
RD
112 # setup a taskbar icon, and catch some events from it
113 self.tbicon = wxTaskBarIcon()
114 self.tbicon.SetIcon(icon, "wxPython Demo")
115 EVT_TASKBAR_LEFT_DCLICK(self.tbicon, self.OnTaskBarActivate)
116 EVT_TASKBAR_RIGHT_UP(self.tbicon, self.OnTaskBarMenu)
117 EVT_MENU(self.tbicon, self.TBMENU_RESTORE, self.OnTaskBarActivate)
118 EVT_MENU(self.tbicon, self.TBMENU_CLOSE, self.OnTaskBarClose)
119
cf694132
RD
120
121 self.otherWin = None
122 EVT_IDLE(self, self.OnIdle)
f6bcfd97 123 EVT_CLOSE(self, self.OnCloseWindow)
f3d9dc1d
RD
124 EVT_ICONIZE(self, self.OnIconfiy)
125 EVT_MAXIMIZE(self, self.OnMaximize)
cf694132
RD
126
127 self.Centre(wxBOTH)
128 self.CreateStatusBar(1, wxST_SIZEGRIP)
5a7823f5 129
f6bcfd97
BP
130 splitter = wxSplitterWindow(self, -1, style=wxNO_3D|wxSP_3D)
131 splitter2 = wxSplitterWindow(splitter, -1, style=wxNO_3D|wxSP_3D)
5a7823f5 132
d56cebe7
RD
133 def EmptyHandler(evt): pass
134 EVT_ERASE_BACKGROUND(splitter, EmptyHandler)
135 EVT_ERASE_BACKGROUND(splitter2, EmptyHandler)
cf694132 136
b1cfebd9 137 # Prevent TreeCtrl from displaying all items after destruction when true
cf694132
RD
138 self.dying = false
139
140 # Make a File menu
141 self.mainmenu = wxMenuBar()
142 menu = wxMenu()
2f90df85 143 exitID = wxNewId()
f0261a72 144 menu.Append(exitID, 'E&xit\tAlt-X', 'Get the heck outta here!')
2f90df85 145 EVT_MENU(self, exitID, self.OnFileExit)
cf694132
RD
146 self.mainmenu.Append(menu, '&File')
147
ec3e670f
RD
148 # Make a Demo menu
149 menu = wxMenu()
150 for item in _treeList:
151 submenu = wxMenu()
152 for childItem in item[1]:
153 mID = wxNewId()
154 submenu.Append(mID, childItem)
155 EVT_MENU(self, mID, self.OnDemoMenu)
156 menu.AppendMenu(wxNewId(), item[0], submenu)
157 self.mainmenu.Append(menu, '&Demo')
158
159
cf694132 160 # Make a Help menu
2f90df85 161 helpID = wxNewId()
cf694132 162 menu = wxMenu()
2f90df85
RD
163 menu.Append(helpID, '&About\tCtrl-H', 'wxPython RULES!!!')
164 EVT_MENU(self, helpID, self.OnHelpAbout)
cf694132
RD
165 self.mainmenu.Append(menu, '&Help')
166 self.SetMenuBar(self.mainmenu)
167
2f90df85 168 # set the menu accellerator table...
f0261a72 169 aTable = wxAcceleratorTable([(wxACCEL_ALT, ord('X'), exitID),
2f90df85
RD
170 (wxACCEL_CTRL, ord('H'), helpID)])
171 self.SetAcceleratorTable(aTable)
172
bb0054cd 173
cf694132 174 # Create a TreeCtrl
f6bcfd97
BP
175 tID = wxNewId()
176 self.treeMap = {}
177 self.tree = wxTreeCtrl(splitter, tID,
178 style=wxTR_HAS_BUTTONS |
179 wxTR_EDIT_LABELS |
180 wxTR_HAS_VARIABLE_ROW_HEIGHT |
181 wxSUNKEN_BORDER)
182 #self.tree.SetBackgroundColour(wxNamedColour("Pink"))
183 root = self.tree.AddRoot("Overview")
184 firstChild = None
185 for item in _treeList:
186 child = self.tree.AppendItem(root, item[0])
187 if not firstChild: firstChild = child
188 for childItem in item[1]:
189 theDemo = self.tree.AppendItem(child, childItem)
190 self.treeMap[childItem] = theDemo
191
192 self.tree.Expand(root)
193 self.tree.Expand(firstChild)
194 EVT_TREE_ITEM_EXPANDED (self.tree, tID, self.OnItemExpanded)
195 EVT_TREE_ITEM_COLLAPSED (self.tree, tID, self.OnItemCollapsed)
196 EVT_TREE_SEL_CHANGED (self.tree, tID, self.OnSelChanged)
197 EVT_LEFT_DOWN (self.tree, self.OnTreeLeftDown)
cf694132 198
cf694132 199 # Create a Notebook
d56cebe7 200 self.nb = wxNotebook(splitter2, -1, style=wxCLIP_CHILDREN)
cf694132 201
c368d904
RD
202 # Set up a wxHtmlWindow on the Overview Notebook page
203 # we put it in a panel first because there seems to be a
204 # refresh bug of some sort (wxGTK) when it is directly in
205 # the notebook...
206 if 0: # the old way
207 self.ovr = wxHtmlWindow(self.nb, -1, size=(400, 400))
208 self.nb.AddPage(self.ovr, "Overview")
209
e87269a7 210 else: # hopefully I can remove this hacky code soon, see bug #216861
d56cebe7 211 panel = wxPanel(self.nb, -1, style=wxCLIP_CHILDREN)
c368d904
RD
212 self.ovr = wxHtmlWindow(panel, -1, size=(400, 400))
213 self.nb.AddPage(panel, "Overview")
214
215 def OnOvrSize(evt, ovr=self.ovr):
216 ovr.SetSize(evt.GetSize())
217
218 EVT_SIZE(panel, OnOvrSize)
d56cebe7
RD
219 EVT_ERASE_BACKGROUND(panel, EmptyHandler)
220
c368d904
RD
221
222 self.SetOverview("Overview", overview)
cf694132
RD
223
224
225 # Set up a TextCtrl on the Demo Code Notebook page
efc5f224
RD
226 self.txt = wxTextCtrl(self.nb, -1,
227 style = wxTE_MULTILINE|wxTE_READONLY|wxHSCROLL)
cf694132
RD
228 self.nb.AddPage(self.txt, "Demo Code")
229
230
cf694132 231 # Set up a log on the View Log Notebook page
f6bcfd97 232 self.log = wxTextCtrl(splitter2, -1,
efc5f224 233 style = wxTE_MULTILINE|wxTE_READONLY|wxHSCROLL)
f6bcfd97 234 # Set the wxWindows log target to be this textctrl
76bfdc78
RD
235 #wxLog_SetActiveTarget(wxLogTextCtrl(self.log))
236 wxLog_SetActiveTarget(MyLog(self.log))
cf694132
RD
237
238
5a7823f5 239
f6bcfd97 240 self.Show(true)
5a7823f5 241
f6bcfd97
BP
242 # add the windows to the splitter and split it.
243 splitter2.SplitHorizontally(self.nb, self.log)
244 splitter2.SetSashPosition(450, true)
245 splitter2.SetMinimumPaneSize(20)
cf694132 246
f6bcfd97
BP
247 splitter.SplitVertically(self.tree, splitter2)
248 splitter.SetSashPosition(180, true)
249 splitter.SetMinimumPaneSize(20)
cf694132 250
cf694132 251
bb0054cd
RD
252 # select initial items
253 self.nb.SetSelection(0)
f6bcfd97 254 self.tree.SelectItem(root)
ec3e670f
RD
255
256 if len(sys.argv) == 2:
257 try:
258 selectedDemo = self.treeMap[sys.argv[1]]
259 except:
260 selectedDemo = None
f6bcfd97 261 if selectedDemo:
ec3e670f
RD
262 self.tree.SelectItem(selectedDemo)
263 self.tree.EnsureVisible(selectedDemo)
264
bb0054cd 265
f6bcfd97 266 wxLogMessage('window handle: %s' % self.GetHandle())
2abc0a0f
RD
267
268
cf694132
RD
269 #---------------------------------------------
270 def WriteText(self, text):
f6bcfd97
BP
271 if text[-1:] == '\n':
272 text = text[:-1]
273 wxLogMessage(text)
274
cf694132
RD
275
276 def write(self, txt):
277 self.WriteText(txt)
278
279 #---------------------------------------------
280 def OnItemExpanded(self, event):
281 item = event.GetItem()
f6bcfd97 282 wxLogMessage("OnItemExpanded: %s" % self.tree.GetItemText(item))
c368d904 283 event.Skip()
cf694132
RD
284
285 #---------------------------------------------
286 def OnItemCollapsed(self, event):
287 item = event.GetItem()
f6bcfd97 288 wxLogMessage("OnItemCollapsed: %s" % self.tree.GetItemText(item))
c368d904 289 event.Skip()
f6bcfd97
BP
290
291 #---------------------------------------------
292 def OnTreeLeftDown(self, event):
293 pt = event.GetPosition();
294 item, flags = self.tree.HitTest(pt)
295 if item == self.tree.GetSelection():
296 self.SetOverview(self.tree.GetItemText(item), self.curOverview)
185d7c3e 297 event.Skip()
cf694132
RD
298
299 #---------------------------------------------
300 def OnSelChanged(self, event):
301 if self.dying:
302 return
303
5a7823f5
RD
304 item = event.GetItem()
305 itemText = self.tree.GetItemText(item)
306 self.RunDemo(itemText)
307
308
309 #---------------------------------------------
310 def RunDemo(self, itemText):
694759cf 311 os.chdir(self.cwd)
cf694132
RD
312 if self.nb.GetPageCount() == 3:
313 if self.nb.GetSelection() == 2:
314 self.nb.SetSelection(0)
315 self.nb.DeletePage(2)
316
cf694132
RD
317 if itemText == 'Overview':
318 self.GetDemoFile('Main.py')
319 self.SetOverview('Overview', overview)
cf694132 320 self.nb.Refresh();
e91a9dfc 321 self.window = None
cf694132
RD
322
323 else:
324 if os.path.exists(itemText + '.py'):
9d8bd15f 325 wxBeginBusyCursor()
f6bcfd97
BP
326 wxLogMessage("Running demo %s.py..." % itemText)
327 try:
328 self.GetDemoFile(itemText + '.py')
329 module = __import__(itemText, globals())
330 self.SetOverview(itemText, module.overview)
331 finally:
332 wxEndBusyCursor()
cf694132
RD
333
334 # in case runTest is modal, make sure things look right...
335 self.nb.Refresh();
336 wxYield()
337
f6bcfd97 338 self.window = module.runTest(self, self.nb, self) ###
e91a9dfc
RD
339 if self.window:
340 self.nb.AddPage(self.window, 'Demo')
dcd38683 341 wxYield()
cf694132 342 self.nb.SetSelection(2)
cf694132
RD
343
344 else:
f6bcfd97 345 self.ovr.SetPage("")
cf694132 346 self.txt.Clear()
e91a9dfc 347 self.window = None
cf694132 348
2f90df85 349
cf694132
RD
350
351 #---------------------------------------------
352 # Get the Demo files
353 def GetDemoFile(self, filename):
354 self.txt.Clear()
bb0054cd
RD
355 try:
356 self.txt.SetValue(open(filename).read())
8bf5d46e 357 except IOError:
cf694132
RD
358 self.txt.WriteText("Cannot open %s file." % filename)
359
360 self.txt.SetInsertionPoint(0)
361 self.txt.ShowPosition(0)
362
363 #---------------------------------------------
364 def SetOverview(self, name, text):
f6bcfd97
BP
365 self.curOverview = text
366 lead = text[:6]
367 if lead != '<html>' and lead != '<HTML>':
368 text = string.join(string.split(text, '\n'), '<br>')
369 #text = '<font size="-1"><pre>' + text + '</pre></font>'
370 self.ovr.SetPage(text)
cf694132 371 self.nb.SetPageText(0, name)
cf694132
RD
372
373 #---------------------------------------------
374 # Menu methods
c368d904 375 def OnFileExit(self, *event):
cf694132
RD
376 self.Close()
377
378
379 def OnHelpAbout(self, event):
e166644c 380 from About import MyAboutBox
ec3e670f 381 about = MyAboutBox(self)
cf694132
RD
382 about.ShowModal()
383 about.Destroy()
384
385
386 #---------------------------------------------
387 def OnCloseWindow(self, event):
388 self.dying = true
e91a9dfc 389 self.window = None
26197023 390 self.mainmenu = None
c368d904
RD
391 if hasattr(self, "tbicon"):
392 del self.tbicon
cf694132
RD
393 self.Destroy()
394
c368d904 395
cf694132
RD
396 #---------------------------------------------
397 def OnIdle(self, event):
398 if self.otherWin:
399 self.otherWin.Raise()
e91a9dfc 400 self.window = self.otherWin
cf694132
RD
401 self.otherWin = None
402
ec3e670f
RD
403 #---------------------------------------------
404 def OnDemoMenu(self, event):
f6bcfd97
BP
405 try:
406 selectedDemo = self.treeMap[self.mainmenu.GetLabel(event.GetId())]
407 except:
408 selectedDemo = None
409 if selectedDemo:
410 self.tree.SelectItem(selectedDemo)
411 self.tree.EnsureVisible(selectedDemo)
ec3e670f 412
c368d904
RD
413
414 #---------------------------------------------
415 def OnTaskBarActivate(self, evt):
416 if self.IsIconized():
417 self.Iconize(false)
418 if not self.IsShown():
419 self.Show(true)
420 self.Raise()
421
422 #---------------------------------------------
423
424 TBMENU_RESTORE = 1000
425 TBMENU_CLOSE = 1001
426
427 def OnTaskBarMenu(self, evt):
428 menu = wxMenu()
429 menu.Append(self.TBMENU_RESTORE, "Restore wxPython Demo")
430 menu.Append(self.TBMENU_CLOSE, "Close")
431 self.tbicon.PopupMenu(menu)
432 menu.Destroy()
433
434 #---------------------------------------------
435 def OnTaskBarClose(self, evt):
436 self.Close()
437
438 # because of the way wxTaskBarIcon.PopupMenu is implemented we have to
439 # prod the main idle handler a bit to get the window to actually close
440 wxGetApp().ProcessIdle()
441
442
f3d9dc1d
RD
443 #---------------------------------------------
444 def OnIconfiy(self, evt):
445 wxLogMessage("OnIconfiy")
446 evt.Skip()
447
448 #---------------------------------------------
449 def OnMaximize(self, evt):
450 wxLogMessage("OnMaximize")
451 evt.Skip()
452
453
454
455
cf694132
RD
456#---------------------------------------------------------------------------
457#---------------------------------------------------------------------------
458
459class MyApp(wxApp):
460 def OnInit(self):
f6bcfd97 461 wxInitAllImageHandlers()
9fb56e0c
RD
462
463 self.splash = SplashScreen(None, bitmapfile='bitmaps/splash.gif',
464 duration=4000, callback=self.AfterSplash)
465 self.splash.Show(true)
466 wxYield()
467 return true
468
f6bcfd97 469
9fb56e0c
RD
470 def AfterSplash(self):
471 self.splash.Close(true)
472 frame = wxPythonDemo(None, -1, "wxPython: (A Demonstration)")
cf694132
RD
473 frame.Show(true)
474 self.SetTopWindow(frame)
f6bcfd97
BP
475 self.ShowTip(frame)
476
477
478 def ShowTip(self, frame):
479 try:
480 showTipText = open("data/showTips").read()
481 showTip, index = eval(showTipText)
482 except IOError:
483 showTip, index = (1, 0)
c368d904 484 #print showTip, index
f6bcfd97
BP
485 if showTip:
486 tp = wxCreateFileTipProvider("data/tips.txt", index)
487 showTip = wxShowTip(frame, tp)
488 index = tp.GetCurrentTip()
489 open("data/showTips", "w").write(str( (showTip, index) ))
490
cf694132
RD
491
492#---------------------------------------------------------------------------
493
494def main():
e02c03a4 495 try:
d56cebe7 496 demoPath = os.path.dirname(__file__)
e02c03a4
RD
497 os.chdir(demoPath)
498 except:
499 pass
cf694132
RD
500 app = MyApp(0)
501 app.MainLoop()
502
503
504#---------------------------------------------------------------------------
505
506
507
f6bcfd97
BP
508overview = """<html><body>
509 <h2>Python</h2>
510
511 Python is an interpreted, interactive, object-oriented programming
512 language often compared to Tcl, Perl, Scheme, or Java.
513
514 <p> Python combines remarkable power with very clear syntax. It has
515 modules, classes, exceptions, very high level dynamic data types, and
516 dynamic typing. There are interfaces to many system calls and
517 libraries, and new built-in modules are easily written in C or
518 C++. Python is also usable as an extension language for applications
519 that need a programmable interface. <p>
520
521 <h2>wxWindows</h2>
522
523 wxWindows is a free C++ framework designed to make cross-platform
524 programming child's play. Well, almost. wxWindows 2 supports Windows
525 3.1/95/98/NT, Unix with GTK/Motif/Lesstif, with a Mac version
526 underway. Other ports are under consideration. <p>
527
528 wxWindows is a set of libraries that allows C++ applications to
529 compile and run on several different types of computers, with minimal
530 source code changes. There is one library per supported GUI (such as
531 Motif, or Windows). As well as providing a common API (Application
532 Programming Interface) for GUI functionality, it provides
533 functionality for accessing some commonly-used operating system
534 facilities, such as copying or deleting files. wxWindows is a
535 'framework' in the sense that it provides a lot of built-in
536 functionality, which the application can use or replace as required,
537 thus saving a great deal of coding effort. Basic data structures such
538 as strings, linked lists and hash tables are also supported.
539
540 <p>
541 <h2>wxPython</h2>
542
543 wxPython is a Python extension module that encapsulates the wxWindows
544 GUI classes. Currently it is only available for the Win32 and GTK
545 ports of wxWindows, but as soon as the other ports are brought up to
546 the same level as Win32 and GTK, it should be fairly trivial to
547 enable wxPython to be used with the new GUI.
548
549 <p>
550
551 The wxPython extension module attempts to mirror the class heiarchy
552 of wxWindows as closely as possible. This means that there is a
553 wxFrame class in wxPython that looks, smells, tastes and acts almost
554 the same as the wxFrame class in the C++ version. Unfortunately,
555 because of differences in the languages, wxPython doesn't match
556 wxWindows exactly, but the differences should be easy to absorb
557 because they are natural to Python. For example, some methods that
558 return multiple values via argument pointers in C++ will return a
559 tuple of values in Python.
560
561 <p>
562
563 There is still much to be done for wxPython, many classes still need
564 to be mirrored. Also, wxWindows is still somewhat of a moving target
565 so it is a bit of an effort just keeping wxPython up to date. On the
566 other hand, there are enough of the core classes completed that
567 useful applications can be written.
568
569 <p>
570
571 wxPython is close enough to the C++ version that the majority of
572 the wxPython documentation is actually just notes attached to the C++
573 documents that describe the places where wxPython is different. There
574 is also a series of sample programs included, and a series of
575 documentation pages that assist the programmer in getting started
576 with wxPython.
577
578 """
cf694132
RD
579
580
581#----------------------------------------------------------------------------
582#----------------------------------------------------------------------------
583
584if __name__ == '__main__':
585 main()
586
587#----------------------------------------------------------------------------
588
589
590
591
592
593
594