Skip to content

ENH: make mouse over behavior configurable #4847

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

Closed
wants to merge 1 commit into from

Conversation

tacaswell
Copy link
Member

The mouse-over behavior to add a string to the message line
can quickly become too expensive to compute the hit list
in the time between mouse move events (particularly in the nbagg
and qt5agg backends) when the number of artists gets large.

This adds:

  • a flag on the Axes objects to control if the hitlist should
    be computed
  • an rcparam to control it

@blink1073

import matplotlib.backend_bases as mbb

fig, ax = plt.subplots()
for j in range(150):
    ax.plot(j + np.sin(np.arange(0, 5000)))

# fake mouse event
ev = mbb.MouseEvent('motion_notify_event', fig.canvas, 150, 150)
ev.inaxes = ax

ax.mouseover = True
%prun ax.figure.canvas.manager.toolbar.mouse_move(ev)
         17104 function calls (16654 primitive calls) in 0.038 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      166    0.017    0.000    0.018    0.000 lines.py:36(segment_hits)
      198    0.003    0.000    0.033    0.000 lines.py:384(contains)
      229    0.002    0.000    0.002    0.000 {built-in method affine_transform}
      198    0.002    0.000    0.003    0.000 path.py:212(_update_values)
  323/171    0.001    0.000    0.003    0.000 transforms.py:2352(get_affine)
    261/1    0.001    0.000    0.037    0.037 artist.py:333(hitlist)
      202    0.001    0.000    0.001    0.000 {method 'reduce' of 'numpy.ufunc' objects}
      408    0.001    0.000    0.001    0.000 weakref.py:101(__init__)
      396    0.001    0.000    0.002    0.000 numeric.py:2428(seterr)
      396    0.001    0.000    0.001    0.000 numeric.py:2524(geterr)
      327    0.001    0.000    0.001    0.000 {built-in method array}
      198    0.001    0.000    0.004    0.000 path.py:103(__init__)
      337    0.000    0.000    0.000    0.000 {built-in method dot}
      408    0.000    0.000    0.001    0.000 transforms.py:86(__init__)
      198    0.000    0.000    0.007    0.000 transforms.py:1660(transform_path_affine)
      353    0.000    0.000    0.002    0.000 transforms.py:1768(__init__)
      412    0.000    0.000    0.000    0.000 __init__.py:879(__getitem__)
      170    0.000    0.000    0.000    0.000 {built-in method concatenate}
      371    0.000    0.000    0.001    0.000 transforms.py:1622(__init__)
      364    0.000    0.000    0.000    0.000 {method 'nonzero' of 'numpy.ndarray' objects}
        1    0.000    0.000    0.000    0.000 {built-in method showMessage}
      219    0.000    0.000    0.002    0.000 transforms.py:1723(transform_affine)
      524    0.000    0.000    0.000    0.000 {built-in method isinstance}
      408    0.000    0.000    0.000    0.000 weakref.py:255(update)
      396    0.000    0.000    0.000    0.000 {built-in method seterrobj}
      248    0.000    0.000    0.001    0.000 numeric.py:394(asarray)
      198    0.000    0.000    0.000    0.000 transforms.py:2662(_revalidate)
      198    0.000    0.000    0.003    0.000 transforms.py:2687(get_transformed_path_and_affine)
     1246    0.000    0.000    0.000    0.000 {built-in method len}
       23    0.000    0.000    0.002    0.000 text.py:917(get_window_extent)
       39    0.000    0.000    0.002    0.000 text.py:251(contains)
       16    0.000    0.000    0.000    0.000 transforms.py:692(translated)
      332    0.000    0.000    0.000    0.000 {method 'ravel' of 'numpy.ndarray' objects}
      198    0.000    0.000    0.003    0.000 transforms.py:2703(get_affine)
      792    0.000    0.000    0.000    0.000 {built-in method geterrobj}
      437    0.000    0.000    0.000    0.000 transforms.py:1813(get_matrix)
      167    0.000    0.000    0.000    0.000 transforms.py:2527(get_matrix)
      400    0.000    0.000    0.000    0.000 path.py:221(vertices)
      202    0.000    0.000    0.001    0.000 {method 'all' of 'numpy.ndarray' objects}
      202    0.000    0.000    0.001    0.000 _methods.py:40(_all)
  186/170    0.000    0.000    0.000    0.000 transforms.py:2313(_get_is_affine)
       25    0.000    0.000    0.000    0.000 transforms.py:401(_get_bounds)
      198    0.000    0.000    0.007    0.000 transforms.py:1656(transform_path)
       25    0.000    0.000    0.000    0.000 transforms.py:779(__init__)
      162    0.000    0.000    0.000    0.000 transforms.py:2209(get_matrix)
       16    0.000    0.000    0.000    0.000 font_manager.py:786(get_size_in_points)
      198    0.000    0.000    0.000    0.000 core.py:5776(isMaskedArray)
      198    0.000    0.000    0.000    0.000 lines.py:663(_get_transformed_path)
        4    0.000    0.000    0.000    0.000 linalg.py:455(inv)
       39    0.000    0.000    0.000    0.000 text.py:880(get_position)
       21    0.000    0.000    0.001    0.000 transforms.py:1292(transform)
       42    0.000    0.000    0.000    0.000 weakref.py:149(__setitem__)
        2    0.000    0.000    0.000    0.000 patches.py:653(_update_patch_transform)
       16    0.000    0.000    0.000    0.000 font_manager.py:701(__hash__)
       16    0.000    0.000    0.000    0.000 text.py:886(get_prop_tup)
       49    0.000    0.000    0.000    0.000 {method 'copy' of 'numpy.ndarray' objects}
       16    0.000    0.000    0.000    0.000 text.py:327(_get_layout)
      242    0.000    0.000    0.000    0.000 artist.py:351(get_children)
      202    0.000    0.000    0.000    0.000 path.py:235(codes)
      198    0.000    0.000    0.000    0.000 cbook.py:753(is_numlike)
       21    0.000    0.000    0.001    0.000 transforms.py:1388(transform_point)
      214    0.000    0.000    0.000    0.000 figure.py:399(_get_dpi)
       22    0.000    0.000    0.000    0.000 transforms.py:162(set_children)
       21    0.000    0.000    0.000    0.000 transforms.py:2328(transform_affine)
        4    0.000    0.000    0.000    0.000 {built-in method point_in_path}
        2    0.000    0.000    0.001    0.000 patches.py:673(contains)

vs

ax.mouseover = False
%prun ax.figure.canvas.manager.toolbar.mouse_move(ev)
         13 function calls in 0.000 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 {method 'emit' of 'PyQt5.QtCore.pyqtBoundSignal' objects}
        1    0.000    0.000    0.000    0.000 {built-in method exec}
        1    0.000    0.000    0.000    0.000 {built-in method statusBar}
        1    0.000    0.000    0.000    0.000 backend_bases.py:2805(mouse_move)
        1    0.000    0.000    0.000    0.000 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 backend_qt5.py:494(_show_message)
        1    0.000    0.000    0.000    0.000 backend_bases.py:2789(_set_cursor)
        1    0.000    0.000    0.000    0.000 backend_qt5.py:664(set_message)
        1    0.000    0.000    0.000    0.000 {built-in method showMessage}
        1    0.000    0.000    0.000    0.000 _base.py:3070(format_coord)
        1    0.000    0.000    0.000    0.000 _base.py:3110(get_navigate)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 {built-in method len}

These are on a newish i7 desktop, I was getting a ms/Line2D object on a 1.5 year old mobile i5 with power saving turned up to 11.

The mouse-over behavior to add a string to the message line
can quickly become too expensive to compute the hit list
in the time between mouse move events (particularly in the nbagg
and qt5agg backends) when the number of artists gets large.

This adds:

 - a flag on the Axes objects to control if the hitlist should
   be computed
 - an rcparam to control it
@tacaswell tacaswell added this to the next point release milestone Aug 1, 2015
@tacaswell
Copy link
Member Author

Closes #4767

which can be used to add zdata to the cursor display
in the status bar. Also adds an implementation for Images.

Added an property attribute ``mouseover`` and rcParam
Copy link
Member

Choose a reason for hiding this comment

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

I was a bit confused by this, I think... This PR is really just an optimization around a new feature that hasn't been released yet, right?

Then maybe instead of "Added an attribute..." say "This feature requires that the axes.mouseover attribute is set to True..." etc.

Barring that, I'm 👍 on this PR.

@blink1073
Copy link
Member

Might it make more sense to always show it when the number of artists is small, and have this parameter which allows one to force it to show for a larger number of artists? Another alternative is to make this a property of the Artist, where all Artists but Images default to False.

My concern is that the reason I added this is my new-user colleagues were turned off by the lack of mouse-over display on images, and these folks are not going to know to enable this setting.

@tacaswell
Copy link
Member Author

How about adding a property to the artists that the axes checks on addition to allow the Axes to keep a set of mouseable artists?

@blink1073
Copy link
Member

That works for me.

@tacaswell
Copy link
Member Author

@blink1073 You want to do it or should I?

@blink1073
Copy link
Member

My todo list is quite large at the moment...

@tacaswell
Copy link
Member Author

It was worth a try! Hopefully I will get to it tonight.

@tacaswell
Copy link
Member Author

closing in favor of #4878

@tacaswell tacaswell closed this Aug 10, 2015
@tacaswell tacaswell deleted the enh_add_mouseover_rc branch August 10, 2015 13:06
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.

3 participants