Re-call matplotlib graphs after inital call - python

I have a Tkinter GUI which creates a budget calculator and stores transactions. One of the buttons on the GUI is a call to a script that creates charts from the transaction data.
The first time I click the button, the graphs open up fine. If I attempt to click the button again, a blank figure comes up. I believe this comes from my initial,
plt.show()
call where I draw the graphs. Has anyone ran into a problem like this before?
GUI code:
ViewCharts = Button(win,text = 'View Spending Charts')
ViewCharts.grid(row = 21,column = 1)
def view_charts():
Graphs.plot_charts()
ViewCharts.configure(command = view_charts)
Charts code:
global fig
fig = plt.figure(figsize=(12, 9))
fig.subplots_adjust(hspace=.5)
global ax1
ax1 = fig.add_subplot(1,2,1)
global ax2
ax2 = fig.add_subplot(1,2,2)
def plot_charts():
category_bar_chart()
category_pie_chart()
fig = plt.show()
Where category_bar_chart() and category_pie_chart() are just functions to add plots into ax1 and ax2.
Any help or advice is greatly appreciated! I am working on Python3 on a Mac/PC depending on the time of day. Thanks!

The problem is you are redefining fig when you do fig = plt.show() so after the first call, you no longer have the plots stored anywhere. Simply do plt.show() instead.

Related

Fig layout made tighter interactively by clicking 'adjustment' -> 'tight layout' many times, but unable to reproduce it in my code

A strange thing is happening. I am interactively changing how tight the figure is after showing a plot, by clicking 'adjustment' -> 'tight layout' many times. I know I could copy the 6 parameters and use them to put in my code fig.subplots_adjust(), but the thing is that the layout changes, but there is a point in which the parameters do not!! So, I'm unable to reproduce the effect .... Here I'll show you what I mean:
In my code, I've plt.tight_layout() before plt.show().
Here is the outcome I got:
Now, I click on 'tight layout'. Parameters change, and the subplots look bigger and nearer:
Again, parameters change, and the subplots look bigger and nearer:
Again, subplots are nearer BUT only one parameter changed, and the change is almost meaningless - top was 0.950, and now it's 0.951:
Now, if I export the values and put before plt.show()
plt.subplots_adjust(
top=0.95,
bottom=0.062,
left=0.012,
right=0.988,
hspace=0.249,
wspace=0.0)
I get:
The figure is not as tight as I wanted it to be!! And the subplots aren't as big as I want!! I put the exact same parameters. Anyone knows how to solve it?
TOY EXAMPLE TO REPRODUCE THE ISSUE:
import matplotlib.pyplot as plt
limit = 3
fig, axes = plt.subplots(nrows=limit, ncols=limit, sharex=True, sharey=True, )
for i in range(limit):
for j in range(limit):
ax = axes[i, j]
ax.set_xbound(lower=-2, upper=2)
ax.set_ybound(lower=-2, upper=2)
ax.grid()
ax.set_aspect('equal', adjustable='box')
# show
fig = plt.gcf()
fig.canvas.manager.window.showMaximized()
fig.canvas.set_window_title('Test')
plt.tight_layout() # TODO: comment this line to see the difference if tight layout is not specified
# TODO: uncomment the following block to see that nothing happens if tight layout and the parameters are set
# plt.subplots_adjust(
# top=0.951,
# bottom=0.062,
# left=0.012,
# right=0.988,
# hspace=0.249,
# wspace=0.0
# )
plt.show()
Side note: I'm currently using matplotlib 3.1.1

plt.figure.Figure.show() does nothing when not executing interactively

So I have a following simple code saved in a .py file, and executing in shell:
import matplotlib.pyplot as plt
myfig = plt.figure(figsize=(5, 5))
ax1 = myfig.add_subplot(1, 1, 1)
myfig.show()
However it does nothing upon execution, no errors nothing.
Then when I start Ipython in shell, and type exact same code, it does pop up an empty window. Why is that?
of course I can use plt.show() and everything is fine. But lets say I have two figures, fig1 and fig2, and there is stuff in both figs, and I want to only display one of them, how can I do that? plt.show() plots both of them.
Sorry if this is stupid I'm just curious why when working interactively in ipython, window pops up upon calling fig1.show() but nothing happens when I execute same script in shell but doing: python myfile.py
Thank you!
plt.show starts an event loop, creates interactive windows and shows all current figures in them. If you have more figures than you actually want to show in your current pyplot state, you may close all unneeded figures prior to calling plt.show().
fig1 = plt.figure()
ax1 = fig1.add_subplot(1, 1, 1)
ax1.plot([1,3,4])
fig2 = plt.figure()
ax2 = fig2.add_subplot(1, 1, 1)
ax2.plot([1,2,5])
# close first figure
plt.close(fig1)
# show all active figures (which is now only fig2)
plt.show()
In contrast fig.show() will not start an event loop. It will hence only make sense in case an event loop already has been started, e.g. after plt.show() has been called. In non-interactive mode that may happen upon events in the event loop. To give an example, the following would show fig2 once a key on the keyboard is pressed when fig1 is active.
import matplotlib.pyplot as plt
fig1 = plt.figure()
ax1 = fig1.add_subplot(1, 1, 1)
ax1.plot([1,3,4])
def show_new_figure(evt=None):
fig2 = plt.figure()
ax2 = fig2.add_subplot(1, 1, 1)
ax2.plot([1,2,5])
fig2.show()
# Upon pressing any key in fig1, show fig2.
fig1.canvas.mpl_connect("key_press_event", show_new_figure)
plt.show()
You need to modify your code like this:
import matplotlib.pyplot as plt
myfig = plt.figure(figsize=(5, 5))
ax1 = myfig.add_subplot(1, 1, 1)
plt.plot((1, 2, 3)) # <- plot something
plt.show() # <- show the plot
more info in matplotlib docs here.
you need add an extra line
%matplotlib inline
To get the plot in jupyter notebook.
for more you can refer http://ipython.readthedocs.io/en/stable/interactive/tutorial.html#magics-explained

Overlapping legend when create plot with matplotlib and mpld3 in python

