Skip to content
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

ConnectionPatch hidden by plots #8744

Closed
yha opened this issue Jun 10, 2017 · 15 comments · Fixed by #15020
Closed

ConnectionPatch hidden by plots #8744

yha opened this issue Jun 10, 2017 · 15 comments · Fixed by #15020
Milestone

Comments

@yha
Copy link

yha commented Jun 10, 2017

A ConnectionPatch is sometimes not shown on one of the plots, depending on the position of the axesA and axesB subplots.

A reproducing example, with only minor changes to the ConnnectionPatch example from the docs:

from matplotlib.patches import ConnectionPatch
import matplotlib.pyplot as plt

fig, (ax1, ax2) = plt.subplots(1,2)

xy = (0.3, 0.2)
coordsA = "data"
coordsB = "data"
con = ConnectionPatch(xyA=xy, xyB=xy, coordsA=coordsA, coordsB=coordsB,
                      axesA=ax1, axesB=ax2,
                      arrowstyle="->", shrinkB=5)
ax1.add_artist(con)

# This works fine:
#con = ConnectionPatch(xyA=xy, xyB=xy, coordsA=coordsA, coordsB=coordsB,
#                      axesA=ax2, axesB=ax1,
#                      arrowstyle="->", shrinkB=5)
#ax2.add_artist(con)

ax1.set_xlim(0, 1)
ax1.set_ylim(0, 1)
ax2.set_xlim(0, .5)
ax2.set_ylim(0, .5)

plt.show()

This produces:

connectionpatch

Tested on matplotlib 2.0.2 (from Anaconda 4.4.0) on Debian.

@tacaswell
Copy link
Member

This is actually the expected behavior, the second axes (ax2) is drawn after ax1 and hence over-draws it. To fix this you can:

  • set the zorder (which controls the draw order) of either axes to make ax2 draw second ax1.set_zorder(-1) or ax2.set_zorder(1)
  • add the connector to the second axes (as the example did)
  • make the background of the second axes undrawn (ax2.set_facecolor('none'))

@tacaswell tacaswell added this to the 2.2 (next next feature release) milestone Jun 12, 2017
@tacaswell
Copy link
Member

The details of z-order at both the Axes and Figure level should be clearly documented.

@anntzer
Copy link
Contributor

anntzer commented Jun 12, 2017

Could an artist such as ConnectionPatch be added to the figure rather than to the axes (and if it can, is there a mechanism to let such artists be drawn last)? This is where it really belongs after all.

@yha
Copy link
Author

yha commented Jun 13, 2017

I think it is also surprising (and does not seem to be explicitly documented) that the artist must be added to the axes passed as axesA rather than axesB, otherwise the ConnectionPatch is not drawn at all.
Perhaps a better interface would be something like
ax.add_connection( xy, coords, target, xyTarget, coordsTarget, <...>)
or a similar method on the figure.

@tacaswell
Copy link
Member

It probably should live on the Figure (and there is even an artists list on Figure), but the tooling around that is sparse (and we probably should not document fig.artists.append(...) even if that would work).

@yrevar
Copy link

yrevar commented Aug 9, 2019

It'd be a nice to have such a feature. Is there any progress on this? Did anyone find any workaround @anntzer @tacaswell @yha ?

@jklymak
Copy link
Member

jklymak commented Aug 9, 2019

Fig.add_artist doesn’t work?

@yrevar
Copy link

yrevar commented Aug 9, 2019

Thanks @jklymak , do you mean fig.add_artist(con) instead of ax1.add_artist(con)?

@yrevar
Copy link

yrevar commented Aug 9, 2019

I just tried that syntax, it fails with error below.

Error in callback <function install_repl_displayhook.<locals>.post_execute at 0x11319b840> (for post_execute):
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
~/anaconda3/envs/irl/lib/python3.7/site-packages/matplotlib/pyplot.py in post_execute()
    107             def post_execute():
    108                 if matplotlib.is_interactive():
--> 109                     draw_all()
    110 
    111             # IPython >= 2

~/anaconda3/envs/irl/lib/python3.7/site-packages/matplotlib/_pylab_helpers.py in draw_all(cls, force)
    130         for f_mgr in cls.get_all_fig_managers():
    131             if force or f_mgr.canvas.figure.stale:
--> 132                 f_mgr.canvas.draw_idle()
    133 
    134 atexit.register(Gcf.destroy_all)

~/anaconda3/envs/irl/lib/python3.7/site-packages/matplotlib/backend_bases.py in draw_idle(self, *args, **kwargs)
   1897         if not self._is_idle_drawing:
   1898             with self._idle_draw_cntx():
-> 1899                 self.draw(*args, **kwargs)
   1900 
   1901     def draw_cursor(self, event):

~/anaconda3/envs/irl/lib/python3.7/site-packages/matplotlib/backends/backend_agg.py in draw(self)
    400         toolbar = self.toolbar
    401         try:
--> 402             self.figure.draw(self.renderer)
    403             # A GUI class may be need to update a window using this draw, so
    404             # don't forget to call the superclass.

~/anaconda3/envs/irl/lib/python3.7/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     48                 renderer.start_filter()
     49 
---> 50             return draw(artist, renderer, *args, **kwargs)
     51         finally:
     52             if artist.get_agg_filter() is not None:

~/anaconda3/envs/irl/lib/python3.7/site-packages/matplotlib/figure.py in draw(self, renderer)
   1647 
   1648             mimage._draw_list_compositing_images(
-> 1649                 renderer, self, artists, self.suppressComposite)
   1650 
   1651             renderer.close_group('figure')

~/anaconda3/envs/irl/lib/python3.7/site-packages/matplotlib/image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    136     if not_composite or not has_images:
    137         for a in artists:
--> 138             a.draw(renderer)
    139     else:
    140         # Composite any adjacent images together

~/anaconda3/envs/irl/lib/python3.7/site-packages/matplotlib/patches.py in draw(self, renderer)
   4610         if renderer is not None:
   4611             self._renderer = renderer
-> 4612         if not self.get_visible() or not self._check_xy(renderer):
   4613             return
   4614         FancyArrowPatch.draw(self, renderer)

~/anaconda3/envs/irl/lib/python3.7/site-packages/matplotlib/patches.py in _check_xy(self, renderer)
   4592             x, y = self.xy1
   4593             xy_pixel = self._get_xy(x, y, self.coords1, self.axesA)
-> 4594             if not self.axes.contains_point(xy_pixel):
   4595                 return False
   4596 

AttributeError: 'NoneType' object has no attribute 'contains_point'

@ImportanceOfBeingErnest
Copy link
Member

Yeah, it's not working yet. But that would be the plan to have it working soon.

@yrevar
Copy link

yrevar commented Aug 9, 2019

Oh okay, that'd be cool!
I'm trying to draw trajectories over axis, it should be possible but would be too cumbersome to handle cases like below with connectionpatch's ordering constraints on the axes. Might need to keep flipping axes as per the ordering constraints maybe.

gridspec_connectionpatch

@ImportanceOfBeingErnest
Copy link
Member

Actually, I think the logic in

def _check_xy(self, renderer):
is wrong anyways. So @yrevar, your image would be a good test case; if you can condense it to a minimal, self-contained example it would help us.

@yrevar
Copy link

yrevar commented Aug 9, 2019

@yrevar
Copy link

yrevar commented Aug 9, 2019

I just found a workaround!
I was using two connection patches to deal with it earlier when I was using lines instead of arrows. It just struck me that the same concept could also be used for drawing arrows, in the reverse direction (i.e., ax2->ax1) we can use "reversed arrow"!!
The limitations is that shrinkB and shrinkA needs to be equal or we'll see weird overlapping arrow heads, but it's okay for my use case.

@ImportanceOfBeingErnest
Copy link
Member

Thanks for the example @yrevar
It would be working as it is just by replacing fig.add_artist(con) with #15020. Meanwhile, the workaround you found seems reasonable.

@QuLogic QuLogic modified the milestones: needs sorting, v3.1.2 Aug 10, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants