Set matplotlib default figure window title - matplotlib

The default window title of a figure is figure X, where X is increased each figure.
I know how to change the title of a figure:
fig = pylab.gcf()
fig.canvas.set_window_title('Test')
But how do I change the default window title (So that it will be Test 1, Test 2 etc..)? so that I will not need to change the window title each time.
I did not find a key in the mpl.rcParams
Thanks

Edit: my answer does not change the defaults, as requested by OP, but provides a way to define figure title at figure creation.
When creating a figure using matplotlib.pyplot.subplots, there is an optional argument num that, even if not documented as such (as far as I could search), is later used as figure title:
import matplotlib.pyplot as plt
fig, axes = plt.subplots(nrows=2, num="some nice window title")
plt.ion() # to make plot non-blocking, i.e. if multiple plots are launched
fig.show()
It is also used as default filename when saving the plot, which is a very neat feature.
(Caution: even if not documented, this num value is also a key to this figure. So, take care not to reuse the same value.)
And here's the result:

There is no key in mpl.rcParams since the default title is hardcoded in the backends. For example, have a look at the figure initialization code of the QT5 backend (https://github.com/matplotlib/matplotlib/blob/c1a3c030c66f512c6f79e4f45b0870b68921320c/lib/matplotlib/backends/backend_qt5.py#L554):
self.window.setWindowTitle("Figure %d" % num)
This means you cannot change the default window title unless you change the code of the matplotlib module itself.

Related

Matplotlib widget, secondary y axis, twinx

i use jupyterlab together with matplotlib widgets. I have ipywidgets installed.
My goal is to choose which y-axis data is displayed in the bottom of the figure.
When i use the interactive tool to see the coordinates i get only the data of the right y-axis displayed. Both would be really nice^^ My minimal code example:
import matplotlib.pyplot as plt
import numpy as np
%matplotlib widgets
x=np.linspace(0,100)
y=x**2
y2=x**3
fig,ax=plt.subplots()
ax2=ax.twinx()
ax.plot(x,y)
ax2.plot(x,y2)
plt.show()
With this example you might ask why not to plot them to the same y-axis but thats why it is a minimal example. I would like to plot data of different units.
To choose which y-axis is used, you can set the zorder property of the axes containing this y-axis to a higher value than that of the other axes (0 is the default):
ax.zorder = 1
However, that will cause this Axes to obscure the other Axes. To counteract this, use
ax.set_facecolor((0, 0, 0, 0))
to make the background color of this Axes transparent.
Alternatively, use the grab_mouse function of the figure canvas:
fig.canvas.grab_mouse(ax)
See here for the (minimal) documentation for grab_mouse.
The reason this works is this:
The coordinate line shown below the figure is obtained by an event callback which ultimately calls matplotlib.Axes.format_coord() on the axes instance returned by the inaxes property of the matplotlib events that are being generated by your mouse movement. This Axes is the one returned by FigureCanvasBase.inaxes() which uses the Axes zorder, and in case of ties, chooses the last Axes created.
However, you can tell the figure canvas that one Axes should receive all mouse events, in which case this Axes is also set as the inaxes property of generated events (see the code).
I have not found a clean way to make the display show data from both Axes. The only solution I have found would be to monkey-patch NavigationToolbar2._mouse_event_to_message (also here) to do what you want.

Python: Setting Seaborn lineplot error band edge color

I am using Seaborn to make lineplots with a band indicating standard deviations. Something just like the second/third plot in the doc below:
https://seaborn.pydata.org/generated/seaborn.lineplot.html?highlight=lineplot#seaborn.lineplot
I am wondering is that possible to set the edgecolor for the error band separately? I can change linestyle of the band through err_kws. But, if I pass "edgecolor" through err_kws, it seems that nothing happens. Is there someway to allow me to get control with the edges?
Thanks!
As djakubosky notes, the color of the line and the error band are coupled together internally in seaborn's lineplot. I suggest that it is cleaner to modify the properties of the artists after the plot has been generated. This is a cleaner alternative than editing the library source code directly (maintenance headaches, etc).
For the example data shown on the sns.lineplot docs, we can update the error band properties as follows:
import seaborn as sns
fmri = sns.load_dataset("fmri")
ax = sns.lineplot(x="timepoint", y="signal", data=fmri)
# by inspection we see that the PolyCollection is the first artist
for child in ax.get_children():
print(type(child))
# and so we can update its properties
ax.get_children()[0].set_color('k')
ax.get_children()[0].set_hatch('//')
It may be more robust to select by property of the artist rather than selecting the first artist (especially if you have already rendered something on the same axes), e.g. along these lines:
from matplotlib.collections import PolyCollection
for child in ax.findobj(PolyCollection):
child.set_color('k')
child.set_hatch('//')
It appears that it isn't really possible to change this color under the current seaborn implementation. This is because they pass the color of the main line explicitly to the error band as ax.fillbetweenx(... color=original_color). After playing around in the past, I found that this color arg seems to supersede the other color arguments such as facecolor and edgecolor, thus it doesn't matter what you put in there in the err_kws. However you could fix it by editing line 810 in site-packages/seaborn/relational.py from:
ax.fill_between(x, low, high, color=line_color, **err_kws)
to
ax.fill_between(x, low, high, **err_kws)
and passing the colors explicitly through err_kws.

change matplotlib data in gui

I've developed an gui with python pyqt. There I have a matplotlib figure with x,y-Data and vlines that needs to change dynamically with a QSlider.
Right now I change the data just with deleting everything and plot again but this is not effective
This is how I do it:
def update_verticalLines(self, Data, xData, valueSlider1, valueSlider2, PlotNr, width_wg):
if PlotNr == 2:
self.axes.cla()
self.axes.plot(xData, Data, color='b', linewidth=2)
self.axes.vlines(valueSlider1,min(Data),max(Data),color='r',linewidth=1.5, zorder = 4)
self.axes.vlines(valueSlider2,min(Data),max(Data),color='r',linewidth=1.5, zorder = 4)
self.axes.text(1,0.8*max(Data),str(np.round(width_wg,2))+u"µm", fontsize=16, bbox=dict(facecolor='m', alpha=0.5))
self.axes.text(1,0.6*max(Data),"Pos1: "+str(round(valueSlider1,2))+u"µm", fontsize=16, bbox=dict(facecolor='m', alpha=0.5))
self.axes.text(1,0.4*max(Data),"Pos2: "+str(round(valueSlider2,2))+u"µm", fontsize=16, bbox=dict(facecolor='m', alpha=0.5))
self.axes.grid(True)
self.draw()
"vlines" are LineCollections in matplotlib. I searched in the documentation but could not find any hint to a function like 'set_xdata' How can I change the x value of vertical lines when they are already drawn and embedded into FigureCanvas?
I have the same problem with changing the x and y data. When trying the known functions of matplotlib like 'set_data', I get an error that AxisSubPlot does not have this attribute.
In the following is my code for the FigureCanvas Class. The def update_verticalLines should only contain commands for changing the x coord of the vlines and not complete redraw.
Edit: solution
Thanks #Craigular Joe
This was not exactly how it worked for me. I needed to change something:
def update_verticalLines(self, Data, xData, valueSlider1, valueSlider2, PlotNr, width_wg):
self.vLine1.remove()
self.vLine1 = self.axes.vlines(valueSlider1,min(Data), max(Data), color='g', linewidth=1.5, zorder = 4)
self.vLine2.remove()
self.vLine2 = self.axes.vlines(valueSlider2,min(Data), max(Data), color='g', linewidth=1.5, zorder = 4)
self.axes.draw_artist(self.vLine1)
self.axes.draw_artist(self.vLine2)
#self.update()
#self.flush_events()
self.draw()
update() did not work without draw(). (The old vlines stayed)
flush_events() did some crazy stuff. I have two instances of FigureCanvas. flush_events() caused that within the second instance call the vlines moved with the slider but moved then back to the start position.
When you create the vlines, save a reference to them, e.g.
self.my_vlines = self.axes.vlines(...)
so that when you want to change them, you can just remove and replace them, e.g.
self.my_vlines.remove()
self.my_vlines = self.axes.vlines(...)
# Redraw vline
self.axes.draw_artist(self.my_vlines)
# Add newly-rendered lines to drawing backend
self.update()
# Flush GUI events for figure
self.flush_events()
By the way, in the future you should try your best to pare down your code sample to just the essential parts. Having a lot of unnecessary sample code makes it hard to understand your question. :)

