SourceForge logo
SourceForge logo
Menu

matplotlib-devel

From: Gael V. <gae...@no...> - 2008年01月30日 02:05:26
Attachments: ginput.diff
Hi all,
A while ago (a year or so), I was looking for a ginput-like function with
matplotlib. For those who don't know what I am talking about, it is a
blocking call that can be used in a script to ask the user to enter
coordinnate by clicking on the figure. This is incredibly handy, as it
allows some simple user interaction without worrying about events loop
and Co.
At the time I gave up on that. I have made progress with GUI and event
loops, and a recent post on the matplotlib-user list put me back to work
on this (
http://www.nabble.com/ginput-is-here%21-%28for-wx-anyway%29-to14960506.html
).
I have a preliminary patch to add this feature to matplotlib that I
attach. I add a method to the backend canvases, to be able to deal with
event-loop-handling, and following Jack Gurkesaft I create my own a small
event-loop to poll for events inside my function. This is a kludge, but it
allows to block the function while still processing events. If you have
better ideas.
I have implemented the functionnality for all the interactive backends I
could test (and thus not cocoa), but I am not too sure my gtk code is
kosher, I wouldn't mind advice on this one.
The code of ginput itself is currently in a seperate file
(matplotlib.ginput, that you can run for a demo) because I wasn't too
sure where to add it.
This is a first version of the patch. I would like comments on it,
especially where to add the code itself, and if the proposed additions to
the backends look all right.
Once I have comments, I plan to:
 * Make right click cancel an input
 * Allow infinite number of inputs, terminated by right-click
 * Add optional display of inputs
 * Rework docstrings
More suggestions welcomed,
This could (and should, IMHO) open the road to other blocking calls for
user interaction, like the selection of a window, as they make life
really easy for people wanting to hack quick data analysis scripts.
Cheers,
Gaël
From: Gael V. <gae...@no...> - 2008年01月30日 02:15:54
Attachments: ginput.diff
Ooops, I had forgotten to add the Wx backend. Here is a new patch.
By the way, with the wx backend, there seems to be a simple mistake in
the "show" method of the figure manager, to reproduce the traceback do
(with a recent ipython):
ipython -wthread -pylab
In [1]: f = figure()
In [2]: f.show()
Cheers,
Gaël
On Wed, Jan 30, 2008 at 03:05:13AM +0100, Gael Varoquaux wrote:
> Hi all,
> A while ago (a year or so), I was looking for a ginput-like function with
> matplotlib. For those who don't know what I am talking about, it is a
> blocking call that can be used in a script to ask the user to enter
> coordinnate by clicking on the figure. This is incredibly handy, as it
> allows some simple user interaction without worrying about events loop
> and Co.
> At the time I gave up on that. I have made progress with GUI and event
> loops, and a recent post on the matplotlib-user list put me back to work
> on this (
> http://www.nabble.com/ginput-is-here%21-%28for-wx-anyway%29-to14960506.html
> ).
> I have a preliminary patch to add this feature to matplotlib that I
> attach. I add a method to the backend canvases, to be able to deal with
> event-loop-handling, and following Jack Gurkesaft I create my own a small
> event-loop to poll for events inside my function. This is a kludge, but it
> allows to block the function while still processing events. If you have
> better ideas.
> I have implemented the functionnality for all the interactive backends I
> could test (and thus not cocoa), but I am not too sure my gtk code is
> kosher, I wouldn't mind advice on this one.
> The code of ginput itself is currently in a seperate file
> (matplotlib.ginput, that you can run for a demo) because I wasn't too
> sure where to add it.
> This is a first version of the patch. I would like comments on it,
> especially where to add the code itself, and if the proposed additions to
> the backends look all right.
> Once I have comments, I plan to:
> * Make right click cancel an input
> * Allow infinite number of inputs, terminated by right-click
> * Add optional display of inputs
> * Rework docstrings
> More suggestions welcomed,
> This could (and should, IMHO) open the road to other blocking calls for
> user interaction, like the selection of a window, as they make life
> really easy for people wanting to hack quick data analysis scripts.
> Cheers,
> Gaël
> Index: trunk/matplotlib/lib/matplotlib/backend_bases.py
> ===================================================================
> --- trunk/matplotlib/lib/matplotlib/backend_bases.py	(revision 4908)
> +++ trunk/matplotlib/lib/matplotlib/backend_bases.py	(working copy)
> @@ -1151,7 +1151,13 @@
> """
> return self.callbacks.disconnect(cid)
> + def flush_events(self):
> + """ Flush the GUI events for the figure. Implemented only for
> + backends with GUIs.
> + """
> + raise NotImplementedError
> +
> class FigureManagerBase:
> """
> Helper class for matlab mode, wraps everything up into a neat bundle
> Index: trunk/matplotlib/lib/matplotlib/ginput.py
> ===================================================================
> --- trunk/matplotlib/lib/matplotlib/ginput.py	(revision 0)
> +++ trunk/matplotlib/lib/matplotlib/ginput.py	(revision 0)
> @@ -0,0 +1,80 @@
> +
> +from matplotlib.pyplot import gcf
> +import time
> +
> +class BlockingMouseInput(object):
> + """ Class that creates a callable object to retrieve mouse click 
> + in a blocking way, a la MatLab. 
> + """
> +
> + callback = None 
> + verbose = False
> +
> + def on_click(self, event):
> + """ Event handler that will be passed to the current figure to
> + retrieve clicks.
> + """
> + # if it's a valid click, append the coordinates to the list
> + if event.inaxes:
> + self.clicks.append((event.xdata, event.ydata))
> + if self.verbose: 
> + print "input %i: %f,%f" % (len(self.clicks), 
> + event.xdata, event.ydata)
> +
> + def __call__(self, fig, n=1, timeout=30, verbose=False):
> + """ Blocking call to retrieve n coordinate pairs through mouse 
> + clicks.
> + """
> +
> + assert isinstance(n, int), "Requires an integer argument"
> +
> + # Ensure that the current figure is shown 
> + fig.show()
> + # connect the click events to the on_click function call
> + self.callback = fig.canvas.mpl_connect('button_press_event', 
> + self.on_click)
> +
> + # initialize the list of click coordinates
> + self.clicks = []
> +
> + self.verbose = verbose
> +
> + # wait for n clicks
> + counter = 0
> + while len(self.clicks) < n:
> + fig.canvas.flush_events()
> + # rest for a moment
> + time.sleep(0.01)
> +
> + # check for a timeout
> + counter += 1
> + if timeout > 0 and counter > timeout/0.01: 
> + print "ginput timeout"; 
> + break;
> +
> + # All done! Disconnect the event and return what we have
> + fig.canvas.mpl_disconnect(self.callback)
> + self.callback = None
> + return self.clicks
> +
> +
> +def ginput(n=1, timeout=30, verbose=False):
> + """
> + Blocking call to interact with the figure. 
> +
> + This will wait for n clicks from the user and return a list of the 
> + coordinates of each click.
> +
> + If timeout is negative, does not timeout.
> + """
> +
> + blocking_mouse_input = BlockingMouseInput()
> + return blocking_mouse_input(gcf(), n, timeout, verbose=verbose)
> +
> +if __name__ == "__main__":
> + import pylab
> + t = pylab.arange(10)
> + pylab.plot(t, pylab.sin(t))
> + print "Please click"
> + ginput(3, verbose=True)
> +
> Index: trunk/matplotlib/lib/matplotlib/backends/backend_qt.py
> ===================================================================
> --- trunk/matplotlib/lib/matplotlib/backends/backend_qt.py	(revision 4908)
> +++ trunk/matplotlib/lib/matplotlib/backends/backend_qt.py	(working copy)
> @@ -175,6 +175,9 @@
> return key
> + def flush_events(self):
> + qt.qApp.processEvents()
> +
> class FigureManagerQT( FigureManagerBase ):
> """
> Public attributes
> Index: trunk/matplotlib/lib/matplotlib/backends/backend_gtk.py
> ===================================================================
> --- trunk/matplotlib/lib/matplotlib/backends/backend_gtk.py	(revision 4908)
> +++ trunk/matplotlib/lib/matplotlib/backends/backend_gtk.py	(working copy)
> @@ -386,6 +386,13 @@
> def get_default_filetype(self):
> return 'png'
> + def flush_events(self):
> + gtk.gdk.threads_enter() 
> + while gtk.events_pending():
> + gtk.main_iteration(True)
> + gtk.gdk.flush()
> + gtk.gdk.threads_leave()
> + 
> class FigureManagerGTK(FigureManagerBase):
> """
> Index: trunk/matplotlib/lib/matplotlib/backends/backend_tkagg.py
> ===================================================================
> --- trunk/matplotlib/lib/matplotlib/backends/backend_tkagg.py	(revision 4908)
> +++ trunk/matplotlib/lib/matplotlib/backends/backend_tkagg.py	(working copy)
> @@ -269,8 +269,9 @@
> key = self._get_key(event)
> FigureCanvasBase.key_release_event(self, key, guiEvent=event)
> + def flush_events(self):
> + self._master.update()
> -
> class FigureManagerTkAgg(FigureManagerBase):
> """
> Public attributes
> Index: trunk/matplotlib/lib/matplotlib/backends/backend_qt4.py
> ===================================================================
> --- trunk/matplotlib/lib/matplotlib/backends/backend_qt4.py	(revision 4908)
> +++ trunk/matplotlib/lib/matplotlib/backends/backend_qt4.py	(working copy)
> @@ -13,7 +13,7 @@
> from matplotlib.mathtext import MathTextParser
> from matplotlib.widgets import SubplotTool
> -from PyQt4 import QtCore, QtGui
> +from PyQt4 import QtCore, QtGui, Qt
> backend_version = "0.9.1"
> def fn_name(): return sys._getframe(1).f_code.co_name
> @@ -174,6 +174,9 @@
> return key
> + def flush_events(self):
> + Qt.qApp.processEvents()
> +
> class FigureManagerQT( FigureManagerBase ):
> """
> Public attributes
> -------------------------------------------------------------------------
> This SF.net email is sponsored by: Microsoft
> Defy all challenges. Microsoft(R) Visual Studio 2008.
> http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
> _______________________________________________
> Matplotlib-devel mailing list
> Mat...@li...
> https://lists.sourceforge.net/lists/listinfo/matplotlib-devel
-- 
 Gael Varoquaux,
 Quantum degenerate gases group
 European Laboratory for Non-Linear Spectroscopy
 University of Florence, Polo Scientifico
 Via Nello Carrara 1, I-50019-Sesto-Fiorentino (Firenze) Italy
 Phone: ++ 390-55-457242145 Fax: ++ 390-55-4572451
 ++ and ++
 Groupe d'optique atomique,
 Laboratoire Charles Fabry de l'Institut d'Optique
 Campus Polytechnique, RD 128, 91127 Palaiseau cedex FRANCE
 Tel : 33 (0) 1 64 53 33 23 - Fax : 33 (0) 1 64 53 31 01
 Labs: 33 (0) 1 64 53 33 63 - 33 (0) 1 64 53 33 62
From: John H. <jd...@gm...> - 2008年01月31日 14:20:40
On Jan 29, 2008 8:15 PM, Gael Varoquaux <gae...@no...> wrote:
> Ooops, I had forgotten to add the Wx backend. Here is a new patch.
>
> By the way, with the wx backend, there seems to be a simple mistake in
> the "show" method of the figure manager, to reproduce the traceback do
> (with a recent ipython):
Hey Gael -- this is really cool. As you know, this has been a much
requested feature, and the hard part is to get something working
across backends, which it looks like you've done.
I suggest a minor reorganization. Get rid of ginput.py altogether,
add the BlockingMouseInput code to either backend_bases.py or
figure.py. Make a figure.Figure.ginput method, so folks can use it
outside of pylab, and then add a ginput method to pyplot.py which is a
thin wrapper to the Figure.ginput method.
If this seems like a good organization to you, I'll wait for a new
patch and then contribute that.
Thanks!
JDH
From: Gael V. <gae...@no...> - 2008年01月31日 15:41:54
On Thu, Jan 31, 2008 at 08:20:37AM -0600, John Hunter wrote:
> As you know, this has been a much requested feature,
I know. I have wanted it pretty badly.
> and the hard part is to get something working across backends, which it
> looks like you've done.
Looks like it works OK. I would appreciate heads up from people who know
GTK, as I am not too sure of possibly garbbling the mainloop with my
kludge. But I have tested it quit extensively and it seems to work.
> I suggest a minor reorganization. Get rid of ginput.py altogether,
> add the BlockingMouseInput code to either backend_bases.py or
> figure.py. Make a figure.Figure.ginput method, so folks can use it
> outside of pylab, and then add a ginput method to pyplot.py which is a
> thin wrapper to the Figure.ginput method.
Yup, this seems like a good solution.
> If this seems like a good organization to you, I'll wait for a new
> patch and then contribute that.
Give me a few days, but it will come.
Cheers,
Gaël
From: Gael V. <gae...@no...> - 2008年02月02日 14:49:00
Attachments: ginput.diff
On Thu, Jan 31, 2008 at 04:41:41PM +0100, Gael Varoquaux wrote:
> > If this seems like a good organization to you, I'll wait for a new
> > patch and then contribute that.
> Give me a few days, but it will come.
Here is the new patch. I added visual feedback when accumulating points.
I hope the docstrings are clear.
Cheers,
Gaël
From: John H. <jd...@gm...> - 2008年02月05日 21:58:15
On Feb 2, 2008 8:48 AM, Gael Varoquaux <gae...@no...> wrote:
> Here is the new patch. I added visual feedback when accumulating points.
> I hope the docstrings are clear.
Great -- thanks again. I applied this patch and created a new example
ginput_demo.py
Tested on GTKAgg and TkAgg on my system, and looks good so far.
JDH
From: Paul K. <pki...@ni...> - 2008年02月05日 23:31:08
On Tue, Feb 05, 2008 at 03:58:08PM -0600, John Hunter wrote:
> On Feb 2, 2008 8:48 AM, Gael Varoquaux <gae...@no...> wrote:
> 
> > Here is the new patch. I added visual feedback when accumulating points.
> > I hope the docstrings are clear.
> 
> Great -- thanks again. I applied this patch and created a new example
> ginput_demo.py
> 
> Tested on GTKAgg and TkAgg on my system, and looks good so far.
This failed for me in wx.
I posted a patch r4943 which moves flush_events from Figure to Canvas,
and removes the Yield in gui_repaint which caused an exception in wx.
The showfig callback in FigureManagerWx had an unreferenced global
variable figwin which I removed. It wasn't obvious to me why this 
was broken, or how long it has been broken, but the ginput example
works for me now.
Setting the timeout to 0.01s for the busy loop in ginput seems excessive.
Since it is waiting for human interaction, generally 0.1 seconds is good
enough.
	- Paul
From: Gael V. <gae...@no...> - 2008年02月05日 23:38:42
On Tue, Feb 05, 2008 at 06:30:53PM -0500, Paul Kienzle wrote:
> This failed for me in wx.
> I posted a patch r4943 which moves flush_events from Figure to Canvas,
> and removes the Yield in gui_repaint which caused an exception in wx.
> The showfig callback in FigureManagerWx had an unreferenced global
> variable figwin which I removed. It wasn't obvious to me why this 
> was broken, or how long it has been broken, but the ginput example
> works for me now.
Yes, I had noticed this problem (did I signal it to the list, or did I
forget ?) I wasn't too sure what to do about it, as I don't really know
the MPL codebase. As a result I tested the principle under Wx in an older
version of MPL, which did not have this problem, but also did not have
Yield in gui_repaint. But you sorted out this problem, thanks a lot!
> Setting the timeout to 0.01s for the busy loop in ginput seems excessive.
> Since it is waiting for human interaction, generally 0.1 seconds is good
> enough.
I had gone for that originally, but it looked quite ugly when moving the
window around. This busy waiting is quite ugly, but I have found a way of
avoid it. IMHO it should be done in the toolkit's event loop, but it
seems that trying to do it this would add gobbles of code for little
gain.
Cheers,
Gaël
From: Paul K. <pki...@ni...> - 2008年02月06日 00:17:11
On Wed, Feb 06, 2008 at 12:38:32AM +0100, Gael Varoquaux wrote:
> On Tue, Feb 05, 2008 at 06:30:53PM -0500, Paul Kienzle wrote:
> > Setting the timeout to 0.01s for the busy loop in ginput seems excessive.
> > Since it is waiting for human interaction, generally 0.1 seconds is good
> > enough.
> 
> I had gone for that originally, but it looked quite ugly when moving the
> window around. This busy waiting is quite ugly, but I have found a way of
> avoid it. IMHO it should be done in the toolkit's event loop, but it
> seems that trying to do it this would add gobbles of code for little
> gain.
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
Looking through wx I don't see how to "get next event", my choices being
run all events (MainLoop) or run pending events. I do see an "exit main
loop" function, so for wx at least one could redirect the event handler 
for the window and run all the events, exiting the main loop when done,
but that seems ugly. I'll look around some more and see if I can find
the sleep until next event function in wx.
 	- Paul
From: Gael V. <gae...@no...> - 2008年02月06日 00:21:40
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.
This indeed seems to be the way to go. Maybe I would empty the event
stack when the "until()" condition is met, before returning to the
caller, and thus blocking again.
> 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 ?
Cheers,
Gaël
From: Paul K. <pki...@ni...> - 2008年02月06日 15:04:58
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 ?
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
From: Paul K. <pki...@ni...> - 2008年02月07日 16:31:57
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.
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
From: Gael V. <gae...@no...> - 2008年02月08日 07:15:51
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. 
> [...]
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 
From: Paul K. <pki...@ni...> - 2008年02月08日 14:35:06
On Fri, Feb 08, 2008 at 08:15:20AM +0100, Gael Varoquaux wrote:
> 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. 
> 
> > [...]
> 
> 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.
Having played with both "flush_until" and "start/stop" I have to say I
prefer the start/stop interface. It removes the need for users to share
a 'done' variable between the callback and the caller and the need to 
create a dummy lambda:done function. Also, it is a better match to
the semantics of wx/gtk/qt (see below).
I don't like the names start_event_loop/stop_event_loop, but couldn't 
think of anything better. How about: gui_wait/end_wait?
 - Paul
-----
Proposed generic implementation based on flush_event and sleep, to be
put into CanvasBase:
 def gui_wait(self, timeout=0):
 """Run the event loop."""
 if timeout == 0: timeout = npy.inf
 timestep = 0.01
 counter = 0
 self._waiting = True
 while self._waiting and counter*timestep < timeout:
 self.flush_events()
 time.sleep(timestep)
 counter += 1
 def end_wait(self):
 """Stop the event loop."""
 self._waiting = False
Suggested implementation for backend_gtk (untested!!!):
 def gui_wait(self, timeout=0):
 """Run the gtk event loop."""
 # TODO: call self.end_wait() on window close
 self._waiting = True
 if timeout > 0:
 timer = gtk.gobject.timeout_add(timeout,self._onTimer)
 gtk.main()
 self._waiting = False
 gobject.source_remove(timer)
 def _onTimer(self):
 self.end_wait()
 return False
 def end_wait(self):
 """Stop the gtk event loop."""
 if self._waiting: gtk.main_quit()
From: John H. <jd...@gm...> - 2008年02月05日 22:12:03
On Feb 5, 2008 3:58 PM, John Hunter <jd...@gm...> wrote:
> On Feb 2, 2008 8:48 AM, Gael Varoquaux <gae...@no...> wrote:
>
> > Here is the new patch. I added visual feedback when accumulating points.
> > I hope the docstrings are clear.
>
> Great -- thanks again. I applied this patch and created a new example
> ginput_demo.py
Jack replied to me offlist so I am going to paste in his post below.
Perhaps you and Gael can consult on the ideal functionality of ginput
vis-a-vis optional line segment drawing, etc...
From	Jack Sankey <jac...@gm...>
to	John Hunter <jd...@gm...>,
date	Feb 5, 2008 4:02 PM
subject	Re: [matplotlib-devel] ginput: blocking call for mouse input
mailed-by	gmail.com
	
	
	
