I have a matplotlib axes instance inside which I'm animating an AxesImage via blitting.
What I'd like to do is animate the ticks on the x-axis as well.
I am updating the data on the AxesImage (and subsequently) drawing its artist quite frequently, and on each update I'd like to move one extra tick placed to highlight the position of something.
This is what I'm doing right now:
axis = axes.get_xaxis
im.set_data(new_data)
axis.set_ticks([10,20,30,x,t])
axis.set_ticklabels(["p", "u", "z", "z", "i"])
axes.draw_artist(im)
axes.draw_artist(axis)
While I see the ticks correctly updating, the labels are not. I think that the axes bbox does not include the axes, is this possible? If so, how can I animate it? Should I copy and restore from somewhere else?
The axes bbox doesn't include anything outside of the "inside" of the axes (e.g. it doesn't include the tick labels, title, etc.)
One quick way around this is to just grab the entire region of the figure when you're blitting. (E.g. background = canvas.copy_from_bbox(fig.bbox))
This can cause problems if you have multiple subplots and only want to animate one of them. In that case, you can do something along the lines of background = canvas.copy_from_bbox(ax.bbox.expanded(1.1, 1.2)). You'll have to guesstimate the ratios you need, though.
If you need the exact extent of the tick labels, it's a bit trickier. The easiest way is to iterate through the ticklabel objects and get the union with ax.bbox. You can make this a one-liner: ax.bbox.union([label.get_window_extent() for label in ax.get_xticklabels()]).
At any rate, one of those three options should do what you need, I think.
Related
I plotted a figure as the attached picture.
There are 3 labels in the legend.
As you can see, the label on the second row is quite long, so I'd like to set the right of “BS” label align with the end of "Farinotti et al. (2019)" label.
How should I do this?
Basically, you're thinking "these labels form a 2x2 grid, and I want the bottom row to span 2 columns"?
To be honest, I don't think that's necessary; to my eyes your legend looks fine. This won't save as much white space, but you could save some by just swapping the positions of "BS" and "Farinotti". Saving more white space than that would be inconsequential here. You can have a look at this other answer for tips on how to flip the label positions:
Matplotlib legend, add items across columns instead of down
But if you want to go down the path of creating column-spanning labels anyway, for a more extreme case or out of curiosity, what you want to do is define your own legend handler. Required reading:
https://matplotlib.org/1.3.1/users/legend_guide.html
https://matplotlib.org/3.1.0/api/legend_handler_api.html
Or here's a somewhat hacky answer I found that combines 2 legends into 1, which should give you the effect that you're looking for.
pyplot: change ncols within a legend
I'm making a plotting app which should, through a tkinter gui, allow the user to create plots to graph spectral data, and modify the plots.
I'm trying to make it so the user can change the limits of the x and y axes after data has been plotted. Looking through the mpl docs, it seems like the obvious solution is to use the set_xlim and set_ylim methods. However, if these methods are invoked after a line is plotted, the line can no longer be seen, and there are no intermediate axis ticks. Through my other widgets, I know the lines are still contained in the lines attribute of the axis object, they just aren't visible.
My question is, is there a way to 'set' the the axes after a line has been plotted, so that the data will adjust to it, i.e be visible after adjusting the axes, and have ticks like it did before being adjusted? I'll show images of the graph to illustrate my situation. The class definitions are rather long, so I'll just show the use of set_xlim and set_ylim
The plot before changing the axis limits:
The plot after changing the x limits:
if all(entry.get() for entry in {self.lxlimEntry, self.rxlimEntry}):
self.master.controller.get_plots()[self.plotVar.get()].axes[0].set_xlim(self.lxlimEntry.get(), self.rxlimEntry.get())
if all(entry.get() for entry in {self.lylimEntry, self.rylimEntry}):
self.master.controller.get_plots()[self.plotVar.get()].axes[0].set_ylim(self.lylimEntry.get(), self.rylimEntry.get())
The Entry widgets are contained in a Toplevel, instantiated by a page of the application (master), and the Entries are where the user inputs the new x and y limits. The controller is the App backend.
Here is part of my code, which exports figures with tight layout.
...
fig.set_size_inches(8,6.8)
fig.tight_layout(rect=(0, 0, 1, 0.9))
# fig.savefig(path,bbox_inches='tight',dpi=100)
fig.savefig(path,dpi=100)
plt.gcf().clear()
plt.close(fig)
...
Unfortunately when I looping over different frames and y axis labels are changing, there are frames when ticklabels getting very close to the edge and then entire plot is shrinked as shown in figures between red and green lines.
https://dl.dropboxusercontent.com/u/248943005/Montage.png
I tried several cases, but this effect is always coming up in one or another way. It seems like it would be good to make tight layout somehow independent of ticklabels. Is that possible? If no, are there alternatives?
Is there a command to set the length of an axis? I do not mean the range! Independently from the values, the range from the axis or other factors, I want to set its length. How can I do that?
Something like plt.yaxislenght(20)?
I'm not sure of a specific way to set an axis length of axes generated by e.g. plt.subplots. You can use ax.set_aspect(num), but this adjusts the aspect ratio, and therefore will change both axes in a dependent way.
You can however use ax = plt.axes([left,bottom,width,height]) to add individual subplots in whatever positions you like. This should allow you to achieve what you want, but will be tedious because you need to place each subplot manually.
What you want to do is tricky due to the way that mpl works underneath. Most of the artist are specified in units that are not on-screen units (data, axes, or figure space: see transfrom tutorial). This gives you a good deal of power/flexibility as most of the time you want to work in one of the relative sets of coordinates, however the cost is if you want to set 'absolute' sizes of things you end up having to do it indirectly.
If you want you axis to be a fixed length (in display units) between figures, then you need to control the size of you axes (in figure units) by hand (via fig.add_axes) and then use fig.set_size_inches to set the size of your over-all figure. By tweaking these values you can get what you want.
I am trying to minimize margins around a 1X2 figure, a figure which are two stacked subplots. I searched a lot and came up with commands like:
self.figure.subplots_adjust(left=0.01, bottom=0.01, top=0.99, right=0.99)
Which leaves a large gap on top and between the subplots. Playing with these parameters, much less understanding them was tough (things like ValueError: bottom cannot be >= top)
My questions :
What is the command to completely minimize the margins?
What do these numbers mean, and what coordinate system does this follow (the non-standard percent thing and origin point of this coordinate system)? What are the special rules on top of this coordinate system?
Where is the exact point this command needs to be called? From experiment, I figured out it works after you create subplots. What if you need to call it repeatedly after you resize a window and need to resize the figure to fit inside?
What are the other methods of adjusting layouts, especially for a single subplot?
They're in figure coordinates: http://matplotlib.sourceforge.net/users/transforms_tutorial.html
To remove gaps between subplots, use the wspace and hspace keywords to subplots_adjust.
If you want to have things adjusted automatically, have a look at tight_layout
Gridspec: http://matplotlib.sourceforge.net/users/gridspec.html