From: <dmk...@us...> - 2008-07-17 19:21:20
|
Revision: 5782 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5782&view=rev Author: dmkaplan Date: 2008-07-17 19:20:32 +0000 (Thu, 17 Jul 2008) Log Message: ----------- Adding more functionality for interacting with figure windows through mouse clicks and keyboard clicks. This includes adding a waitforbuttonpress function, as well as manual contour label location selection for clabel. The underlying Blocking* classes that drive this interaction have been moved into a separate file "lib/matplotlib/blocking_input.py". Also added a changelog entry. Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/contour.py trunk/matplotlib/lib/matplotlib/figure.py trunk/matplotlib/lib/matplotlib/pyplot.py Added Paths: ----------- trunk/matplotlib/lib/matplotlib/blocking_input.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-07-17 19:15:58 UTC (rev 5781) +++ trunk/matplotlib/CHANGELOG 2008-07-17 19:20:32 UTC (rev 5782) @@ -1,3 +1,6 @@ +2008-07-17 Added ability to manually select contour label locations. + Also added a waitforbuttonpress function. - DMK + 2008-07-17 Fix bug with NaNs at end of path (thanks, Andrew Straw for the report) - MGD Added: trunk/matplotlib/lib/matplotlib/blocking_input.py =================================================================== --- trunk/matplotlib/lib/matplotlib/blocking_input.py (rev 0) +++ trunk/matplotlib/lib/matplotlib/blocking_input.py 2008-07-17 19:20:32 UTC (rev 5782) @@ -0,0 +1,540 @@ +""" +This provides several classes used for blocking interaction with figure windows: + +:class:`BlockingInput` + creates a callable object to retrieve events in a blocking way for interactive sessions + +:class:`BlockingKeyMouseInput` + creates a callable object to retrieve key or mouse clicks in a blocking way for interactive sessions. + Note: Subclass of BlockingInput. Used by waitforbuttonpress + +:class:`BlockingMouseInput` + creates a callable object to retrieve mouse clicks in a blocking way for interactive sessions. + Note: Subclass of BlockingInput. Used by ginput + +:class:`BlockingContourLabeler` + creates a callable object to retrieve mouse clicks in a blocking way that will then be used to place labels on a ContourSet + Note: Subclass of BlockingMouseInput. Used by clabel +""" + +import time +import numpy as np +import matplotlib.path as path + +class BlockingInput(object): + """ + Class that creates a callable object to retrieve events in a + blocking way. + """ + def __init__(self, fig, eventslist=()): + self.fig = fig + assert isinstance(eventslist, tuple), "Requires a tuple of event name strings" + self.eventslist = eventslist + + def on_event(self, event): + """ + Event handler that will be passed to the current figure to + retrieve events. + """ + # Add a new event to list - using a separate function is + # overkill for the base class, but this is consistent with + # subclasses + self.add_event(event) + + if self.verbose: + print "Event %i" % len(self.events) + + # This will extract info from events + self.post_event() + + # Check if we have enough events already + if len(self.events) >= self.n and self.n > 0: + self.done = True + + def post_event(self): + """For baseclass, do nothing but collect events""" + pass + + def cleanup(self): + """Disconnect all callbacks""" + for cb in self.callbacks: + self.fig.canvas.mpl_disconnect(cb) + + self.callbacks=[] + + def add_event(self,event): + """For base class, this just appends an event to events.""" + self.events.append(event) + + def pop_event(self,index=-1): + """ + This removes an event from the event list. Defaults to + removing last event, but an index can be supplied. Note that + this does not check that there are events, much like the + normal pop method. If not events exist, this will throw an + exception. + """ + self.events.pop(index) + + def pop(self,index=-1): + self.pop_event(index) + pop.__doc__=pop_event.__doc__ + + def __call__(self, n=1, timeout=30, verbose=False ): + """ + Blocking call to retrieve n events + """ + + assert isinstance(n, int), "Requires an integer argument" + self.n = n + + self.events = [] + self.done = False + self.verbose = verbose + self.callbacks = [] + + # Ensure that the figure is shown + self.fig.show() + + # connect the events to the on_event function call + for n in self.eventslist: + self.callbacks.append( self.fig.canvas.mpl_connect(n, self.on_event) ) + + try: + # wait for n clicks + counter = 0 + while not self.done: + self.fig.canvas.flush_events() + time.sleep(0.01) + + # check for a timeout + counter += 1 + if timeout > 0 and counter > timeout/0.01: + print "Timeout reached"; + break; + finally: # Activated on exception like ctrl-c + self.cleanup() + + # Disconnect the callbacks + self.cleanup() + + # Return the events in this case + return self.events + +class BlockingMouseInput(BlockingInput): + """ + Class that creates a callable object to retrieve mouse clicks in a + blocking way. + """ + def __init__(self, fig): + BlockingInput.__init__(self, fig=fig, + eventslist=('button_press_event',) ) + + def post_event(self): + """ + This will be called to process events + """ + assert len(self.events)>0, "No events yet" + + event = self.events[-1] + button = event.button + + # Using additional methods for each button is a bit overkill + # for this class, but it allows for easy overloading. Also, + # this would make it easy to attach other type of non-mouse + # events to these "mouse" actions. For example, the matlab + # version of ginput also allows you to add points with + # keyboard clicks. This could easily be added to this class + # with relatively minor modification to post_event and + # __init__. + if button == 3: + self.button3(event) + elif button == 2: + self.button2(event) + else: + self.button1(event) + + def button1( self, event ): + """ + Will be called for any event involving a button other than + button 2 or 3. This will add a click if it is inside axes. + """ + if event.inaxes: + self.add_click(event) + else: # If not a valid click, remove from event list + BlockingInput.pop(self) + + def button2( self, event ): + """ + Will be called for any event involving button 2. + Button 2 ends blocking input. + """ + + # Remove last event just for cleanliness + BlockingInput.pop(self) + + # This will exit even if not in infinite mode. This is + # consistent with matlab and sometimes quite useful, but will + # require the user to test how many points were actually + # returned before using data. + self.done = True + + def button3( self, event ): + """ + Will be called for any event involving button 3. + Button 3 removes the last click. + """ + # Remove this last event + BlockingInput.pop(self) + + # Now remove any existing clicks if possible + if len(self.events)>0: + self.pop() + + def add_click(self,event): + """ + This add the coordinates of an event to the list of clicks + """ + self.clicks.append((event.xdata,event.ydata)) + if self.verbose: + print "input %i: %f,%f" % (len(self.clicks), + event.xdata, event.ydata) + + # If desired plot up click + if self.show_clicks: + self.marks.extend( + event.inaxes.plot([event.xdata,], [event.ydata,], 'r+') ) + self.fig.canvas.draw() + + def pop_click(self,index=-1): + """ + This removes a click from the list of clicks. Defaults to + removing the last click. + """ + self.clicks.pop(index) + + if self.show_clicks: + mark = self.marks.pop(index) + mark.remove() + self.fig.canvas.draw() + + def pop(self,index=-1): + """ + This removes a click and the associated event from the object. + Defaults to removing the last click, but any index can be + supplied. + """ + self.pop_click(index) + BlockingInput.pop(self,index) + + def cleanup(self): + # clean the figure + if self.show_clicks: + for mark in self.marks: + mark.remove() + self.marks = [] + self.fig.canvas.draw() + + # Call base class to remove callbacks + BlockingInput.cleanup(self) + + def __call__(self, n=1, timeout=30, verbose=False, show_clicks=True): + """ + Blocking call to retrieve n coordinate pairs through mouse + clicks. + """ + self.show_clicks = show_clicks + self.clicks = [] + self.marks = [] + BlockingInput.__call__(self,n=n,timeout=timeout,verbose=verbose) + + return self.clicks + +class BlockingContourLabeler( BlockingMouseInput ): + """ + Class that creates a callable object that uses mouse clicks on a + figure window to place contour labels. + """ + def __init__(self,cs): + self.cs = cs + BlockingMouseInput.__init__(self, fig=cs.ax.figure ) + + def button1(self,event): + """ + This will be called if an event involving a button other than + 2 or 3 occcurs. This will add a label to a contour. + """ + if event.inaxes == self.cs.ax: + conmin,segmin,imin,xmin,ymin = self.cs.find_nearest_contour( + event.x, event.y)[:5] + + paths = self.cs.collections[conmin].get_paths() + lc = paths[segmin].vertices + + # Figure out label rotation. This is very cludgy. + # Ideally, there would be one method in ContourLabeler + # that would figure out the best rotation for a label at a + # point, but the way automatic label rotation is done is + # quite mysterious to me and doesn't seem easy to + # generalize to non-automatic label placement. The method + # used below is not very robust! It basically looks one + # point before and one point after label location on + # contour and takes mean of angles of two vectors formed. + # This produces "acceptable" results, but not nearly as + # nice as automatic method. + ll = lc[max(0,imin-1):imin+2] # Get points around point + dd = np.diff(ll,axis=0) + rotation = np.mean( np.arctan2(dd[:,1], dd[:,0]) ) * 180 / np.pi + if rotation > 90: + rotation = rotation -180 + if rotation < -90: + rotation = 180 + rotation + + self.cs.add_label(xmin,ymin,rotation,conmin) + + if self.inline: + # Get label width for breaking contours + lw = self.cs.get_label_width(self.cs.label_levels[conmin], + self.cs.fmt, + self.cs.fslist[conmin]) + # Break contour + new=self.cs.break_linecontour(lc,rotation,lw,imin) + if len(new[0]): + paths[segmin] = path.Path(new[0]) + if len(new[1]): + paths.extend([path.Path(new[1])]) + + self.fig.canvas.draw() + else: # Remove event if not valid + BlockingInput.pop(self) + + def button3(self,event): + """ + This will be called if button 3 is clicked. This will remove + a label if not in inline mode. Unfortunately, if one is doing + inline labels, then there is currently no way to fix the + broken contour - once humpty-dumpty is broken, he can't be put + back together. In inline mode, this does nothing. + """ + if self.inline: + pass + else: + self.cs.pop_label() + self.cs.ax.figure.canvas.draw() + + def __call__(self,inline,n=-1,timeout=-1): + self.inline=inline + BlockingMouseInput.__call__(self,n=n,timeout=timeout,verbose=False, + show_clicks=False) + +class BlockingKeyMouseInput(BlockingInput): + """ + Class that creates a callable object to retrieve a single mouse or + keyboard click + """ + def __init__(self, fig): + BlockingInput.__init__(self, fig=fig, eventslist=('button_press_event','key_press_event') ) + + def post_event(self): + """ + Determines if it is a key event + """ + assert len(self.events)>0, "No events yet" + + self.keyormouse = self.events[-1].name == 'key_press_event' + + def __call__(self, timeout=30, verbose=False): + """ + Blocking call to retrieve a single mouse or key click + Returns True if key click, False if mouse, or None if timeout + """ + self.keyormouse = None + BlockingInput.__call__(self,n=1,timeout=timeout,verbose=verbose) + + return self.keyormouse + +""" +This provides several classes used for interaction with figure windows: + +:class:`BlockingInput` + creates a callable object to retrieve events in a blocking way for interactive sessions + +:class:`BlockingKeyMouseInput` + creates a callable object to retrieve key or mouse clicks in a blocking way for interactive sessions. + Note: Subclass of BlockingInput. Used by waitforbuttonpress + +:class:`BlockingMouseInput` + creates a callable object to retrieve mouse clicks in a blocking way for interactive sessions. + Note: Subclass of BlockingInput. Used by ginput +""" + +import time + +class BlockingInput(object): + """ + Class that creates a callable object to retrieve events in a + blocking way. + """ + def __init__(self, fig, eventslist=()): + self.fig = fig + assert isinstance(eventslist, tuple), \ + "Requires a tuple of event name strings" + self.eventslist = eventslist + + def on_event(self, event): + """ + Event handler that will be passed to the current figure to + retrieve events. + """ + self.events.append(event) + + if self.verbose: + print "Event %i" % len(self.events) + + # This will extract info from events + self.post_event() + + if len(self.events) >= self.n and self.n > 0: + self.done = True + + def post_event(self): + """For baseclass, do nothing but collect events""" + pass + + def cleanup(self): + """Remove callbacks""" + for cb in self.callbacks: + self.fig.canvas.mpl_disconnect(cb) + + self.callbacks=[] + + def __call__(self, n=1, timeout=30, verbose=False ): + """ + Blocking call to retrieve n events + """ + + assert isinstance(n, int), "Requires an integer argument" + self.n = n + + self.events = [] + self.done = False + self.verbose = verbose + self.callbacks = [] + + # Ensure that the figure is shown + self.fig.show() + + # connect the events to the on_event function call + for n in self.eventslist: + self.callbacks.append( self.fig.canvas.mpl_connect(n, self.on_event) ) + + try: + # wait for n clicks + counter = 0 + while not self.done: + self.fig.canvas.flush_events() + time.sleep(0.01) + + # check for a timeout + counter += 1 + if timeout > 0 and counter > timeout/0.01: + print "Timeout reached"; + break; + finally: # Activated on exception like ctrl-c + self.cleanup() + + # Disconnect the callbacks + self.cleanup() + + # Return the events in this case + return self.events + +class BlockingMouseInput(BlockingInput): + """ + Class that creates a callable object to retrieve mouse clicks in a + blocking way. + """ + def __init__(self, fig): + BlockingInput.__init__(self, fig=fig, eventslist=('button_press_event',) ) + + def post_event(self): + """ + This will be called to process events + """ + assert len(self.events)>0, "No events yet" + + event = self.events[-1] + + if event.button == 3: + # If it's a right click, pop the last coordinates. + if len(self.clicks) > 0: + self.clicks.pop() + del self.events[-2:] # Remove button=3 event and previous event + + if self.show_clicks: + mark = self.marks.pop() + mark.remove() + self.fig.canvas.draw() + elif event.button == 2 and self.n < 0: + # If it's a middle click, and we are in infinite mode, finish + self.done = True + elif event.inaxes: + # If it's a valid click, append the coordinates to the list + self.clicks.append((event.xdata, event.ydata)) + if self.verbose: + print "input %i: %f,%f" % (len(self.clicks), + event.xdata, event.ydata) + if self.show_clicks: + self.marks.extend( + event.inaxes.plot([event.xdata,], [event.ydata,], 'r+') ) + self.fig.canvas.draw() + + def cleanup(self): + # clean the figure + if self.show_clicks: + for mark in self.marks: + mark.remove() + self.marks = [] + self.fig.canvas.draw() + + # Call base class to remove callbacks + BlockingInput.cleanup(self) + + def __call__(self, n=1, timeout=30, verbose=False, show_clicks=True): + """ + Blocking call to retrieve n coordinate pairs through mouse + clicks. + """ + self.show_clicks = show_clicks + self.clicks = [] + self.marks = [] + BlockingInput.__call__(self,n=n,timeout=timeout,verbose=verbose) + + return self.clicks + +class BlockingKeyMouseInput(BlockingInput): + """ + Class that creates a callable object to retrieve a single mouse or + keyboard click + """ + def __init__(self, fig): + BlockingInput.__init__(self, fig=fig, eventslist=('button_press_event','key_press_event') ) + + def post_event(self): + """ + Determines if it is a key event + """ + assert len(self.events)>0, "No events yet" + + self.keyormouse = self.events[-1].name == 'key_press_event' + + def __call__(self, timeout=30, verbose=False): + """ + Blocking call to retrieve a single mouse or key click + Returns True if key click, False if mouse, or None if timeout + """ + self.keyormouse = None + BlockingInput.__call__(self,n=1,timeout=timeout,verbose=verbose) + + return self.keyormouse + Modified: trunk/matplotlib/lib/matplotlib/contour.py =================================================================== --- trunk/matplotlib/lib/matplotlib/contour.py 2008-07-17 19:15:58 UTC (rev 5781) +++ trunk/matplotlib/lib/matplotlib/contour.py 2008-07-17 19:20:32 UTC (rev 5782) @@ -17,6 +17,9 @@ import matplotlib.text as text import matplotlib.cbook as cbook +# Import needed for adding manual selection capability to clabel +from matplotlib.blocking_input import BlockingContourLabeler + # We can't use a single line collection for contour because a line # collection can have only a single line style, and we want to be able to have # dashed negative contours, for example, and solid positive contours. @@ -69,6 +72,13 @@ *fmt*: a format string for the label. Default is '%1.3f' + *manual*: + if *True*, contour labels will be placed manually using + mouse clicks. Click the first button near a contour to + add a label, click the second button (or potentially both + mouse buttons at once) to finish adding labels. The third + button can be used to remove the last label added, but + only if labels are not inline. """ fontsize = kwargs.get('fontsize', None) @@ -76,8 +86,9 @@ self.fmt = kwargs.get('fmt', '%1.3f') _colors = kwargs.get('colors', None) + # Detect if manual selection is desired and remove from argument list + self.manual_select=kwargs.get('manual',False) - if len(args) == 0: levels = self.levels indices = range(len(self.levels)) @@ -126,10 +137,16 @@ #self.cl_cvalues = [] # same self.cl_xy = [] - self.labels(inline) + if self.manual_select: + print 'Select label locations manually using first mouse button.' + print 'End manual selection with second mouse button.' + if not inline: + print 'Remove last label by clicking third mouse button.' - for label in self.cl: - self.ax.add_artist(label) + blocking_contour_labeler = BlockingContourLabeler(self) + blocking_contour_labeler(inline) + else: + self.labels(inline) self.label_list = cbook.silent_list('text.Text', self.cl) return self.label_list @@ -335,19 +352,85 @@ return x,y, rotation, dind + def add_label(self,x,y,rotation,icon): + dx,dy = self.ax.transData.inverted().transform_point((x,y)) + t = text.Text(dx, dy, rotation = rotation, + horizontalalignment='center', + verticalalignment='center') + + color = self.label_mappable.to_rgba(self.label_cvalues[icon], + alpha=self.alpha) + + _text = self.get_text(self.label_levels[icon],self.fmt) + self.set_label_props(t, _text, color) + self.cl.append(t) + self.cl_cvalues.append(self.label_cvalues[icon]) + + # Add label to plot here - useful for manual mode label selection + self.ax.add_artist(t) + + def pop_label(self,index=-1): + '''Defaults to removing last label, but any index can be supplied''' + self.cl_cvalues.pop(index) + t = self.cl.pop(index) + t.remove() + + def find_nearest_contour( self, x, y, pixel=True ): + """ + Finds contour that is closest to a point. Defaults to + measuring distance in pixels (screen space - useful for manual + contour labeling), but this can be controlled via a keyword + argument. + + Returns a tuple containing the contour, segment, index of + segment, x & y of segment point and distance to minimum point. + """ + + # This function uses a method that is probably quite + # inefficient based on converting each contour segment to + # pixel coordinates and then comparing the given point to + # those coordinates for each contour. This will probably be + # quite slow for complex contours, but for normal use it works + # sufficiently well that the time is not noticeable. + # Nonetheless, improvements could probably be made. + + dmin = 1e10 + conmin = None + segmin = None + xmin = None + ymin = None + + for icon in self.label_indices: + con = self.collections[icon] + paths = con.get_paths() + for segNum, linepath in enumerate(paths): + lc = linepath.vertices + + # transfer all data points to screen coordinates if desired + if pixel: + lc = self.ax.transData.transform(lc) + + ds = (lc[:,0]-x)**2 + (lc[:,1]-y)**2 + d = min( ds ) + if d < dmin: + dmin = d + conmin = icon + segmin = segNum + imin = mpl.mlab.find( ds == d )[0] + xmin = lc[imin,0] + ymin = lc[imin,1] + + return (conmin,segmin,imin,xmin,ymin,dmin) + def labels(self, inline): levels = self.label_levels fslist = self.fslist trans = self.ax.transData - _colors = self.label_mappable.to_rgba(self.label_cvalues, - alpha=self.alpha) - fmt = self.fmt - for icon, lev, color, cvalue, fsize in zip(self.label_indices, - self.label_levels, - _colors, - self.label_cvalues, fslist): + + for icon, lev, fsize in zip(self.label_indices, + self.label_levels, fslist): con = self.collections[icon] - lw = self.get_label_width(lev, fmt, fsize) + lw = self.get_label_width(lev, self.fmt, fsize) additions = [] paths = con.get_paths() for segNum, linepath in enumerate(paths): @@ -362,16 +445,8 @@ slc = trans.transform(linecontour) if self.print_label(slc,lw): x,y, rotation, ind = self.locate_label(slc, lw) - # transfer the location of the label back to - # data coordinates - dx,dy = trans.inverted().transform_point((x,y)) - t = text.Text(dx, dy, rotation = rotation, - horizontalalignment='center', - verticalalignment='center') - _text = self.get_text(lev,fmt) - self.set_label_props(t, _text, color) - self.cl.append(t) - self.cl_cvalues.append(cvalue) + self.add_label(x,y,rotation,icon) + if inline: new = self.break_linecontour(linecontour, rotation, lw, ind) if len(new[0]): Modified: trunk/matplotlib/lib/matplotlib/figure.py =================================================================== --- trunk/matplotlib/lib/matplotlib/figure.py 2008-07-17 19:15:58 UTC (rev 5781) +++ trunk/matplotlib/lib/matplotlib/figure.py 2008-07-17 19:20:32 UTC (rev 5782) @@ -6,9 +6,6 @@ :class:`SubplotParams` control the default spacing of the subplots -:class:`BlockingMouseInput` - creates a callable object to retrieve mouse clicks in a blocking way for interactive sessions - :class:`Figure` top level container for all plot elements @@ -32,6 +29,7 @@ from transforms import Affine2D, Bbox, BboxTransformTo, TransformedBbox from projections import projection_factory, get_projection_names, \ get_projection_class +from matplotlib.blocking_input import BlockingMouseInput, BlockingKeyMouseInput import matplotlib.cbook as cbook @@ -117,87 +115,6 @@ setattr(self, s, val) - -class BlockingMouseInput(object): - """ - Class that creates a callable object to retrieve mouse clicks in a - blocking way. - """ - def __init__(self, fig): - self.fig = fig - - - def on_click(self, event): - """ - Event handler that will be passed to the current figure to - retrieve clicks. - """ - if event.button == 3: - # If it's a right click, pop the last coordinates. - if len(self.clicks) > 0: - self.clicks.pop() - if self.show_clicks: - mark = self.marks.pop() - mark.remove() - self.fig.canvas.draw() - elif event.button == 2 and self.n < 0: - # If it's a middle click, and we are in infinite mode, finish - self.done = True - elif event.inaxes: - # If it's a valid click, append the coordinates to the list - self.clicks.append((event.xdata, event.ydata)) - if self.verbose: - print "input %i: %f,%f" % (len(self.clicks), - event.xdata, event.ydata) - if self.show_clicks: - self.marks.extend( - event.inaxes.plot([event.xdata,], [event.ydata,], 'r+') ) - self.fig.canvas.draw() - if self.n > 0 and len(self.clicks) >= self.n: - self.done = True - - - def __call__(self, n=1, timeout=30, verbose=False, show_clicks=True): - """ - Blocking call to retrieve n coordinate pairs through mouse - clicks. - """ - self.verbose = verbose - self.done = False - self.clicks = [] - self.show_clicks = True - self.marks = [] - - assert isinstance(n, int), "Requires an integer argument" - self.n = n - - # Ensure that the figure is shown - self.fig.show() - # connect the click events to the on_click function call - self.callback = self.fig.canvas.mpl_connect('button_press_event', - self.on_click) - # wait for n clicks - counter = 0 - while not self.done: - self.fig.canvas.flush_events() - time.sleep(0.01) - - # check for a timeout - counter += 1 - if timeout > 0 and counter > timeout/0.01: - print "ginput timeout"; - break; - - # Disconnect the event, clean the figure, and return what we have - self.fig.canvas.mpl_disconnect(self.callback) - self.callback = None - if self.show_clicks: - for mark in self.marks: - mark.remove() - self.fig.canvas.draw() - return self.clicks - - class Figure(Artist): """ @@ -1121,10 +1038,27 @@ blocking_mouse_input = BlockingMouseInput(self) return blocking_mouse_input(n=n, timeout=timeout, - verbose=verbose, show_clicks=True) + verbose=verbose, show_clicks=show_clicks) + def waitforbuttonpress(self, timeout=-1): + """ + call signature:: + waitforbuttonpress(self, timeout=-1) + Blocking call to interact with the figure. + + This will return True is a key was pressed, False if a mouse + button was pressed and None if *timeout* was reached without + either being pressed. + + If *timeout* is negative, does not timeout. + """ + + blocking_input = BlockingKeyMouseInput(self) + return blocking_input(timeout=timeout) + + def figaspect(arg): """ Create a figure with specified aspect ratio. If *arg* is a number, Modified: trunk/matplotlib/lib/matplotlib/pyplot.py =================================================================== --- trunk/matplotlib/lib/matplotlib/pyplot.py 2008-07-17 19:15:58 UTC (rev 5781) +++ trunk/matplotlib/lib/matplotlib/pyplot.py 2008-07-17 19:20:32 UTC (rev 5782) @@ -320,7 +320,21 @@ if Figure.ginput.__doc__ is not None: ginput.__doc__ = dedent(Figure.ginput.__doc__) +def waitforbuttonpress(*args, **kwargs): + """ + Blocking call to interact with the figure. + This will wait for *n* key or mouse clicks from the user and + return a list containing True's for keyboard clicks and False's + for mouse clicks. + + If *timeout* is negative, does not timeout. + """ + return gcf().waitforbuttonpress(*args, **kwargs) +if Figure.waitforbuttonpress.__doc__ is not None: + waitforbuttonpress.__doc__ = dedent(Figure.waitforbuttonpress.__doc__) + + # Putting things in figures def figtext(*args, **kwargs): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |