Skip to content

Git issue #7216 - Add a "ruler" tool to the plot UI - #8774

Closed
terranjp wants to merge 2 commits into
matplotlib:masterfrom
terranjp:ruler
Closed

terranjp wants to merge 2 commits into
matplotlib:masterfrom
terranjp:ruler

Conversation

@terranjp

Copy link
Copy Markdown

PR Summary

A ruler as described in issue #7216. It is heavily inspired by the ruler in ImageJ. This is my first PR to an open source project, and my first code that will be reviewed by others. I tried my best to adhere to the developer guide and the style the other widgets are written in. Feedback and constructive criticism would be much appreciated.

Issue #7216

PR Checklist

  • Has Pytest style unit tests
    I could not find tests for the other widgets and I am not sure how to go about testing it. Help would be appreciated if this addition requires tests.
  • Code is PEP 8 compliant
  • New features are documented, with examples if plot related
  • Documentation is sphinx and numpydoc compliant
  • Added an entry to doc/users/whats_new.rst if major new feature
  • Documented in doc/api/api_changes.rst if API changed in a backward-incompatible way

6

@tacaswell tacaswell added this to the 2.1 (next point release) milestone Jun 18, 2017
Comment thread lib/matplotlib/widgets.py Outdated

For the ruler to remain responsive you must keep a reference to it.

Parameters

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

When using numpydoc, you do not need to add markup to the parameter names (it is automatic) and can only use a limited number of heading names (see https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt)

Comment thread lib/matplotlib/widgets.py Outdated
x0 = self.ax.get_xlim()[0]
y0 = self.ax.get_ylim()[0]

self.ruler, = self.ax.plot([x0, x0], [y0, y0], label='ruler',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Might not want to give it a label, this will make it appear in legends by default.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I removed it. This is a leftover from when I attempted to use a legend to display the measure.

Comment thread lib/matplotlib/widgets.py
# default marker props. Don't really want to override the entire props
# if a user only gives one value.

if markerprops is not None:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

default_markerprops does not get re-used else where so you can do this is

used_markerprops = dict(...)
if markerprops is not None:
    used_markerprops.update(markerprops)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Nice. This cleans it up a bit.

Comment thread lib/matplotlib/widgets.py
else:
used_markerprops = default_markerprops

if textprops is not None:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same comment as with markerprops above.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Same as markerprops above

Comment thread lib/matplotlib/widgets.py Outdated
self.axes_text = self.ax.annotate(s='', **used_textprops)
self.ax.add_artist(self.axes_text)

self.marker_a, = self.ax.plot((x0, y0), **used_markerprops)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

These could all be one line as they all have the same marker, just use linestyle 'none'.

Comment thread lib/matplotlib/widgets.py
string will be appended to the length text annotations.

"""
AxesWidget.__init__(self, ax)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

How many of the inputs can be passed to the base-class init instead of dealing with them explicitly here?

Comment thread lib/matplotlib/widgets.py Outdated
bbox=bbox)

x0 = self.ax.get_xlim()[0]
y0 = self.ax.get_ylim()[0]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I suggest initializing to np.nan.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done.

Comment thread lib/matplotlib/widgets.py
string will be appended to the length text annotations.

"""
AxesWidget.__init__(self, ax)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

How many of the inputs can be passed to the base-class init instead of dealing with them explicitly here?

Comment thread lib/matplotlib/widgets.py
self.marker_b,
self.marker_c]

def connect_events(self):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should probably also listen for draw events to help with managing the blitting.

Comment thread lib/matplotlib/widgets.py
self.marker_b,
self.marker_c]

def connect_events(self):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should probably also listen for draw events to help with managing the blitting.

@tacaswell

Copy link
Copy Markdown
Member

I left a bunch of comments, but over all 👍 Please take many of them as questions, if there is a reason you did something that I commented on, explain why you did it that way!

This is not the simplest topic to take on as a first PR as anything involving blitting + user input has a large number of corner cases!

@terranjp

terranjp commented Jun 19, 2017 via email

Copy link
Copy Markdown
Author

@anntzer

anntzer commented Jun 20, 2017

Copy link
Copy Markdown
Contributor

I think a lot of attributes on the class could be made private. (Anything that's not private is going to become a backcompatibility nightmare at one point or another.)

Also this needs some testing, see test_widgets.py.

@terranjp

Copy link
Copy Markdown
Author

@anntzer Agreed. I will change as many as attributes as possible to private. Also thanks for the tip on test_widgets.py. I will write tests for it.

@tacaswell tacaswell modified the milestones: 2.1 (next point release), 2.2 (next next feature release) Aug 29, 2017
@tacaswell
tacaswell dismissed their stale review September 24, 2017 20:39

due to it being very old

@afvincent afvincent modified the milestones: needs sorting, v3.0 Feb 15, 2018
@jklymak

jklymak commented May 30, 2018

Copy link
Copy Markdown
Member

@terranjp Any intetrest in still pushing this through?

This looks well-written, and some care has gone into it. OTOH, I can see the use, but it seems very fiddly, and one ruler implimentation will suit some needs, but not be useful for other needs. I almost wonder if this should be made a tutorial example or a self-contained library? Edge-case tools like this may not belong in the core library?

@anntzer

anntzer commented May 31, 2018

Copy link
Copy Markdown
Contributor

I think this should be a tutorial/third-party lib.

@terranjp

Copy link
Copy Markdown
Author

@anntzer @jklymak I agree. I think it should be a third party library. It would be easier to make it a little less fiddly and easier for people to hack on it. I've been wanting to get back to it as I have some ideas I would implement and I would like to package it up correctly. A tutorial would also be helpful as it is not immediately apparent how to create something like this in matplotlib. I will try and devote some time to it.

@jklymak

jklymak commented May 31, 2018

Copy link
Copy Markdown
Member

@terranjp OK, I'll close.

If you end up with something that is rock solid that you think really has general benefit let us know.

Note that there will be a toolbar manager at some point with extensible tools so a user could easily plug in a widget like this, rather than having it be something everyone has. Keep an eye out for it - I'm not sure how we will advertise non-native plugins, but ideally there will be a list of related projects somewhere.

Thanks a lot for the contribution, and I hope you will consider adding other functionality!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants