ginput: blocking call for mouse input

There are two ways to do this in wx.

One is to use eventloop.Dispatch() which waits until the next available
event:

    def _onTimeout(self,evt):
        'event loop timer completed'
        self._event_loop_running = False
        
    def event_loop(self, done=lambda:False, timeout=0):
        'run the event loop until done or timeout'
        
        timer = wx.Timer(self)
        self._event_loop_running = True
        if timeout > 0:
            timer.Start(timeout*1000, oneShot=True)
            self.Bind(wx.EVT_TIMER, self._onTimeout, timer)
        loop = wx.EventLoop()
        while self._event_loop_running and loop.Dispatch() and not done():
            #print "Processing event"
            pass

        self._event_loop_running = False
        timer.Stop()

The other is to trigger a loop.Exit() from the click callback.

Rather than passing a stop condition to the event_loop method, the
user would call start_event_loop to start collecting events,
and stop_event_loop in the event callback when the terminating
condition is reached. In the ginput example, this is just calling
self.fig.canvas.stop_event_loop() rather than self.done = True in the
callback. The code for this is:

    def __init__
        ...
        self._event_loop = wx.EventLoop()
        ...

    def start_event_loop(self, timeout=0):
        timer = wx.Timer(self)
        if timeout > 0:
            timer.Start(timeout*1000, oneShot=True)
            self.Bind(wx.EVT_TIMER, self.stop_event_loop, timer)
        self._event_loop.Run()
        timer.Stop()

    def stop_event_loop(self, event=None):
        if self._event_loop.IsRunning():
            self._event_loop.Exit()

Note that we need to also check for the window closing while the
event loop is running, which we can do using the following at the
beginning of start_event_loop:

        root = self.GetTopLevelParent()
        root.Bind(wx.EVT_CLOSE, self.stop_event_loop)

This will unfortunately kill any window kill event that the
application defined.

We may also want to give the application direct access to the timer
for the purpose of creating animations independent of the toolkit.

  - Paul

···

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:
> How about giving flush_events() an until=condition and timeout=n keywords
> so that flush_events becomes:

> if timeout > 0: set timer event which triggers out_of_time
> while get next event:
> process event
> if out_of_time or until(): break

I'd say this is exactly the way to do it. The problem is that under GTK
is seems fairly easy to do, under Tk it seems feasible, but under Qt and
under Wx I have no clue how to do this. We can always fallback to
busy-waiting for these toolkits.

Very nice. I am impressed. We need to find a way of doing the same in
other toolkits. Way can however change the function call flush_event to
give it an "until" callback (and maybe rename it to
"wait_interactively"), and move the busy waiting code in the
backend-dependant part of the code. That way we can remove it backend
after backend.

I can contribute a bit to this refactoring, but give me a couple of weeks
before.

Cheers,

Ga�l

···

On Thu, Feb 07, 2008 at 11:31:11AM -0500, Paul Kienzle wrote:

There are two ways to do this in wx.

One is to use eventloop.Dispatch() which waits until the next available
event:

  [...]

The other is to trigger a loop.Exit() from the click callback.

  [...]