pandas.plot and pyplot.save_fig create different sized PNGs for same figsize

When I call the same function that uses pandas.plot with the same figsize, I get different sized PNG files. The width is same but the height in pixels changes. I suspect that the length of the x-axis labels changes the height.I have not yet tried directly calling the matplotlib functions.
I have also tried plt.rcParams['figure.figsize'] = (7,4). The problem does not appear to be in how figsize is set. My print_fig_info always produces the desire values.
# Primitive way that confirmed that the figure size does not change
def print_fig_info(label=""):
print(label,str(plt.gcf().get_size_inches()))
def my_plot(df):
global c
print_fig_info("Before plot")
df.plot(kind='bar', figsize=(7,4))
print_fig_info("After plot")
# want to make output files unique
c += 1
plt.savefig("output"+str(c), bbox_inches='tight', dpi='figure')
In your call to savefig you explicitely ask matplotlib to change the figsize to the minimal size that still fits all the elements in via bbox_inches='tight'.
Or in other words, bbox_inches='tight' is especially designed for changing the figure size to the minimum bounding box, and matplotlib is therefore doing what it's being asked for.
Solution: Don't use bbox_inches='tight'.

Matplotlib annotate doesn't work on log scale?

I am making log-log plots for different data sets and need to include the best fit line equation. I know where in the plot I should place the equation, but since the data sets have very different values, I'd like to use relative coordinates in the annotation. (Otherwise, the annotation would move for every data set.)
I am aware of the annotate() function of matplotlib, and I know that I can use textcoords='axes fraction' to enable relative coordinates. When I plot my data on the regular scale, it works. But then I change at least one of the scales to log and the annotation disappears. I get no error message.
Here's my code:
plt.clf()
samplevalues = [100,1000,5000,10^4]
ax = plt.subplot(111)
ax.plot(samplevalues,samplevalues,'o',color='black')
ax.annotate('hi',(0.5,0.5), textcoords='axes fraction')
ax.set_xscale('log')
ax.set_yscale('log')
plt.show()
If I comment out ax.set_xcale('log') and ax.set_ycale('log'), the annotation appears right in the middle of the plot (where it should be). Otherwise, it doesn't appear.
Thanks in advance for your help!
It may really be a bug as pointed out by #tcaswell in the comment but a workaround is to use text() in axis coords:
plt.clf()
samplevalues = [100,1000,5000,10^4]
ax = plt.subplot(111)
ax.loglog(samplevalues,samplevalues,'o',color='black')
ax.text(0.5, 0.5,'hi',transform=ax.transAxes)
plt.show()
Another approach is to use figtext() but that is more cumbersome to use if there are already several plots (panels).
By the way, in the code above, I plotted the data using log-log scale directly. That is, instead of:
ax.plot(samplevalues,samplevalues,'o',color='black')
ax.set_xscale('log')
ax.set_yscale('log')
I did:
ax.loglog(samplevalues,samplevalues,'o',color='black')