-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Support for Scalar Image Cursor Display? #3984
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
We could also add a --- a/lib/matplotlib/axes/_base.py
+++ b/lib/matplotlib/axes/_base.py
@@ -438,6 +438,7 @@ class _AxesBase(martist.Artist):
# funcs used to format x and y - fall back on major formatters
self.fmt_xdata = None
self.fmt_ydata = None
+ self.fmt_zdata = None
self.set_cursor_props((1, 'k')) # set the cursor properties for axes
@@ -2971,7 +2972,13 @@ class _AxesBase(martist.Artist):
ys = '???'
else:
ys = self.format_ydata(y)
- return 'x=%s y=%s' % (xs, ys)
+ try:
+ z = self.images[0].get_array()[int(y + 0.5), int(x + 0.5)]
+ formatter = self.fmt_zdata or self.format_xdata
+ zs = formatter(z)
+ return 'x=%s y=%s z=%s' % (xs, ys, zs)
+ except:
+ return 'x=%s y=%s' % (xs, ys)
def minorticks_on(self): |
You need to also take the |
Yeah, it is a lot more complicated than that. I made an exa!mole demoing it
|
I should note that my example was partly based off of the functionality
|
I hear this complaint from everyone I introduce to Python, so I'd really like to nail this down. |
Hack so far based on mpldatacursor: --- a/lib/matplotlib/axes/_base.py
+++ b/lib/matplotlib/axes/_base.py
@@ -438,6 +438,7 @@ class _AxesBase(martist.Artist):
# funcs used to format x and y - fall back on major formatters
self.fmt_xdata = None
self.fmt_ydata = None
+ self.fmt_zdata = None
self.set_cursor_props((1, 'k')) # set the cursor properties for axes
@@ -2961,6 +2962,17 @@ class _AxesBase(martist.Artist):
val = func(y)
return val
+ def _get_z_coord(self, x, y):
+ im = self.images[0]
+ xmin, xmax, ymin, ymax = im.get_extent()
+ if im.origin == 'upper':
+ ymin, ymax = ymax, ymin
+ data_extent = mtransforms.Bbox([[ymin, xmin], [ymax, xmax]])
+ array_extent = mtransforms.Bbox([[0, 0], im.get_array().shape[:2]])
+ trans = mtransforms.BboxTransformFrom(data_extent) +\
+ mtransforms.BboxTransformTo(array_extent)
+ return trans.transform_point([y,x]).astype(int)
+
def format_coord(self, x, y):
"""Return a format string formatting the *x*, *y* coord"""
if x is None:
@@ -2971,7 +2983,14 @@ class _AxesBase(martist.Artist):
ys = '???'
else:
ys = self.format_ydata(y)
- return 'x=%s y=%s' % (xs, ys)
+ try:
+ i, j = self._get_z_coord(x, y)
+ z = self.images[0].get_array()[i, j]
+ formatter = self.fmt_zdata or self.format_xdata
+ zs = formatter(z)
+ return 'x=%s y=%s z=%s' % (xs, ys, zs)
+ except:
+ return 'x=%s y=%s' % (xs, ys)
def minorticks_on(self):
'Add autoscaling minor ticks to the axes.' |
With --- a/lib/matplotlib/axes/_base.py
+++ b/lib/matplotlib/axes/_base.py
@@ -438,6 +438,7 @@ class _AxesBase(martist.Artist):
# funcs used to format x and y - fall back on major formatters
self.fmt_xdata = None
self.fmt_ydata = None
+ self.fmt_zdata = None
self.set_cursor_props((1, 'k')) # set the cursor properties for axes
@@ -2961,6 +2962,16 @@ class _AxesBase(martist.Artist):
val = func(y)
return val
+ def _get_z_coord(self, im, x, y):
+ xmin, xmax, ymin, ymax = im.get_extent()
+ if im.origin == 'upper':
+ ymin, ymax = ymax, ymin
+ data_extent = mtransforms.Bbox([[ymin, xmin], [ymax, xmax]])
+ array_extent = mtransforms.Bbox([[0, 0], im.get_array().shape[:2]])
+ trans = mtransforms.BboxTransformFrom(data_extent) +\
+ mtransforms.BboxTransformTo(array_extent)
+ return trans.transform_point([y,x]).astype(int)
+
def format_coord(self, x, y):
"""Return a format string formatting the *x*, *y* coord"""
if x is None:
@@ -2971,7 +2982,15 @@ class _AxesBase(martist.Artist):
ys = '???'
else:
ys = self.format_ydata(y)
- return 'x=%s y=%s' % (xs, ys)
+ try:
+ im = self.images[np.argmax([im.zorder for im in self.images])]
+ i, j = self._get_z_coord(im, x, y)
+ z = im.get_array()[i, j]
+ formatter = self.fmt_zdata or self.format_xdata
+ zs = formatter(z)
+ return 'x=%s y=%s z=%s' % (xs, ys, zs)
+ except:
+ return 'x=%s y=%s' % (xs, ys)
def minorticks_on(self):
'Add autoscaling minor ticks to the axes.' |
All artists have picker logic, you could probably hi-jack that to sort out which, if any of the you are in and then take the top one of that sub-set. This may be why no one has done this yet (obnoxious demanding devs) ;) |
So basically all of mpldatacursor then... |
The problem I see is that we don't have an event, which is needed by the picking logic and is used extensively by mpldatacursor. |
We should have a mouse motion event (as that is what is updating the On Thu Jan 08 2015 at 10:14:31 PM Steven Silvester [email protected]
|
So we'd need to plug in here:
|
Get the |
Shucks, there is no |
Here's what I see: |
This should handle #3416, but all we know about is |
@tacaswell, what |
Here's what I've got so far: --- a/lib/matplotlib/backend_bases.py
+++ b/lib/matplotlib/backend_bases.py
@@ -2791,6 +2791,13 @@ class NavigationToolbar2(object):
except (ValueError, OverflowError):
pass
else:
+ ax = event.inaxes
+ artists = ax.artists + ax.images + ax.lines
+ artists = [a for a in artists if a.contains(event)[0]]
+ if artists:
+ artist = artists[np.argmax([a.zorder for a in artists])]
+ print(artist)
+
if len(self.mode):
self.set_message('%s, %s' % (self.mode, s))
else: |
You guys are just trying to obsolete my book before it comes out, aren't
|
Here's a working example that handles just images: --- a/lib/matplotlib/backend_bases.py
+++ b/lib/matplotlib/backend_bases.py
@@ -51,6 +51,7 @@ from matplotlib import get_backend
from matplotlib._pylab_helpers import Gcf
from matplotlib.transforms import Bbox, TransformedBbox, Affine2D
+import matplotlib.transforms as mtransforms
import matplotlib.tight_bbox as tight_bbox
import matplotlib.textpath as textpath
@@ -2781,6 +2782,26 @@ class NavigationToolbar2(object):
self._lastCursor = cursors.MOVE
+ def _get_z_data(self, event):
+ images = event.inaxes.images
+ images = [im for im in images if im.contains(event)[0]]
+ if not images:
+ return ''
+ im = images[np.argmax([i.zorder for i in images])]
+ xmin, xmax, ymin, ymax = im.get_extent()
+ if im.origin == 'upper':
+ ymin, ymax = ymax, ymin
+ data_extent = mtransforms.Bbox([[ymin, xmin], [ymax, xmax]])
+ array_extent = mtransforms.Bbox([[0, 0], im.get_array().shape[:2]])
+ trans = mtransforms.BboxTransformFrom(data_extent) +\
+ mtransforms.BboxTransformTo(array_extent)
+ i, j = trans.transform_point([event.ydata, event.xdata]).astype(int)
+ z = im.get_array()[i, j]
+ if z.size > 1:
+ # Override default numpy formatting for this specific case. Bad idea?
+ z = ', '.join('{:0.3g}'.format(item) for item in z)
+ return 'z=%s' % z
+
def mouse_move(self, event):
self._set_cursor(event)
@@ -2791,6 +2812,7 @@ class NavigationToolbar2(object):
except (ValueError, OverflowError):
pass
else:
+ s += self._get_z_data(event)
if len(self.mode):
self.set_message('%s, %s' % (self.mode, s))
else: |
@blink1073 The proper OO way would be to add a no-op function to |
I sense a PR coming... |
What if there are multiple images though? Don't we still need a zorder arbiter? |
Yeah, it should probably figure out which of the artists it is over and then only ask the top artist to add an entry to the message. This will also help arbitrate when we have multiple crossing lines or lines on top of images, etc. |
I think we should still check for |
Ah, I see that is what you already said... |
How would everyone feel about the following change? It will show the
z
value of a scalar image in the cursor if available (similar to what Matlab does). If there is no image or the image is RGB, it will revert to the previous behaviour.The text was updated successfully, but these errors were encountered: