Resizing a plot in Tkinter - matplotlib

I am fooling around making a small program for some personal use with Tkinter, and it will contain a plot which needs to be resized if the window is resized. This works if I make the window smaller, however, if I increase the window size the plot stays the same size after it has reached some size (although it nicely sticks to the middle of the frame it is in). I tried both pack and grid, but I don't understand why it will resize any bigger ... Currently I set to original image to 21 by 13 inches (but I would like to know what prevents/prohibits it from resizing in the first place.
this is my code (I know its a bit bulky..)
from Tkinter import *
import ttk
import misc
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
root = Tk()
root.title("Plotting Customers Lines")
content = ttk.Frame(root, padding=(3,3,12,12))
plot = ttk.Frame(root, padding=(3,3,12,12))
toolbarframe = ttk.Frame(root)
subject = StringVar(content)
dropdown = ttk.Combobox(content, textvariable = subject, values = ['To', 'Do', 'Later'])
ok = ttk.Button(content, text='Plot Timeline')
f = plt.figure(figsize=(21,13))
a = f.add_subplot(111)
a.plot(range(100),range(100))
canvas = FigureCanvasTkAgg(f, plot)
canvas.show()
toolbar = NavigationToolbar2TkAgg(canvas, toolbarframe)
toolbar.update()
content.grid(column=1, row=0)
plot.grid(column=0,row=0)
toolbarframe.grid(column=0,row=1)
ok.grid(column=1, row=1)
dropdown.grid(column=1,row=0)
canvas.get_tk_widget().grid(column=0, row=0, sticky=(N,W,E,S))#pack(side=TOP, fill=BOTH, expand = True)#grid(column=0, row=0, sticky=(N,W,E,S))
root.columnconfigure(0, weight = 1)
root.columnconfigure(1, weight = 0)
root.rowconfigure(0, weight = 1)
root.rowconfigure(1, weight = 0)
plot.columnconfigure(0, weight = 1)
plot.rowconfigure(0, weight = 1)
root.mainloop()

Related

Add text flush left below plot in python

I'd like to add text beneath a plot, which includes the source of the used data.
It should be positioned at the edge of the image, so beneath the longest ytick and if possible at a fixed vertical distance to the x-axis.
My approach:
import matplotlib.pyplot as plt
country = ['Portugal','Spain','Austria','Italy','France','Federal Republic of Germany']
value = [6,8,10,12,14,25]
plt.figure(figsize=(4,4))
plt.barh(country,value)
plt.xlabel('x-axis')
plt.text(-18,-2.5,'Source: blablablablablablablablablablablablablablablablabla',ha='left')
Plot of the code
I used plt.text(). My problem with the command is, that I have to manually try x and y values (in the code: -18,-2.5) for different plots.
Is there a better way?
Thanks in advance.
Firstly, I got the box info of yticklabels, and then got the leftmost x location for all the yticklabels. Finally, the blended transform method was used to add text with some location adjustments.
import matplotlib.pyplot as plt
from matplotlib.transforms import IdentityTransform
import matplotlib.transforms as transforms
country = ['Portugal','Spain','Austria','Italy','France','Federal Republic of Germany']
value = [6,8,10,12,14,25]
plt.figure(figsize=(4,4))
plt.barh(country,value)
plt.xlabel('x-axis')
ax = plt.gca()
fig =plt.gcf()
fig.tight_layout()
fig.canvas.draw()
labs = ax.get_yticklabels()
xlocs = []
for ilab in labs:
xlocs.append(ilab.get_window_extent().x0)
print(xlocs)
x0 = min(xlocs)
trans = transforms.blended_transform_factory(IdentityTransform(), ax.transAxes)
plt.text(x0-2.5,-0.2,'Source: blablablablablablablablablablablablablablablablabla',ha='left',transform=trans)
plt.savefig("flush.png",bbox_inches="tight")

Displaying cursor coordinates in embedded matplotlib + pyqt

I'm using matplotlib as an embedded control in a PyQt 4 application to display and interact with images. I'd like to display the cursor coordinates and underlying value when the user moves it over the image. I've found the following post that addresses my needs but can't seem to get it to work:
matplotlib values under cursor
Here's what I have. First, I derive a class from FigureCanvasQtAgg:
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQtAgg as FigureCanvas
import matplotlib as mpImage
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
class MatPlotLibImage(FigureCanvas):
def __init__(self, parent = None):
self.parent = parent
self.fig = Figure()
super(MatPlotLibImage, self).__init__(self.fig)
self.axes = self.fig.add_subplot(111)
self.axes.get_xaxis().set_visible(False)
self.axes.get_yaxis().set_visible(False)
def displayNumpyArray(self, myNumpyArray):
self.dataArray = myNumpyArray
self.dataRows = self.dataArray.shape[0]
self.dataColumns = self.dataArray.shape[1]
self.axes.clear()
imagePlot = self.axes.imshow(myNumpyArray, interpolation = "nearest")
I'm also creating a new class that uses the above as its base, and this is the one that has to display the coords + value:
from MatPlotLibControl import *
class MainMatPlotLibImage(MatPlotLibControl):
def __init__(self, parent = None):
super(MainMatPlotLibImage, self).__init__(parent)
self.parent = parent
self.axes.format_coord = self.format_coord
def format_coord(self, x, y):
column = int(x + 0.5)
row = int(y + 0.5)
if column >= 0 and column <= self.dataColumns - 1 and row >= 0 and row <= self.dataRows - 1:
value = self.dataArray[row, column\
return 'x=%1.4f, y=%1.4f, z=%1.4f'%(column, row, value)
Everything is working smashingly except that when I move the cursor over the image I don't see the coords + value displayed on the plot. I then came across this post that seems to imply that they are actually displayed on the toolbar, not the plot itself: Disable coordinates from the toolbar of a Matplotlib figure
I'm not using the toolbar and this would explain why I don't see anything. Does anyone know if this is indeed the case (i.e. displayed on the toolbar)? If this is the case I will still need to retrieve the cords + underlying value and display them somewhere else in the application, but I've noticed that my "format_coord()" override is never called. Thanks in advance for any help.
-L

How to expand matplolib window without stretching the plot?

I want to increase the grey area around the plot, but keeping the plot the same size. I've already tried changing the figure size, which ends up stretching the plot.
The axes inside the figure is positionned relative to the figure. Per default you have e.g. a fraction of 0.125 of figure width as space at the left. This means that resizing the figure, scales the axes as well.
You may calculate how much the spacings need to change such that if the figure is rescaled, the axes size remains constant. The new spacings then need to be set using fig.subplots_adjust.
import matplotlib.pyplot as plt
def set_figsize(figw,figh, fig=None):
if not fig: fig=plt.gcf()
w, h = fig.get_size_inches()
l = fig.subplotpars.left
r = fig.subplotpars.right
t = fig.subplotpars.top
b = fig.subplotpars.bottom
hor = 1.-w/float(figw)*(r-l)
ver = 1.-h/float(figh)*(t-b)
fig.subplots_adjust(left=hor/2., right=1.-hor/2., top=1.-ver/2., bottom=ver/2.)
fig, ax=plt.subplots()
ax.plot([1,3,2])
set_figsize(9,7)
plt.show()
You may then also use this function to update the subplot params when the figure window is resized.
import matplotlib.pyplot as plt
class Resizer():
def __init__(self,fig=None):
if not fig: fig=plt.gcf()
self.fig=fig
self.w, self.h = self.fig.get_size_inches()
self.l = self.fig.subplotpars.left
self.r = self.fig.subplotpars.right
self.t = self.fig.subplotpars.top
self.b = self.fig.subplotpars.bottom
def set_figsize(self, figw,figh):
hor = 1.-self.w/float(figw)*(self.r-self.l)
ver = 1.-self.h/float(figh)*(self.t-self.b)
self.fig.subplots_adjust(left=hor/2., right=1.-hor/2., top=1.-ver/2., bottom=ver/2.)
def resize(self, event):
figw = event.width/self.fig.dpi
figh = event.height/self.fig.dpi
self.set_figsize( figw,figh)
fig, ax=plt.subplots()
ax.plot([1,3,2])
r = Resizer()
cid = fig.canvas.mpl_connect("resize_event", r.resize)
plt.show()
In the window of a matplotlib figure, there's a button called 'Configure subplots' (see below picture, screenshot on Windows 10 with matplotlib version 1.5.2). Try to change the parameters 'left' and 'right'. You can also change these parameters with plt.subplots_adjust(left=..., bottom=..., right=..., top=..., wspace=..., hspace=...).

Update data point labels in bokeh plot

I use bokeh in an ipython notebook and would like to have a button next to a plot to switch on or off labels of the data points. I found a solution using IPython.html.widgets.interact, but this solution resets the plot for each update including zooming and padding
This is the minimal working code example:
from numpy.random import random
from bokeh.plotting import figure, show, output_notebook
from IPython.html.widgets import interact
def plot(label_flag):
p = figure()
N = 10
x = random(N)+2
y = random(N)+2
labels = range(N)
p.scatter(x, y)
if label_flag:
pass
p.text(x, y, labels)
output_notebook()
show(p)
interact(plot, label_flag=True)
p.s. If there is an easy way to do this in matplotlib I would also switch back again.
By using bokeh.models.ColumnDataSource to store and change the plot's data I was able to achieve what I wanted.
One caveat is, that I found no way to make it work w/o refresh w/o calling output_notebook twice in two different cells. If I remove one of the two output_notebook calls the gui of the tools-button looks breaks or changing a setting also results in a reset of the plot.
from numpy.random import random
from bokeh.plotting import figure, show, output_notebook
from IPython.html.widgets import interact
from bokeh.models import ColumnDataSource
output_notebook()
## <-- new cell -->
p = figure()
N = 10
x_data = random(N)+2
y_data = random(N)+2
labels = range(N)
source = ColumnDataSource(
data={
'x':x_data,
'y':y_data,
'desc':labels
}
)
p.scatter('x', 'y', source=source)
p.text('x', 'y', 'desc', source=source)
output_notebook()
def update_plot(label_flag=True):
if label_flag:
source.data['desc'] = range(N)
else:
source.data['desc'] = ['']*N
show(p)
interact(update_plot, label_flag=True)

adjust colour bar range to visible part of basemap contour plot

I have a contour plot on a basemap and i wish to adjust the range of the colour bar so that they fit to the visible data. The default setting makes the colour range to fit to all data, i.e. also those which are not plotted. Is there a setting for this?
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap, addcyclic, shiftgrid
myllcrnrlat=35
myurcrnrlat=65
myllcrnrlon=-45
myurcrnrlon=45
m = Basemap(projection='cyl', llcrnrlat=myllcrnrlat, urcrnrlat=myurcrnrlat,\
llcrnrlon=myllcrnrlon, urcrnrlon=myurcrnrlon, resolution='c')
lonsin=np.asarray(range(0,360,10))
latsin=np.asarray(range(-90,90,10))
valin=np.random.rand(len(latsin), len(lonsin))
valin[0,0]=5 #this is a value outside my basemap area and higher than all inside.
valin_cyclic, lons_cyclic = addcyclic(valin, lonsin)
valin_cyclic, lons_cyclic = shiftgrid(180., valin_cyclic, lons_cyclic, start=False)
lon2d, lat2d = np.meshgrid(lons_cyclic, latsin)
x, y = m(lon2d, lat2d)
cs = m.pcolormesh(x, y, valin_cyclic,cmap=plt.get_cmap('autumn_r'))
cbar = plt.colorbar(cs)
plt.show()
Of couse I can use vmin, vmax by doing something like the following, but it seems rather long, so maybe there is a special setting?
lonsin_inbasemap=np.asarray([a for a in lonsin if myllcrnrlon <= a if a <= myurcrnrlon])
latsin_inbasemap=np.asarray([a for a in latsin if myllcrnrlat <= a if a <= myurcrnrlat])
valin_inbasemap_tmp = np.transpose(np.asarray([valin[:,a] for a in range(len(lonsin)) if lonsin[a] in lonsin_inbasemap]))
valin_inbasemap = np.asarray([valin_inbasemap_tmp[a,:] for a in range(len(latsin)) if latsin[a] in latsin_inbasemap])
del(valin_inbasemap_tmp)
vmax=np.amax(valin_inbasemap)
cs = m.pcolormesh(x, y, valin_cyclic,vmax=vmax, cmap=plt.get_cmap('autumn_r'))
cbar = plt.colorbar(cs)
plt.show()
If you want to mask some data that below some value.
For example, the minus data you would not want to show:
You can use ``
valin = np.ma.masked_less(valin_cyclic,0)
cmap1 = plt.cm,get_cmap("autumn_r")
cmap1.set_bad("w")
p =plt.pcolor((x, y,conc,cmap=cmap1,alpha =1,zorder =2)