Dear matplotlib devels, I wrote a selection widget someone else might find useful. The widget is based on the slider widget. Things I implemented: * blitting (bar still flashes once when clicked on, sorry about that!) * Horizontal or vertical direction * Multiple selection bars on a figure * interaction between two bars to set a range Things I struggled with: * When a bar is dragged towards one of the figure edges too quickly, it gets stuck in its last recorded position on the graph (that should be the figure edge). * Don't know how to change the mouse pointer when on top of the bar (the <-> pointer for a vertical bar would be nice) * Don't know how to define a range in pixels around the bar (for grabbing), so used 0.01 of the full range instead * Haven't implemented dragging when a bar reaches the other bar (set with set_barmin or set_barmax) but hasn't reached the limit of its range. How to test: have mplbar.py and test_bar.py in the same directory, launch test_bar.py Hope this all makes sense (somehow I doubt it). And like I said, if you guys like it or want to adapt it further, please do, the code this is based on is yours after all! Regards, Egor
On Wed, Nov 26, 2008 at 5:34 AM, Egor Zindy <ez...@gm...> wrote: > Dear matplotlib devels, > > I wrote a selection widget someone else might find useful. The widget is > based on the slider widget.> > Things I implemented: > * blitting (bar still flashes once when clicked on, sorry about that!) > * Horizontal or vertical direction > * Multiple selection bars on a figure > * interaction between two bars to set a range I think this is a useful tool, but I would like to see a few enhancements, some of which you mention below. > Things I struggled with: > * When a bar is dragged towards one of the figure edges too quickly, it gets > stuck in its last recorded position on the graph (that should be the figure > edge). I think the cause of this is when your mouse leaves the axes, the axes stops processing events, so it no longer gets updates to move the bar. I'm not sure what the right solution is though. > * Don't know how to change the mouse pointer when on top of the bar (the <-> > pointer for a vertical bar would be nice) In backend bases there is a cursors enum::: In [5]: import matplotlib.backend_bases as bb In [7]: bb.cursors.HAND Out[7]: 0 In [8]: bb.cursors.MOVE Out[8]: 3 and you can use the canvas toolbar to change the cursor In [11]: fig.canvas.toolbar.set_cursor(bb.cursors.MOVE) > * Don't know how to define a range in pixels around the bar (for grabbing), > so used 0.01 of the full range instead Read the event handling and picking tutorial at http://matplotlib.sourceforge.net/users/event_handling.html if you haven't already. I suggest doing the hit testing in figure rather than data coordinates. So instead of val = event.xdata if val < self.val + self.radius ... do something like the following:: def _update(self, event) # store the line location in pixel space self.linex = event.x def _press(event): if abs(event.x-self.linex)<=5: # 5 pixels # hit! > * Haven't implemented dragging when a bar reaches the other bar (set with > set_barmin or set_barmax) but hasn't reached the limit of its range. > > How to test: > have mplbar.py and test_bar.py in the same directory, launch test_bar.py > > > Hope this all makes sense (somehow I doubt it). And like I said, if you guys > like it or want to adapt it further, please do, the code this is based on is > yours after all! With some additional cleanup along the lines of the above, I would be happy to accept this. Please document every method (I realize some exisint code does not meet this standard but we are trying to improve). Also, please use the rest conventions when documenting new code; the widgets code has not been ported over but a lot has -- see http://matplotlib.sourceforge.net/devel/documenting_mpl.html One feature you may want to consider. I think it would be nice to have color coded ticks, or possibly a static hspan along the regions where the bar can slide. Eg, see the following minor modification to test_bar:: bar1 = mplbar.Bar(ax1,valrange=[10,60],useblit=True, lw=2, color='g') ax1.axvspan(10, 60, ymin=0, ymax=0.05, facecolor='g', alpha=0.2) #range 10 to 60, init value set to 60, use blit, blue bar2 = mplbar.Bar(ax1,valrange=[10,60],valinit=60,useblit=True, lw=2, color='b') ax1.axvspan(10, 60, ymin=0.95, ymax=1.0, facecolor='blue', alpha=0.2) #range 30 to +inf (None = no bound), default set to 30, use blit, horizontal line, red bar3 = mplbar.Bar(ax1,valrange=[30,None],useblit=True, direction='horizontal', lw=2, color='r') ax1.axhspan(30, 2*npts, xmin=0.95, xmax=1.0, facecolor='red', alpha=0.2) Of course the use can always add this, so I am not sure if it is worth building in... but support for something like this via a kwar might be nice, eg Bar(blah, ..., showrange=True, rangespan=[0.95, 1.0]) JDH
> > Things I struggled with: > > * When a bar is dragged towards one of the figure edges too quickly, > it gets > > stuck in its last recorded position on the graph (that should be the > figure > > edge). > > I think the cause of this is when your mouse leaves the axes, the axes > stops processing events, so it no longer gets updates to move the bar. > I'm not sure what the right solution is though. > I'm pretty sure this is exactly what's happening. This is also annoying when you're trying to use a zoom rectangle and want to start at some point in the plot and drag it to the boundary. We've been planning on putting some time in to fix this this but just haven't been able to make the time yet. I think one possible solution is that anytime the mouse moves outside the axes, it should get one last event that is at the boundary which would require tracking which axes the last event was sent in. We haven't tried to see how hard that is to implement thought... Ted
On Wed, Nov 26, 2008 at 10:41 AM, Drain, Theodore R <the...@jp...> wrote: > I'm pretty sure this is exactly what's happening. This is also annoying when you're trying to use a zoom rectangle and want to start at some point in the plot and drag it to the boundary. We've been planning on putting some time in to fix this this but just haven't been able to make the time yet. > > I think one possible solution is that anytime the mouse moves outside the axes, it should get one last event that is at the boundary which would require tracking which axes the last event was sent in. We haven't tried to see how hard that is to implement thought... probably fairly easy using a class level attr in the MouseEvent. Another possibility would be to add support for figure_enter_event, figure_leave_event, axes_enter_event, axes_leave_event. The user code would have to separately process the axes_leave_event but this might be the cleanest solution. I am happy to add support for these ... JDH
John Hunter wrote: > On Wed, Nov 26, 2008 at 10:41 AM, Drain, Theodore R > <the...@jp...> wrote: > > >> I'm pretty sure this is exactly what's happening. This is also >> annoying when you're trying to use a zoom rectangle and want to start >> at some point in the plot and drag it to the boundary. We've been >> planning on putting some time in to fix this this but just haven't >> been able to make the time yet. >> >> I think one possible solution is that anytime the mouse moves outside >> the axes, it should get one last event that is at the boundary which >> would require tracking which axes the last event was sent in. We >> haven't tried to see how hard that is to implement thought... >> > > probably fairly easy using a class level attr in the MouseEvent. > Another possibility would be to add support for figure_enter_event, > figure_leave_event, axes_enter_event, axes_leave_event. The user code > would have to separately process the axes_leave_event but this might > be the cleanest solution. I am happy to add support for these ... > John, in my code, I implemented tests like: if self.valmin is not None: if val < self.valmin: val = self.valmin which makes limit handling better than that of the Slider class this was based on: if self.slidermin is not None: if val<=self.slidermin.val: return Theodore nailed what is happening. At the moment, tests like the one I used cannot be implemented because event.xdata returns None when outside the boundaries. This cannot be compared with ax.get_xlim(). Maybe a value should be returned even when outside. the event.inaxes should be enough to know if the mouse pointer is inside or outside. Although that would probably break a few things that at the moment depend on event.xdata being None when outside!
On Wed, Nov 26, 2008 at 10:59 AM, John Hunter <jd...@gm...> wrote: > probably fairly easy using a class level attr in the MouseEvent. > Another possibility would be to add support for figure_enter_event, > figure_leave_event, axes_enter_event, axes_leave_event. The user code > would have to separately process the axes_leave_event but this might > be the cleanest solution. I am happy to add support for these ... I just committed axes/figure enter/leave event notifications, with example in examples/event_handling/figure_axes_enter_leave.py. If you register for a leave event, you will get passed the last event that was in your axes/figure before it left Everything is working well with one caveat -- a figure leave event is only triggered when you enter a new figure, not simply when you leave the figure. This is because I just used the existing mpl LocationEvents to support the new events, and none of these are created when you are not over a canvas. To properly handle the figure leave events, we will need to tap into the underlying GUI leave events. The axes leave events work fine, with the one caveat that if the axes region is [0,0,1,1] you may not see the leave event for the same reason you do not see the figure leave event until you are over another canvas. JDH
John Hunter wrote: > On Wed, Nov 26, 2008 at 10:59 AM, John Hunter <jd...@gm...> wrote: > > >> probably fairly easy using a class level attr in the MouseEvent. >> Another possibility would be to add support for figure_enter_event, >> figure_leave_event, axes_enter_event, axes_leave_event. The user code >> would have to separately process the axes_leave_event but this might >> be the cleanest solution. I am happy to add support for these ... >> > > I just committed axes/figure enter/leave event notifications, with > example in examples/event_handling/figure_axes_enter_leave.py. If you > register for a leave event, you will get passed the last event that > was in your axes/figure before it left > > > Everything is working well with one caveat -- a figure leave event is > only triggered when you enter a new figure, not simply when you leave > the figure. This is because I just used the existing mpl > LocationEvents to support the new events, and none of these are > created when you are not over a canvas. To properly handle the figure > leave events, we will need to tap into the underlying GUI leave > events. The axes leave events work fine, with the one caveat that if > the axes region is [0,0,1,1] you may not see the leave event for the > same reason you do not see the figure leave event until you are over > another canvas. > > JDH > John, thank you, I'll have to check out the repository to see how I can use the changes. Just a thought: a figure_leave / figure_enter event could be triggered by comparing the currently held axis with event.inaxes (that's what Slider and my bar widget do in _update() ). Cheers, Egor
On Wed, Nov 26, 2008 at 11:57 AM, Egor Zindy <ez...@gm...> wrote: > thank you, I'll have to check out the repository to see how I can use the > changes. > > Just a thought: a figure_leave / figure_enter event could be triggered by > comparing the currently held axis with event.inaxes (that's what Slider and > my bar widget do in _update() ). That's exactly how its done. The problem I referred to above (which will not affect your use case since you are leaving the axes) occurs when you want to connect to a figure leave event and move your mouse to a non-mpl window. Then we get no events to compare to the prior. JDH
Hi John, John Hunter wrote: > On Wed, Nov 26, 2008 at 11:57 AM, Egor Zindy <ez...@gm...> wrote: > > >> thank you, I'll have to check out the repository to see how I can use the >> changes. >> >> Just a thought: a figure_leave / figure_enter event could be triggered by >> comparing the currently held axis with event.inaxes (that's what Slider and >> my bar widget do in _update() ). >> > > That's exactly how its done. The problem I referred to above (which > will not affect your use case since you are leaving the axes) occurs > when you want to connect to a figure leave event and move your mouse > to a non-mpl window. Then we get no events to compare to the prior. > > JDH > The new version attached that fixes a few of the things I was unhappy about: * radius for clicking the bar now in pixels * case when mouse pointer is outside the graph now handled correctly. * commented the methods a bit more (still working on that) Both these rely on the following two functions: #convert from x to xdata print self.ax.transData.inverted().transform_point((event.x, event.y)) #convert from xdata to x print self.ax.transData.transform_point((event.xdata, event.ydata)) event.x always returns a value, so I used that to "fix" xdata when the cursor is outside the graph Things I have struggled with (this time): * there is a toobar.set_cursor() but no toolbar.get_cursor() which would be interesting to have to set the cursor back to what it should be (rather than arbitrary POINTER type). * more cursor types would be nice (could do with size_ver and size_hor in http://wiki.openusability.org/guidelines/index.php/Design_and_Layout:Visual_Design:Cursors). * took a long time to check out matplotlib from SVN, have yet to check the new events out. * a nice name? (been struggling with that a lot lately!) Is "Bar" OK? or maybe "SelectionBar"? Regards, Egor
Jeff, Is it possible to install the basemap data into a different directory? I'm trying to set up a tool delivery layout for our users that allows me to rapidly update packages that tend to change. We have a core set of software that our code is built on and it's a lot of work for us to update that and test it on every platform. What I'm doing is separating out the packages of that core set that we generally need to update at a higher rate (currently that's matplotlib and sphinx) because they're under more active development. The largest piece (by an order of magnitude) of these active tools is the basemap data. What I'd really like to do is install the basemap data with the core set of tools and then tell basemap where it's located instead of having to redeliver this large chunk of unchanging data every time we do a bug fix update to MPL. Perhaps by setting an environment variable that takes precedence over the default location? Any thoughts? I could put a patch together for it if you think it's worthwhile. Thanks, Ted
Drain, Theodore R wrote: > Jeff, > Is it possible to install the basemap data into a different directory? > > I'm trying to set up a tool delivery layout for our users that allows me to rapidly update packages that tend to change. We have a core set of software that our code is built on and it's a lot of work for us to update that and test it on every platform. What I'm doing is separating out the packages of that core set that we generally need to update at a higher rate (currently that's matplotlib and sphinx) because they're under more active development. > > The largest piece (by an order of magnitude) of these active tools is the basemap data. What I'd really like to do is install the basemap data with the core set of tools and then tell basemap where it's located instead of having to redeliver this large chunk of unchanging data every time we do a bug fix update to MPL. Perhaps by setting an environment variable that takes precedence over the default location? > > Any thoughts? I could put a patch together for it if you think it's worthwhile. > > Thanks, > Ted > > - Ted: I've changed Basemap to look for it's data in BASEMAPDATA, and if that env var is not set to use the default location (svn revision 6646). Will that work for you? Do you also want an option in setup.py to not install the data? I'm a bit worried that this won't work in Windows - do you have Windows users? -Jeff -- Jeffrey S. Whitaker Phone : (303)497-6313 NOAA/OAR/CDC R/PSD1 FAX : (303)497-6449 325 Broadway Boulder, CO, USA 80305-3328
Nope - no windows users (yet - but we'll test it first if we ever have to deliver there). That will work perfectly - Thanks!!! Ted > -----Original Message----- > From: Jeff Whitaker [mailto:js...@fa...] > Sent: Wednesday, December 17, 2008 5:03 AM > To: Drain, Theodore R > Cc: mat...@li... > Subject: Re: [matplotlib-devel] Basemap question for Jeff: different > data directory? > > Drain, Theodore R wrote: > > Jeff, > > Is it possible to install the basemap data into a different > directory? > > > > I'm trying to set up a tool delivery layout for our users that allows > me to rapidly update packages that tend to change. We have a core set > of software that our code is built on and it's a lot of work for us to > update that and test it on every platform. What I'm doing is > separating out the packages of that core set that we generally need to > update at a higher rate (currently that's matplotlib and sphinx) > because they're under more active development. > > > > The largest piece (by an order of magnitude) of these active tools is > the basemap data. What I'd really like to do is install the basemap > data with the core set of tools and then tell basemap where it's > located instead of having to redeliver this large chunk of unchanging > data every time we do a bug fix update to MPL. Perhaps by setting an > environment variable that takes precedence over the default location? > > > > Any thoughts? I could put a patch together for it if you think it's > worthwhile. > > > > Thanks, > > Ted > > > > - > > Ted: I've changed Basemap to look for it's data in BASEMAPDATA, and if > that env var is not set to use the default location (svn revision > 6646). Will that work for you? Do you also want an option in > setup.py > to not install the data? > > I'm a bit worried that this won't work in Windows - do you have Windows > users? > > -Jeff > > -- > Jeffrey S. Whitaker Phone : (303)497-6313 > NOAA/OAR/CDC R/PSD1 FAX : (303)497-6449 > 325 Broadway Boulder, CO, USA 80305-3328