6

I am using ax.axes('equal') to make the axis spacing equal on X and Y, and also setting xlim and ylim. This over-constrains the problem and the actual limits are not what I set in ax.set_xlim() or ax.set_ylim(). Using ax.get_xlim() just returns what I provided. How can I get the actual visible limits of the plot?

f,ax=plt.subplots(1) #open a figure
ax.axis('equal') #make the axes have equal spacing
ax.plot([0,20],[0,20]) #test data set

#change the plot axis limits
ax.set_xlim([2,18]) 
ax.set_ylim([5,15])

#read the plot axis limits
xlim2=array(ax.get_xlim())
ylim2=array(ax.get_ylim())

#define indices for drawing a rectangle with xlim2, ylim2
sqx=array([0,1,1,0,0])
sqy=array([0,0,1,1,0])

#plot a thick rectangle marking the xlim2, ylim2
ax.plot(xlim2[sqx],ylim2[sqy],lw=3) #this does not go all the way around the edge

What commands will let me draw the green box around the actual edges of the figure?

Plot showing that the results of get_xlim() and get_ylim() do not match the visible bounds of the figure

Related: Force xlim, ylim, and axes('equal') at the same time by letting margins auto-adjust

Community
  • 1
  • 1
EL_DON
  • 1,416
  • 1
  • 19
  • 34
  • I can get a notification of changed `ylim` by `ax.callbacks.connect('ylim_changed',ylim_ch)`, but it still returns the value given to `set_ylim` and not what is shown. See http://stackoverflow.com/questions/31490436/matplotlib-finding-out-xlim-and-ylim-after-zoom – EL_DON Oct 03 '16 at 16:04

2 Answers2

5

The actual limits are not known until the figure is drawn. By adding a canvas draw after setting the xlim and ylim, but before obtaining the xlim and ylim, then one can get the desired limits.

f,ax=plt.subplots(1) #open a figure
ax.axis('equal') #make the axes have equal spacing
ax.plot([0,20],[0,20]) #test data set

#change the plot axis limits
ax.set_xlim([2,18]) 
ax.set_ylim([5,15])

#Drawing is crucial 
f.canvas.draw() #<---------- I added this line

#read the plot axis limits
xlim2=array(ax.get_xlim())
ylim2=array(ax.get_ylim())

#define indices for drawing a rectangle with xlim2, ylim2
sqx=array([0,1,1,0,0])
sqy=array([0,0,1,1,0])

#plot a thick rectangle marking the xlim2, ylim2
ax.plot(xlim2[sqx],ylim2[sqy],lw=3) 

Figure produced by script

esmit
  • 1,748
  • 14
  • 27
  • Does that mean that there's an automatic f.canvas.draw() when the script terminates? – EL_DON Oct 06 '16 at 16:38
  • 1
    It means that there is a GUI loop waiting for idle time to call the `draw` method, where the idleness occurs after the script is done... – esmit Oct 06 '16 at 18:29
2

Not to detract from the accepted answer, which does solve the problem of getting updated axis limits, but is this perhaps an example of the XY problem? If what you want to do is draw a box around the axes, then you don't actually need the xlim and ylim in data coordinates. Instead, you just need to use the ax.transAxes transform which causes both x and y data to be interpreted in normalized coordinates instead of data-centered coordinates:

ax.plot([0,0,1,1,0],[0,1,1,0,0], lw=3, transform=ax.transAxes)

The great thing about this is that your line will stay around the edges of the axes even if the xlim and ylim subsequently change.

You can also use transform=ax.xaxis.get_transform() or transform=ax.yaxis.get_transform() if you want only x or only y to be defined in normalized coordinates, with the other one in data coordinates.

jez
  • 14,867
  • 5
  • 37
  • 64
  • My problem was more general than just drawing a box, so I like the accepted answer (the comments on which also clarified a misunderstanding I had about how the plot displayed), but this is great to know about. Thanks. – EL_DON Oct 14 '16 at 18:35
  • This is an amazing trick to have up your sleeve! I have been using xlim and ylim to customize labels on figures (e.g. "(a)" and "(b)"). I came across this answer trying to figure out how to do it after setting `axis('equal')`. This is so much more elegant that the approach I have been using for years. – ramzeek Sep 23 '21 at 05:13