diff --git a/etg/app.py b/etg/app.py index 1e052c2a..0d77cd09 100644 --- a/etg/app.py +++ b/etg/app.py @@ -63,9 +63,7 @@ def run(): c.find('OnExceptionInMainLoop').ignore() c.find('OnFatalException').ignore() c.find('OnUnhandledException').ignore() - - c.find('ExitMainLoop').isVirtual = False - + # Release the GIL for potentially blocking or long-running functions c.find('MainLoop').releaseGIL() c.find('ProcessPendingEvents').releaseGIL() diff --git a/etg/evtloop.py b/etg/evtloop.py index 13d59cf4..d14736f3 100644 --- a/etg/evtloop.py +++ b/etg/evtloop.py @@ -18,7 +18,8 @@ DOCSTRING = "" # The classes and/or the basename of the Doxygen XML files to be processed by # this script. ITEMS = [ 'wxEventLoopBase', - 'wxEventLoopActivator' + 'wxEventLoopActivator', + 'wxGUIEventLoop', ] #--------------------------------------------------------------------------- @@ -30,7 +31,7 @@ def run(): # myYield used by other tests... ) etgtools.parseDoxyXML(module, ITEMS) - + #----------------------------------------------------------------- # Tweak the parsed meta objects in the module object as needed for # customizing the generated code and docstrings. @@ -41,12 +42,45 @@ def run(): c.find('Yield').releaseGIL() c.find('YieldFor').releaseGIL() + c.find('OnExit').ignore(False) c = module.find('wxEventLoopActivator') c.addPrivateAssignOp() c.addPrivateCopyCtor() + + # context manager methods + c.addPyMethod('__enter__', '(self)', 'return self') + c.addPyMethod('__exit__', '(self, exc_type, exc_val, exc_tb)', 'return False') + + + c = module.find('wxGUIEventLoop') + c.addPrivateCopyCtor() + + # Add declaration of the base class pure virtuals so sip knows they have + # implementations here + c.addItem(etgtools.WigCode("""\ + public: + virtual int Run(); + virtual void Exit(int rc = 0); + virtual bool Pending() const; + virtual bool Dispatch(); + virtual int DispatchTimeout(unsigned long timeout); + virtual void WakeUp(); + + virtual bool ProcessIdle(); + """)) + + module.addPyCode("""\ + @wx.deprecated + class EventLoop(GUIEventLoop): + '''Class using the old name for compatibility.''' + def __init__(self): + GUIEventLoop.__init__(self) + """) + + #----------------------------------------------------------------- tools.doCommonTweaks(module) tools.runGenerators(module) diff --git a/samples/mainloop/mainloop.py b/samples/mainloop/mainloop.py new file mode 100755 index 00000000..0b1cfc7f --- /dev/null +++ b/samples/mainloop/mainloop.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python + +""" +This demo attempts to override the C++ MainLoop and implement it +in Python. +""" + +import time +import wx + +##import os; raw_input('PID: %d\nPress enter...' % os.getpid()) + +#--------------------------------------------------------------------------- + +class MyFrame(wx.Frame): + + def __init__(self, parent, id, title): + wx.Frame.__init__(self, parent, id, title, + (100, 100), (160, 150)) + + self.Bind(wx.EVT_SIZE, self.OnSize) + self.Bind(wx.EVT_MOVE, self.OnMove) + self.Bind(wx.EVT_CLOSE, self.OnCloseWindow) + self.Bind(wx.EVT_IDLE, self.OnIdle) + + self.count = 0 + + panel = wx.Panel(self) + sizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=5) + + self.sizeCtrl = wx.TextCtrl(panel, -1, "", style=wx.TE_READONLY) + sizer.Add(wx.StaticText(panel, -1, "Size:")) + sizer.Add(self.sizeCtrl) + + self.posCtrl = wx.TextCtrl(panel, -1, "", style=wx.TE_READONLY) + sizer.Add(wx.StaticText(panel, -1, "Pos:")) + sizer.Add(self.posCtrl) + + self.idleCtrl = wx.TextCtrl(panel, -1, "", style=wx.TE_READONLY) + sizer.Add(wx.StaticText(panel, -1, "Idle:")) + sizer.Add(self.idleCtrl) + + border = wx.BoxSizer() + border.Add(sizer, 0, wx.ALL, 20) + panel.SetSizer(border) + + + def OnCloseWindow(self, event): + print wx.GetApp().GetExitOnFrameDelete() + self.Destroy() + + def OnIdle(self, event): + self.idleCtrl.SetValue(str(self.count)) + self.count = self.count + 1 + + def OnSize(self, event): + size = event.GetSize() + self.sizeCtrl.SetValue("%s, %s" % (size.width, size.height)) + event.Skip() + + def OnMove(self, event): + pos = event.GetPosition() + self.posCtrl.SetValue("%s, %s" % (pos.x, pos.y)) + + + +#--------------------------------------------------------------------------- + +class MyEventLoop(wx.GUIEventLoop): + def __init__(self): + wx.GUIEventLoop.__init__(self) + self.exitCode = 0 + self.shouldExit = False + + + def DoMyStuff(self): + # Do whatever you want to have done for each iteration of the event + # loop. In this example we'll just sleep a bit to simulate something + # real happening. + time.sleep(0.10) + + + def Run(self): + # Set this loop as the active one. It will automatically reset to the + # original evtloop when the context manager exits. + with wx.EventLoopActivator(self): + while True: + + self.DoMyStuff() + + # Generate and process idles events for as long as there + # isn't anything else to do + while not self.shouldExit and not self.Pending() and self.ProcessIdle(): + pass + + if self.shouldExit: + break + + # dispatch all the pending events and call Dispatch() to wait + # for the next message + if not self.ProcessEvents(): + break + + # Currently on wxOSX Pending always returns true, so the + # ProcessIdle above is not ever called. Call it here instead. + if 'wxOSX' in wx.PlatformInfo: + self.ProcessIdle() + + # Proces remaining queued messages, if any + while True: + checkAgain = False + if wx.GetApp() and wx.GetApp().HasPendingEvents(): + wx.GetApp().ProcessPendingEvents() + checkAgain = True + if 'wxOSX' not in wx.PlatformInfo and self.Pending(): + self.Dispatch() + checkAgain = True + if not checkAgain: + break + + return self.exitCode + + + def Exit(self, rc=0): + self.exitCode = rc + self.shouldExit = True + self.OnExit() + self.WakeUp() + + + def ProcessEvents(self): + if wx.GetApp(): + wx.GetApp().ProcessPendingEvents() + + if self.shouldExit: + return False + + return self.Dispatch() + + + + + +class MyApp(wx.App): + + def MainLoop(self): + self.SetExitOnFrameDelete(True) + self.mainLoop = MyEventLoop() + self.mainLoop.Run() + + def ExitMainLoop(self): + self.mainLoop.Exit() + + def OnInit(self): + frame = MyFrame(None, -1, "This is a test") + frame.Show(True) + self.SetTopWindow(frame) + + #self.keepGoing = True + return True + + +app = MyApp(False) +app.MainLoop() + + + + +