I'm a beginner in the world of python programming and I'm having a really hard time figuring out how to tackle my problem.
The problem is when I created a plot using loop in python with matplotlib and mpld3, the plot is as I expected it to be but the legend of the plot is really wrong because the legends were overlapping with each other. The legend of the plot will be displayed for the latest data only because other data's legend is overlapped on it.
Below is the picture of my plot:
This is the code to create the plot and legend in loop:
plt.rcParams.update({'font.size': 13})
fig, ax = plt.subplots()
for i in range(3,5):
rd1 = pd.read_sql_query("SELECT press, rs FROM basic_chart WHERE
cs = "+str(i), app.config['SQLALCHEMY_DATABASE_URI'])
print(rd1)
pt = ax.plot(rd1['press'],rd1['rs'],'-o')
plugins.connect(fig, plugins.InteractiveLegendPlugin([pt],[str(i)]))
html = mpld3.fig_to_html(fig)
I think that the main problem is on the interactive legend code but I did not manage to figure out the right way to correct it. I really hope experts can help me in this problem.
plt.rcParams.update({'font.size': 13})
fig, ax = plt.subplots()
for i in range(3,5):
rd1 = pd.read_sql_query("SELECT press, rs FROM basic_chart WHERE
cs = "+str(i), app.config['SQLALCHEMY_DATABASE_URI'])
print(rd1)
pt = ax.plot(rd1['press'],rd1['rs'],'-o')
axhandles, axlabels = ax.get_legend_handles_labels()
plugins.connect(fig, plugins.InteractiveLegendPlugin(axhandles, axlabels))
html = mpld3.fig_to_html(fig)
By having the plugins.connect in the loop, you are creating two separate legends.
After plotting, get the handles and labels from the plot and use that in the call to the InteractiveLegendPlugin.

Matplotlib Animation for custom artist classes

Goal
Hi,
I am trying to animate a complex figure with several subplots and have started testing with the artist animation and the function animation methods.
For now, my goal is to have the subplot on the left show a moving colored line (not the problem) and the subplot on the right show an updated representation of a brain scan (the problem). Static, this looks something like this.
# Imports
import nilearn as nil
from nilearn import plotting as nlp
from matplotlib import pyplot as plt
window = np.arange(0,200-50)
fig = plt.figure(figsize=(7,4))
ax = fig.add_subplot(121)
ax.set_xlim([0, 200])
a = ax.axvspan(window[0], window[0]+50, color='blue', alpha=0.5)
ay = fig.add_subplot(122)
b = nlp.plot_stat_map(nil.image.index_img(s_img, 0), axes=ay, colorbar=False, display_mode='x', cut_coords=(0,))
Problem
As you can see, I am using nilearn for plotting the brain image. For some reason, the nilearn object from plot_stat_map does not have an attribute set_visible unlike the matplotlib object from axvspan.
So when I attempt a simple animation like so:
fig = plt.figure(figsize=(7,4))
ax = fig.add_subplot(121)
ax.set_xlim([0, 200])
ay = fig.add_subplot(122)
iml = list()
for i in np.arange(50):
a = ax.axvspan(window[i], window[i]+50, color='blue', alpha=0.5)
b = nlp.plot_stat_map(nil.image.index_img(s_img, i), axes=ay)
iml.append((a,b))
ani = animation.ArtistAniTruemation(fig, iml, interval=50, blit=False,
repeat_delay=1000)
it crashes with the following error:
/home/surchs/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/matplotlib/animation.pyc in _init_draw(self)
974 for f in self.new_frame_seq():
975 for artist in f:
--> 976 artist.set_visible(False)
977 # Assemble a list of unique axes that need flushing
978 if artist.axes not in axes:
AttributeError: 'OrthoSlicer' object has no attribute 'set_visible'
Makes sense, nilearn does maybe not conform to matplotlibs expectations. So I try the function animation method like so:
def show_things(i, window, ax, ay):
ax.axvspan(window[i], window[i]+50, color='blue', alpha=0.5)
nlp.plot_stat_map(nil.image.index_img(s_img, i), axes=ay, colorbar=False, display_mode='x', cut_coords=(0,))
fig = plt.figure(figsize=(7,4))
ax = fig.add_subplot(121)
ax.set_xlim([0, 200])
ay = fig.add_subplot(122)
ani = animation.FuncAnimation(fig, show_things, interval=10, blit=False, fargs=(window, ax, ay))
Although I am not sure if I am using things correctly, this gives me an animated brain plot on the right. However, the plot on the left is now not updated but just drawn over. So instead of a sliding bar, I get an expanding color surface. Something like this:
Question
How do I
get the plot on the left to update (as opposed to overwrite) on each iteration when using the function animation method? I already tried the ax.cla() function in matplotlib but since this also clears all axis attributes (like xlim) this is not a solution for me. Are there altneratives?
get the plot on the right to work with the artist animation method even though the custom plotting class is obviously missing a crucial attribute.
Also, I am not sure if I am doing the whole implementation part right, so any advice on that front is also very appreciated.
I suspect you may need to clear the axvspan axis between plots with ax.cla() to get the correct left plot (N.B. probably should clear the right plot too). To get round the problem of missing attributes, I'd suggest extracting the data from the returned handle from nlp.plot_stat_map and plotting with matplotlib pcolormesh (or imshow). Another possibility is creating a child class and adding this method yourself. It may also be worth submitting a bug/feature request to nilearn if this should be present.
By the way, if you're only after a quick and easy plot, you can do a poor man's version of animation using interactive plots, as a minimal example,
import matplotlib.pyplot as plt
import numpy as np
import time
#Interactive plot
plt.ion()
#Setup figures
fig = plt.figure(figsize=(7,4))
ax = fig.add_subplot(121)
ay = fig.add_subplot(122)
plt.show()
x = np.linspace(0,2*np.pi)
for i in range(10000):
print(i)
#Clear axes
ax.cla(); ay.cla()
#Update data
yx = np.sin(x+i*0.1)
yy = np.sin(2.*(x+i*0.1))
#Replot
ax.plot(x,yx)
ay.plot(x,yy)
#Pause to allow redraw
plt.draw()
plt.pause(0.01)

Python matplotlib Update figure after savefig called

My problem is:
I have Matplotlib figure in PyGTK application, that is constatly updated each few seconds. I've added abbility to save figure to disk as PNG file. After calling figure.savefig(filename, other parameters) my figure in application stops being updated.
Figure initialization phase:
# setup matplotlib stuff on empty space in vbox4
figure = Figure()
canvas = FigureCanvasGTK(figure) # a gtk.DrawingArea
canvas.show()
self.win.get_widget('vbox4').pack_start(canvas, True, True) # this will be aded to last place
self.win.get_widget('vbox4').reorder_child(canvas, 1) #place plot to space where it should be
Figure is being updated this way (this called each few seconds in separate thread):
def _updateGraph(self, fig, x, x1, y):
#Various calculations done here
fig.clf()#repaint plot: delete current and formate a new one
axis = fig.add_subplot(111)
#axis.set_axis_off()
axis.grid(True)
#remove ticks and labels
axis.get_xaxis().set_ticks_position("none")
for i in range(len(axis.get_xticklabels())): axis.get_xticklabels()[i].set_visible(False)
axis.get_yaxis().set_ticks_position("none")
axis.plot(numpy.array(x),numpy.array(y)/(1.0**1), "k-" ,alpha=.2)
axis.set_title('myTitle')
fig.autofmt_xdate()
fig.canvas.draw()
everything works as expected. But after calling:
figure.savefig(fileName, bbox_inches='tight', pad_inches=0.05)
File have been saved, BUT my figure on screen stops being updated.
Any ideas how do I save figure to disk and still be able to update my fig on screen ?
Have you tried updating the line data instead of recreating the figure? This assumes the number of datapoints doesn't change each frame. It might help issue of things refusing to update, and at the least it will be faster.
def _updateGraph(self, fig, x, x1, y):
#Various calculations done here
ydata = numpy.array(y)/(1.0**1)
# retrieved the saved line object
line = getattr(fig, 'animated_line', None);
if line is None:
# no line object so create the subplot and axis and all
fig.clf()
axis = fig.add_subplot(111)
axis.grid(True)
#remove ticks and labels
axis.get_xaxis().set_ticks_position("none")
for i in range(len(axis.get_xticklabels())):
axis.get_xticklabels()[i].set_visible(False)
axis.get_yaxis().set_ticks_position("none")
xdata = numpy.array(x);
line = axis.plot(xdata, ydata, "k-" ,alpha=.2)
axis.set_title('myTitle')
fig.autofmt_xdate()
# save the line for later reuse
fig.animated_line = line
else:
line.set_ydata(ydata)
fig.canvas.draw()
I have found a work-a-round to this. As my figure refuses to be updated after calling figure.savefig() so i found a way how to work a round it. My figure is within HBox2 container (GUI is created with Glade 3.6.7) as first element
# some stuff going
figure.saveFig(fileName)
# WORK-A-ROUND: delete figure after calling savefig()
box = self.win.get_widget('hbox2')
box.remove(box.get_children()[0])
self._figPrepare()
def _figPrepare(self): #initialize graph
figure = Figure()
canvas = FigureCanvasGTK(figure) # a gtk.DrawingArea
canvas.show()
figure.clf()
gui.w().set("figure", figure)
self.win.get_widget('hbox2').pack_start(canvas, True, True) # this will be aded to last place
self.win.get_widget('hbox2').reorder_child(canvas, 0) #place plot to space where it should be
I know this is not best practice, and probably is slow, but it work OK for me. Hope someone else will find this useful
from http://matplotlib.org/examples/user_interfaces/embedding_in_gtk2.html
what seems to help is the "agg" not sure what that means but fixed this bug for me :)
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas

Categories

Resources