[matplotlib-devel] waitforbuttonpress function addition
Hi, Following Gael Varoquaux's lead on adding a ginput command to matplotlib (nice job!), I added a waitforbuttonpress function to matplotlib. The patch is attached (generate using svn diff from the matplotlib/trunk/matplotlib directory). waitforbuttonpress is a simple function with a matlab equivalent that returns a list of true/false's - true for a keyboard click and false for a mouse click. I use it in matlab regularly as a simple yes/no question (often to decide whether or not to save a figure). The way I have implemented it is by adding an additional class BlockingKeyMouseInput, which is quite similar to BlockingMouseInput, but waits for both key and mouse events. A smarter person than I could probably combine these two classes and make something that would serve both functions. But I am basically new to python and don't feel comfortable. Perhaps someone else could take a look and make improvements/simplifications? The other thing that I have noticed with both ginput and waitforbuttonpress is that if you use ctrl-c to break out of either, then the callback functions remain attached to their respective events (e.g., try ginput(n=-1,timeout=-1,verbose=True) and hit ctrl-c). This probably isn't a huge problem, but it would be nice if there was a way to say "if ctrl-c is pressed, cleanup nicely". Does someone know if that is possible? Cheers, David -- ** David M. Kaplan Charge de Recherche 1 Institut de Recherche pour le Developpement Centre de Recherche Halieutique Mediterraneenne et Tropicale av. Jean Monnet B.P. 171 34203 Sete cedex France Phone: +33 (0)4 99 57 32 27 Fax: +33 (0)4 99 57 32 95 http://www.ur097.ird.fr/team/dkaplan/index.html ** -- ** David M. Kaplan Assistant Researcher UCSC / Institute of Marine Sciences Ocean Sciences 1156 High St. SC, CA 95064 Phone: 831-459-4789 Fax: 831-459-4882 http://pmc.ucsc.edu/~dmk/ ** Index: lib/matplotlib/pyplot.py === --- lib/matplotlib/pyplot.py (revision 5735) +++ lib/matplotlib/pyplot.py (working copy) @@ -335,7 +335,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): Index: lib/matplotlib/figure.py === --- lib/matplotlib/figure.py (revision 5735) +++ lib/matplotlib/figure.py (working copy) @@ -6,6 +6,9 @@ :class:`SubplotParams` control the default spacing of the subplots +:class:`BlockingKeyMouseInput` +creates a callable object to retrieve key or mouse clicks in a blocking way for interactive sessions + :class:`BlockingMouseInput` creates a callable object to retrieve mouse clicks in a blocking way for interactive sessions @@ -118,6 +121,78 @@ setattr(self, s, val) +class BlockingKeyMouseInput(object): +""" +Class that creates a callable object to retrieve key or mouse clicks in a +blocking way. +""" +def __init__(self, fig): +self.fig = fig + + +def on_mouse_click(self, event): +""" +Event handler that will be passed to the current figure to +retrieve clicks. +""" +self.events.append(event) +self.buttons.append(event.button) +self.keyormouse.append(False) + +if len(self.keyormouse) >= self.n: +self.done = True + +def on_key_click(self, event): +""" +Event handler that will be passed to the current figure to +retrieve key presses. +""" +self.events.append(event) +self.keys.append(event.key) +self.keyormouse.append(True) + +if len(self.keyormouse) >= self.n: +self.done = True + +def __call__(self, n=1, timeout=-1 ): +""" +Blocking call to retrieve n key or mouse events +""" +self.done= False +self.keys = [] +self.buttons = [] +self.events = [] +self.keyormouse = [] + +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.callbackm = self.fig.canvas.mpl_connect('button_press_event', +
Re: [matplotlib-devel] waitforbuttonpress function addition
On Fri, Jul 11, 2008 at 03:22:30PM +0200, David M. Kaplan wrote: > The way I have implemented it is by adding an additional class > BlockingKeyMouseInput, which is quite similar to BlockingMouseInput, but > waits for both key and mouse events. A smarter person than I could > probably combine these two classes and make something that would serve > both functions. But I am basically new to python and don't feel > comfortable. Perhaps someone else could take a look and make > improvements/simplifications? The only significantly different lines are the two lines where an mplconnect is done to register the callback. You could abstract this in a method and then have a base class and two sub classes for each call: the blocking from mouse and the blocking from button. > The other thing that I have noticed with both ginput and > waitforbuttonpress is that if you use ctrl-c to break out of either, > then the callback functions remain attached to their respective events > (e.g., try ginput(n=-1,timeout=-1,verbose=True) and hit ctrl-c). This > probably isn't a huge problem, but it would be nice if there was a way > to say "if ctrl-c is pressed, cleanup nicely". Does someone know if > that is possible? I think this is a good usecase for a try: ... finally: ... . I don't have time to do these changes right now, as I am very busy both with IPython and Mayavi, and will be travelling next two weeks, but you can have a good at them, and someone else will probably commit your patch, if you removed the code duplication. Cheers, Gaël - Sponsored by: SourceForge.net Community Choice Awards: VOTE NOW! Studies have shown that voting for your favorite open source project, along with a healthy diet, reduces your potential for chronic lameness and boredom. Vote Now at http://www.sourceforge.net/community/cca08 ___ Matplotlib-devel mailing list Matplotlib-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/matplotlib-devel
Re: [matplotlib-devel] waitforbuttonpress function addition
Hi, Attached is a new patch to replace the previous one that I sent that does what Gael suggested. It works well and seems fairly efficient to me, but again I am new to python and I could be mistaken about what Gael was suggesting. Basically, I created a base class that does the blocking and collects events of any particular set of types specified by name at initiation. I then created two subclasses that specify exactly which events they deal with and do additional processing beyond just collecting events, one for mouse events and one for mouse+keyboard events. These are then called by ginput and waitforbuttonpress respectively. I also changed my version of waitforbuttonpress so that it precisely matches the functionality of matlab's waitforbuttonpress, with the exception of a timeout option. Comments welcome. Cheers, David On Fri, 2008-07-11 at 16:12 +0200, Gael Varoquaux wrote: > On Fri, Jul 11, 2008 at 03:22:30PM +0200, David M. Kaplan wrote: > > The way I have implemented it is by adding an additional class > > BlockingKeyMouseInput, which is quite similar to BlockingMouseInput, but > > waits for both key and mouse events. A smarter person than I could > > probably combine these two classes and make something that would serve > > both functions. But I am basically new to python and don't feel > > comfortable. Perhaps someone else could take a look and make > > improvements/simplifications? > > The only significantly different lines are the two lines where an > mplconnect is done to register the callback. You could abstract this in a > method and then have a base class and two sub classes for each call: the > blocking from mouse and the blocking from button. > > > The other thing that I have noticed with both ginput and > > waitforbuttonpress is that if you use ctrl-c to break out of either, > > then the callback functions remain attached to their respective events > > (e.g., try ginput(n=-1,timeout=-1,verbose=True) and hit ctrl-c). This > > probably isn't a huge problem, but it would be nice if there was a way > > to say "if ctrl-c is pressed, cleanup nicely". Does someone know if > > that is possible? > > I think this is a good usecase for a try: ... finally: ... . > > I don't have time to do these changes right now, as I am very busy both > with IPython and Mayavi, and will be travelling next two weeks, but you > can have a good at them, and someone else will probably commit your > patch, if you removed the code duplication. > > Cheers, > > Gaël > -- ** David M. Kaplan Assistant Researcher UCSC / Institute of Marine Sciences Ocean Sciences 1156 High St. SC, CA 95064 Phone: 831-459-4789 Fax: 831-459-4882 http://pmc.ucsc.edu/~dmk/ ** Index: lib/matplotlib/pyplot.py === --- lib/matplotlib/pyplot.py (revision 5735) +++ lib/matplotlib/pyplot.py (working copy) @@ -335,7 +335,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): Index: lib/matplotlib/figure.py === --- lib/matplotlib/figure.py (revision 5735) +++ lib/matplotlib/figure.py (working copy) @@ -6,8 +6,16 @@ :class:`SubplotParams` control the default spacing of the subplots +: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 +creates a callable object to retrieve mouse clicks in a blocking way for interactive sessions. +Note: Subclass of BlockingInput. Used by ginput :class:`Figure` top level container for all plot elements @@ -118,24 +126,107 @@ setattr(self, s, val) -class BlockingMouseInput(object): +class BlockingInput(object): """ -Class that creates a callable object to retrieve mouse clicks in a +Class that creates a callable object to retrieve events in a blocking way. """ -def __init__(self, fig): +def __init__(self, fig, eventslist=()): self.fig = fig
[matplotlib-devel] colormap menu
Hi, I created a menu for selecting colormaps from a context menu on the graph. The attached code cmapmenu.py contains a runnable example. I've only implemented support for wx. In general, I would like to be able to add context menu operations to individual artists on the plot, and will be doing so for my own applications. Is this something that could live in the matplotlib backends? Are there users of Qt, Gtk, Tk, ... who are willing to add support for them? - Paul # This program is public domain """ Defines CMapMenu, a wx submenu containing colormaps. === Example === The following defines a context menu with mapper:: import wx import cmapmenu ... def onContextMenu(self,event): popup = wx.Menu() item = popup.Append(wx.ID_ANY,'&Save image', 'Save image as PNG') wx.EVT_MENU(self, item.GetId(), self.onSaveImage) item = popup.Append(wx.ID_ANY,'&Grid on/off', 'Toggle grid lines') wx.EVT_MENU(self, item.GetId(), self.onGridToggle) item = popup.AppendMenu(wx.ID_ANY, "Colourmaps", CMapMenu(self, self.mapper, self.canvas)) The assumption is that mapper and canvas are attributes of the panel for which the context menu is defined. When the new colour map is selected, the mapper will be reset and the figure redrawn. Sometimes you will want to do more than just update the mapper for the current canvas. You may for example want to record the new colormap name in the application settings file so that it will be there when the application is reloaded. To do this, call CMapMenu(callback=self.OnColormap). This will call the method OnColormap with the parameter name giving the name of the colormap. """ __all__ = ['CMapMenu'] import sys import wx import numpy from matplotlib import cm def colorbar_bitmap(colormap,length,thickness=10,orientation='horizontal'): """ Convert a colormap to a bitmap showing a colorbar for the colormap. Orientation can be vertical or horizontal (only looks at the first letter). """ # Make an RGBA array from the colormap, either horizontally or vertically. V = colormap(numpy.linspace(0,1,length),bytes=True) if orientation[0].lower() == 'h': V = numpy.tile(V,(thickness,1)) bitmap = wx.BitmapFromBufferRGBA(length,thickness,V) elif orientation[0].lower() == 'v': V = numpy.tile(V,(1,thickness)) bitmap = wx.BitmapFromBufferRGBA(thickness,length,V) else: raise ValueError,"expected orientation [V]ertical or [H]orizontal" return bitmap def all_colormaps(): """ Iterate over the available colormaps """ maps = [name for name in cm.datad.keys() if not name.endswith("_r")] maps.sort() return maps def grouped_colormaps(): """ Colormaps grouped by source. """ mlab = ['autumn','winter','spring','summer', 'gray','bone','copper','pink', 'cool','hot', 'hsv','jet','spectral', #'binary', # Seems to be a reverse of gray 'prism','flag'] mlab_r = [m+'_r' for m in mlab] brewer = ['Accent','Dark2', 'Spectral', 'Paired', 'Blues','Greens','Greys','Oranges','Purples','Reds', 'Pastel1','Pastel2', 'Set1','Set2','Set3', 'BrBG','BuGn','BuPu','GnBu', 'OrRd', 'PiYG','PRGn','PuBu','PuBuGn', 'PuOr','PuRd', 'RdBu','RdGy','RdPu', 'RdYlBu','RdYlGn', 'YlGn','YlGnBu','YlOrBr','YlOrRd', ] brewer_r = [m+'_r' for m in brewer] gist = ['gist_ncar','gist_rainbow', 'gist_stern','gist_earth', 'gist_gray','gist_heat', #'gist_yarg', # Seems to be a reverse of gray ] gist_r = [m+'_r' for m in gist] return gist + [None] + mlab + [None] + brewer def event_callback(callback, **kw): """ Add keyword arguments to the event callback. """ return lambda evt: callback(evt,**kw) class CMapMenu(wx.Menu): """ Menu tree binding to a list of colormaps. """ def __init__(self, window, mapper=None, canvas=None, callback=None): """ Define a context menu for selecting colormaps. Need a window to use as the event handler. If mapper is defined, it will be updated with the new colormap. If canvas is defined, it will update on idle. """ wx.Menu.__init__(self) # OS X needs 16x16 icons; Windows and Linux can be longer bar_length = 32 if not sys.platform in ['darwin'] else 16 bar_height = 16 self.mapper,self.canvas,self.callback = mapper,canvas,callback self.selected = None self.mapid = {} for name in grouped_colormaps(): if name is None: self.AppendSeparator() else: item = wx.Menu
[matplotlib-devel] Two projects possibly of interest
Howdy, I stumbled upon these two projects which may be of interest to some in mpl land: - perceptual image diff: http://pdiff.sourceforge.net/ I have no idea if this works well or not, but if it does, it could be useful for regression testing of mpl, something which has been sorely missing and normally involves running the test driver, looking for errors and possibly inspecting plots by hand (or by eye, as it were). I think that mpl would greatly benefit from some form of automatic testing. - Graphite: http://graphite.wikidot.com/faq Python based real-time graphs from large/high rate data sets. Every now and then we get posts here asking about doing plots of high throughput data, a task for which mpl isn't always ideally suited. This might be a good alternative in some cases. I'm sure it doesn't do any of the sophisticated/scientific plotting many of us need mpl for, but for some use cases it may be a good tool. cheers, f - Sponsored by: SourceForge.net Community Choice Awards: VOTE NOW! Studies have shown that voting for your favorite open source project, along with a healthy diet, reduces your potential for chronic lameness and boredom. Vote Now at http://www.sourceforge.net/community/cca08 ___ Matplotlib-devel mailing list Matplotlib-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/matplotlib-devel