Thanks to visit codestin.com
Credit goes to github.com

Skip to content

MultiCursor with additionnal optionnal horizontal bar #1811

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

Merged
merged 3 commits into from
Mar 11, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions doc/api/api_changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ Changes in 1.3.x
* The `~matplotlib.mpl` module is now deprecated. Those who relied on this
module should transition to simply using `import matplotlib as mpl`.

* The extension of :class:`~matplotlib.widgets.MultiCursor` to both vertical
(default) and/or horizontal cursor implied that ``self.line`` is replaced
by ``self.vline`` for vertical cursors lines and ``self.hline`` is added
for the horizontal cursors lines.

Changes in 1.2.x
================

Expand Down
63 changes: 45 additions & 18 deletions lib/matplotlib/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from transforms import blended_transform_factory
from matplotlib import MatplotlibDeprecationWarning as mplDeprecation


class LockDraw:
"""
Some widgets, like the cursor, draw onto the canvas, and this is not
Expand Down Expand Up @@ -829,7 +830,8 @@ class Cursor(AxesWidget):

and the visibility of the cursor itself with the *visible* attribute
"""
def __init__(self, ax, horizOn=True, vertOn=True, useblit=False, **lineprops):
def __init__(self, ax, horizOn=True, vertOn=True, useblit=False,
**lineprops):
"""
Add a cursor to *ax*. If ``useblit=True``, use the backend-
dependent blitting features for faster updates (GTKAgg
Expand Down Expand Up @@ -907,7 +909,8 @@ def _update(self):

class MultiCursor(Widget):
"""
Provide a vertical line cursor shared between multiple axes
Provide a vertical (default) and/or horizontal line cursor shared between
multiple axes

Example usage::

Expand All @@ -925,16 +928,23 @@ class MultiCursor(Widget):
ax2 = fig.add_subplot(212, sharex=ax1)
ax2.plot(t, s2)

multi = MultiCursor(fig.canvas, (ax1, ax2), color='r', lw=1)
multi = MultiCursor(fig.canvas, (ax1, ax2), color='r', lw=1,
horizOn=False, vertOn=True)
show()

"""
def __init__(self, canvas, axes, useblit=True, **lineprops):
def __init__(self, canvas, axes, useblit=True, horizOn=False, vertOn=True,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of the horizOn and vertOn keywords - is the casing for consistency reasons with other signatures? If not, I'd be happier with horiz_lines & vert_lines or even just horizontal and vertical...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To answer myself - horizOn and vertOn are consistent with Cursor. Scrap my comment.

**lineprops):

self.canvas = canvas
self.axes = axes
self.horizOn = horizOn
self.vertOn = vertOn

xmin, xmax = axes[-1].get_xlim()
ymin, ymax = axes[-1].get_ylim()
xmid = 0.5 * (xmin + xmax)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Obviously this breaks backwards compatibility with self.lines - I don't think that's a problem, but it should be documented in doc/api/api_changes.rst. I wonder if it's worth providing a property which returns the union of self.vlines and self.hlines - on second thoughts, that doesn't really help if users want to do self.lines.append(...) so scrap the property idea.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see...
May we keep self.lines pointing to self.vlines ?
Or replace all occurrence of self.vlines by self.lines
Both solution prevent api changes...
What would you prefer ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can just document the change. I think this is a small change which I can't see having a major impact to many codes - I'd sooner have the consistency of what you have implemented, than the inconsistency that not breaking compatibility would bring.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. A new commit document this small API change in doc/api/api_changes.rst

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For backwards compatibility, why don't we set up a property "self.lines" that would point to self.vlines, but puts out a deprecation message?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. That works, nice idea.

ymid = 0.5 * (ymin + ymax)

self.visible = True
self.useblit = useblit and self.canvas.supports_blit
Expand All @@ -943,18 +953,28 @@ def __init__(self, canvas, axes, useblit=True, **lineprops):

if useblit:
lineprops['animated'] = True
self.lines = [ax.axvline(xmid, visible=False, **lineprops)
for ax in axes]

if vertOn:
self.vlines = [ax.axvline(xmid, visible=False, **lineprops)
for ax in axes]
else:
self.vlines = []

if horizOn:
self.hlines = [ax.axhline(ymid, visible=False, **lineprops)
for ax in axes]
else:
self.hlines = []

self.canvas.mpl_connect('motion_notify_event', self.onmove)
self.canvas.mpl_connect('draw_event', self.clear)

def clear(self, event):
"""clear the cursor"""
if self.useblit:
self.background = self.canvas.copy_from_bbox(
self.canvas.figure.bbox)
for line in self.lines:
self.background = (
self.canvas.copy_from_bbox(self.canvas.figure.bbox))
for line in self.vlines + self.hlines:
line.set_visible(False)

def onmove(self, event):
Expand All @@ -965,19 +985,26 @@ def onmove(self, event):
self.needclear = True
if not self.visible:
return

for line in self.lines:
line.set_xdata((event.xdata, event.xdata))
line.set_visible(self.visible)
if self.vertOn:
for line in self.vlines:
line.set_xdata((event.xdata, event.xdata))
line.set_visible(self.visible)
if self.horizOn:
for line in self.hlines:
line.set_ydata((event.ydata, event.ydata))
line.set_visible(self.visible)
self._update()

def _update(self):

if self.useblit:
if self.background is not None:
self.canvas.restore_region(self.background)
for ax, line in zip(self.axes, self.lines):
ax.draw_artist(line)
if self.vertOn:
for ax, line in zip(self.axes, self.vlines):
ax.draw_artist(line)
if self.horizOn:
for ax, line in zip(self.axes, self.hlines):
ax.draw_artist(line)
self.canvas.blit(self.canvas.figure.bbox)
else:

Expand Down Expand Up @@ -1349,7 +1376,7 @@ def ignore(self, event):
# boundaries.
if event.button == self.eventpress.button and event.inaxes != self.ax:
(xdata, ydata) = self.ax.transData.inverted().transform_point(
(event.x, event.y))
(event.x, event.y))
x0, x1 = self.ax.get_xbound()
y0, y1 = self.ax.get_ybound()
xdata = max(x0, xdata)
Expand Down Expand Up @@ -1407,7 +1434,7 @@ def release(self, event):
yproblems = self.minspany is not None and spany < self.minspany

if (((self.drawtype == 'box') or (self.drawtype == 'line')) and
(xproblems or yproblems)):
(xproblems or yproblems)):
# check if drawn distance (if it exists) is not too small in
# neither x nor y-direction
return
Expand Down