ginput: blocking call for mouse input

Sleep not found, despite half the night trying.

What follows three experiments I tried, but now I'm out of ideas. The
last thing to do would be to follow your suggestion and move the busy
loop from ginput to CanvasBase and let the individual backends override
with a better solution if available.

  - Paul

Attempted solutions for modal wx event processing:

1. Run a private event loop with a timer every 0.1s to see if the user
has done selecting. This works, but it doesn't like it if the window
is closed before completion. I didn't test for it, but super-fast
clicking should let the user sometimes select an extra point, so not
a great solution.

    def event_loop(self, done=lambda:False, timeout=0):
        'run the eventloop until done or timeout'
        print "Running private event loop"
        loop = wx.EventLoop()
        
        # stop after timeout period
        def donewaiting(*args,**kw):
            print "Done waiting"
            loop.Exit()
        if timeout > 0:
            print "Starting timer"
            outtimer = wx.Timer(self, id=MODAL_TIMEOUT_TIMER)
            outtimer.Start(timeout*1000, oneShot=True)
            self.Bind(wx.EVT_TIMER, donewaiting, outtimer)

        # check if done every 0.1 s
        def checkdone(*args,**kw):
            if done(): loop.Exit()
        steptimer = wx.Timer(self, id=MODAL_STEP_TIMER)
        steptimer.Start(100, oneShot=False)
        self.Bind(wx.EVT_TIMER, checkdone, steptimer)
    
        loop.Run()
        steptimer.Stop()
        return

2. What seems like it should work is to use an event handler which
checks after processing each event whether or not this completes
the condition and so we can exit the loop. However, ProcessEvent
never seems to be called. Reading elsewhere I see that ProcessEvent
is not a virtual function in wx, so presumably we can't override
it in a subclass of wx.EvtHandler.

        # Redirect input events to new handler
        class ModalHandler(wx.EvtHandler):
            def ProcessEvent(self, evt):
                print "Processing event"
                if done(): loop.Exit()
                return False # True if processed
        print "Pushing handler"
        handler = ModalHandler()
        self.PushEventHandler(handler)
        print "enabled",handler.GetEvtHandlerEnabled()
        print "handler->next",handler.GetNextHandler()
        print "self->previous",self.GetPreviousHandler()
        loop.Run()
        self.PopEventHandler(False)
        print "Done!"

3. Revising two somewhat, I send all events for the canvas through
my own event handler. To make sure I see each one I bind each window
event to my own dispatcher as well as to the underlying window. I
then use this handler rather than self in the canvas __init__.

This fails because either the window handler is not called or it
is called twice, depending on whether PushEventHandler and ProcessEvent
are called. It also fails if the window is closed unexpectedly.

class ModalHandler(wx.EvtHandler):
    def __init__(self, window):
        print "Creating handler"
        wx.EvtHandler.__init__(self)
        self.done = lambda:False
        window.PushEventHandler(self)
        self.window = window
        self.timer = wx.Timer()
        self.Bind(wx.EVT_TIMER, self.OnTimeout, self.timer)
        self.loop = wx.EventLoop()
    def _dispatch(self, evt):
        """Ick! ProcessEvent is not virtual, so we can't override directly!"""
        #print "Processing event"
        self.window.ProcessEvent(evt)
        if self.done(): self.loop.Exit()
        print "Returning event"
        return True
    def EndModal(self):
        """Force the loop to exit"""
        self.done = lambda:True
    def Bind(self,evt,action,*args,**kw):
        # Force all events through ProcessEvent. This is the first binding.
        # ProcessEvent will dispatch the event to the window itself, so be
        # sure to tell the window what to do with the event. This is the
        # second binding.
        if wx.VERSION_STRING >= '2.5': # Event handlers 2.5
            print "Binding 2.5"
            wx.EvtHandler.Bind(self.window,evt,action,*args,**kw)
            wx.EvtHandler.Bind(self,evt,self._dispatch)
        else:
            print "Binding 2.4"
            evt(self,self.ProcessEvent)
            evt(self.window,*args,**kw)
    def OnTimeout(self):
        print "timeout"
        self.loop.Exit()
    def RunLoop(self, done=lambda:False, timeout=0):
        print "Running loop"
        self.done = done
        if timeout > 0: self.timer.Start(timeout, oneShot=True)
        try:
            self.loop.Run()
        finally:
            self.timer.Stop()
            self.done = lambda:False

···

On Wed, Feb 06, 2008 at 01:21:30AM +0100, Gael Varoquaux wrote:

On Tue, Feb 05, 2008 at 07:16:59PM -0500, Paul Kienzle wrote:
> I'll look around some more and see if I can find the sleep until next
> event function in wx.

Yeah, sleep, I need more of that. Or maybe you can find wx.coffee ?