Woa, it's working on GTKAgg using wx.Yield()? You must have added some voodoo :)
Also, my version of GaelInput has seemed to stop evolving. This
version has the option to draw lines between clicks, which I use a
lot. Also, the default timeout is 0 now, since you can always
right-click to abort.
-Jack
class GaelInput(object):
 """
 Class that create a callable object to retrieve mouse click in a
 blocking way, a la MatLab. Based on Gael Varoquaux's almost-working
 object. Thanks Gael! I've wanted to get this working for years!
 -Jack
 """
 debug = False
 cid = None # event connection object
 clicks = [] # list of click coordinates
 n = 1 # number of clicks we're waiting for
 lines = False # if we should draw the lines
 def on_click(self, event):
 """
 Event handler that will be passed to the current figure to
 retrieve clicks.
 """
 # write the debug information if we're supposed to
 if self.debug: print "button "+str(event.button)+":
"+str(event.xdata)+", "+str(event.ydata)
 # if this event's a right click we're done
 if event.button == 3:
 self.done = True
 return
 # if it's a valid click (and this isn't an extra event
 # in the queue), append the coordinates to the list
 if event.inaxes and not self.done:
 self.clicks.append([event.xdata, event.ydata])
 # now if we're supposed to draw lines, do so
 if self.lines and len(self.clicks) > 1:
 event.inaxes.plot([self.clicks[-1][0], self.clicks[-2][0]],
 [self.clicks[-1][1], self.clicks[-2][1]],
 color='w', linewidth=2.0,
scalex=False, scaley=False)
 event.inaxes.plot([self.clicks[-1][0], self.clicks[-2][0]],
 [self.clicks[-1][1], self.clicks[-2][1]],
 color='k', linewidth=1.0,
scalex=False, scaley=False)
 _pylab.draw()
 # if we have n data points, we're done
 if len(self.clicks) >= self.n and self.n is not 0:
 self.done = True
 return
 def __call__(self, n=1, timeout=0, debug=False, lines=False):
 """
 Blocking call to retrieve n coordinate pairs through mouse clicks.
 n=1 number of clicks to collect. Set n=0 to keep collecting
 points until you click with the right mouse button.
 timeout=30 maximum number of seconds to wait for clicks
before giving up.
 timeout=0 to disable
 debug=False show each click event coordinates
 lines=False draw lines between clicks
 """
 # just for printing the coordinates
 self.debug = debug
 # for drawing lines
 self.lines = lines
 # connect the click events to the on_click function call
 self.cid = _pylab.connect('button_press_event', self.on_click)
 # initialize the list of click coordinates
 self.clicks = []
 # wait for n clicks
 self.n = n
 self.done = False
 t = 0.0
 while not self.done:
 # key step: yield the processor to other threads
 _wx.Yield();
 _time.sleep(0.1)
 # check for a timeout
 t += 0.1
 if timeout and t > timeout: print "ginput timeout"; break;
 # All done! Disconnect the event and return what we have
 _pylab.disconnect(self.cid)
 self.cid = None
 return _numpy.array(self.clicks)
def ginput(n=1, timeout=0, show=True, lines=False):
 """
 Simple functional call for physicists. This will wait for n clicks
from the user and
 return a list of the coordinates of each click.
 n=1 number of clicks to collect
 timeout=30 maximum number of seconds to wait for clicks
before giving up.
 timeout=0 to disable
 show=True print the clicks as they are received
 lines=False draw lines between clicks
 """
 x = GaelInput()
 return x(n, timeout, show, lines)
From: Jack S. <jac...@gm...> - 2008年02月05日 22:17:57
I'm not at all attached to any particular functionality. Feel free to
mangle it as you see fit!
On Feb 5, 2008 5:11 PM, John Hunter <jd...@gm...> wrote:
> On Feb 5, 2008 3:58 PM, John Hunter <jd...@gm...> wrote:
> > On Feb 2, 2008 8:48 AM, Gael Varoquaux <gae...@no...> wrote:
> >
> > > Here is the new patch. I added visual feedback when accumulating points.
> > > I hope the docstrings are clear.
> >
> > Great -- thanks again. I applied this patch and created a new example
> > ginput_demo.py
>
> Jack replied to me offlist so I am going to paste in his post below.
> Perhaps you and Gael can consult on the ideal functionality of ginput
> vis-a-vis optional line segment drawing, etc...
>
>
>
> From Jack Sankey <jac...@gm...>
> to John Hunter <jd...@gm...>,
> date Feb 5, 2008 4:02 PM
> subject Re: [matplotlib-devel] ginput: blocking call for mouse input
> mailed-by gmail.com
>
>
>
>
> Woa, it's working on GTKAgg using wx.Yield()? You must have added some voodoo :)
>
> Also, my version of GaelInput has seemed to stop evolving. This
> version has the option to draw lines between clicks, which I use a
> lot. Also, the default timeout is 0 now, since you can always
> right-click to abort.
>
> -Jack
>
>
>
> class GaelInput(object):
> """
> Class that create a callable object to retrieve mouse click in a
> blocking way, a la MatLab. Based on Gael Varoquaux's almost-working
> object. Thanks Gael! I've wanted to get this working for years!
>
> -Jack
> """
>
> debug = False
> cid = None # event connection object
> clicks = [] # list of click coordinates
> n = 1 # number of clicks we're waiting for
> lines = False # if we should draw the lines
>
> def on_click(self, event):
> """
> Event handler that will be passed to the current figure to
> retrieve clicks.
> """
>
> # write the debug information if we're supposed to
> if self.debug: print "button "+str(event.button)+":
> "+str(event.xdata)+", "+str(event.ydata)
>
> # if this event's a right click we're done
> if event.button == 3:
> self.done = True
> return
>
> # if it's a valid click (and this isn't an extra event
> # in the queue), append the coordinates to the list
> if event.inaxes and not self.done:
> self.clicks.append([event.xdata, event.ydata])
>
> # now if we're supposed to draw lines, do so
> if self.lines and len(self.clicks) > 1:
> event.inaxes.plot([self.clicks[-1][0], self.clicks[-2][0]],
> [self.clicks[-1][1], self.clicks[-2][1]],
> color='w', linewidth=2.0,
> scalex=False, scaley=False)
> event.inaxes.plot([self.clicks[-1][0], self.clicks[-2][0]],
> [self.clicks[-1][1], self.clicks[-2][1]],
> color='k', linewidth=1.0,
> scalex=False, scaley=False)
> _pylab.draw()
>
> # if we have n data points, we're done
> if len(self.clicks) >= self.n and self.n is not 0:
> self.done = True
> return
>
>
> def __call__(self, n=1, timeout=0, debug=False, lines=False):
> """
> Blocking call to retrieve n coordinate pairs through mouse clicks.
>
> n=1 number of clicks to collect. Set n=0 to keep collecting
> points until you click with the right mouse button.
>
> timeout=30 maximum number of seconds to wait for clicks
> before giving up.
> timeout=0 to disable
>
> debug=False show each click event coordinates
>
> lines=False draw lines between clicks
> """
>
> # just for printing the coordinates
> self.debug = debug
>
> # for drawing lines
> self.lines = lines
>
> # connect the click events to the on_click function call
> self.cid = _pylab.connect('button_press_event', self.on_click)
>
> # initialize the list of click coordinates
> self.clicks = []
>
> # wait for n clicks
> self.n = n
> self.done = False
> t = 0.0
> while not self.done:
> # key step: yield the processor to other threads
> _wx.Yield();
> _time.sleep(0.1)
>
> # check for a timeout
> t += 0.1
> if timeout and t > timeout: print "ginput timeout"; break;
>
> # All done! Disconnect the event and return what we have
> _pylab.disconnect(self.cid)
> self.cid = None
> return _numpy.array(self.clicks)
>
>
>
> def ginput(n=1, timeout=0, show=True, lines=False):
> """
> Simple functional call for physicists. This will wait for n clicks
> from the user and
> return a list of the coordinates of each click.
>
> n=1 number of clicks to collect
> timeout=30 maximum number of seconds to wait for clicks
> before giving up.
> timeout=0 to disable
> show=True print the clicks as they are received
> lines=False draw lines between clicks
> """
>
> x = GaelInput()
> return x(n, timeout, show, lines)
>
From: Gael V. <gae...@no...> - 2008年02月05日 22:23:27
On Tue, Feb 05, 2008 at 04:11:59PM -0600, John Hunter wrote:
> Also, my version of GaelInput has seemed to stop evolving. This
> version has the option to draw lines between clicks, which I use a
> lot. Also, the default timeout is 0 now, since you can always
> right-click to abort.
You can still use this behavoir, using a timeout of zero, n = 0, and the
middle click to end (I use right_click to cancel points). I am not sure
what the default timeout should be. If you have strong opinions about it
being 0, that's fine with me.
As far as n = 0 being the default, I think this is a bad idea. First of
all, it break matlab-compatibility for no good reasons, second in most
cases, IMHO, the naive programmer only wants one point, and puts some
logics afterwards. He will not read the doc, and wont understand why his
function is not returning after one click (so many people don't even know
how to read docstring, I am not kidding).
As for the lines, having lines implies that there is a connection order
in your points, which is not always the case. I suggest thus adding a
optional (oof by default) keyword argument for this behavior.
I am working late hours currently to try to improve the mayavi2 docs
before we do a major release, I can't work on this patchs. Jack, do you
feel like writing it. It should be pretty trivial by simply extending
what I wrote for displaying points, and cleaning them afterward, which is
the hardest part.
Sorry not to propose to do it myself, but I already put myself behind the
release schedule by hacking on this on saturday, and I am not even
talking about my day work.
Cheers,
Gaël
From: Paul K. <pki...@ni...> - 2008年02月05日 23:46:31
On Tue, Feb 05, 2008 at 11:23:14PM +0100, Gael Varoquaux wrote:
> On Tue, Feb 05, 2008 at 04:11:59PM -0600, John Hunter wrote:
> > Also, my version of GaelInput has seemed to stop evolving. This
> > version has the option to draw lines between clicks, which I use a
> > lot. Also, the default timeout is 0 now, since you can always
> > right-click to abort.
> 
> You can still use this behavoir, using a timeout of zero, n = 0, and the
> middle click to end (I use right_click to cancel points). I am not sure
> what the default timeout should be. If you have strong opinions about it
> being 0, that's fine with me.
> 
> As far as n = 0 being the default, I think this is a bad idea. First of
> all, it break matlab-compatibility for no good reasons, second in most
> cases, IMHO, the naive programmer only wants one point, and puts some
> logics afterwards. He will not read the doc, and wont understand why his
> function is not returning after one click (so many people don't even know
> how to read docstring, I am not kidding).
> 
> As for the lines, having lines implies that there is a connection order
> in your points, which is not always the case. I suggest thus adding a
> optional (oof by default) keyword argument for this behavior.
In my applications I select multiple points from a particular line,
highlighting the point that will be selected as the mouse cursor moves. 
I put a text message on the plot explaining what to do which disappears 
when the selection is complete.
Some other useful cases are selecting an x-range, a y-range or a box,
with the appropriate shading for the selected region.
This is still in Tcl/Tk+BLT; I haven't moved it over to Python yet.
- Paul
From: Gael V. <gae...@no...> - 2008年02月05日 23:50:57
On Tue, Feb 05, 2008 at 06:46:19PM -0500, Paul Kienzle wrote:
> In my applications I select multiple points from a particular line,
> highlighting the point that will be selected as the mouse cursor moves. 
> I put a text message on the plot explaining what to do which disappears 
> when the selection is complete.
The idea of the text message is pretty cool in term of usability.
All in all the features that you describe are relly nice. I am sure that
if you find a way to make them toolkit-agnostic they would be a great
addition to MPL.
> Some other useful cases are selecting an x-range, a y-range or a box,
> with the appropriate shading for the selected region.
Same thing here, this is quite nice too. I think this behavior can now be
created using ginput, but it would be cool to have it out of the box.
Sorry, I can't work on it know.
Gaël
Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.
Thanks for helping keep SourceForge clean.
X





Briefly describe the problem (required):
Upload screenshot of ad (required):
Select a file, or drag & drop file here.
Screenshot instructions:

Click URL instructions:
Right-click on the ad, choose "Copy Link", then paste here →
(This may not be possible with some types of ads)

More information about our ad policies

Ad destination/click URL:

AltStyle によって変換されたページ (->オリジナル) /