How to update the legend from matplotlib toolbar - python-2.7

I am defining a matplotlib plot for a given data. once the plot is displayed, I am trying to change some line property using navigation tool bar edit option.
When I make change say example solid line to dashdotted, the update get reflected on the lines, but the legends are not updated.
How can I capture this event when the apply button is clicked, so i can use this to refresh the legend. At the moment I am capturing a pick_event as a signal to refresh the legends.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('Qt4Agg')
x=np.linspace(0,100,100)
y=np.linspace(100,200,100)
plt.plot(x,y,label='test')
plt.legend()
ax.legend()
plt.show()
#optional code
def on_press(event):
lines, labels = ax.get_legend_handles_labels()
ax.legend(lines, labels, loc=0)
fig.canvas.draw()
cid = fig.canvas.mpl_connect('pick_event', on_press)

After a bit of struggle the only easy way to resolve this solution is to add legend refresh as part of the navigation tool bar call back function
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
def home_callback():
print "home called"
ax.legend()
x=np.linspace(0,100,100)
y=np.linspace(100,200,100)
fig=plt.figure()
ax=fig.add_subplot(111)
ax.plot(x,y,label='test')
ax.legend()
plt.show()
fm = plt.get_current_fig_manager()
fm.toolbar.actions()[0].triggered.connect(home_callback)

Related

Can't remove matplotlib's padding around imshow() figure

I'm embedding matplotlib into my PyQt4 GUI and I'm having a heck of a time. I can get the image to display but it adds a very thick padding around the content that I'd like to remove. Here's what I'm doing:
from PyQt4.QtCore import *
from PyQt.QtGui import *
import numpy as np
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4Agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.image as mpImage
import matplotlib.pyplot as plt
class MatPlotLibImage(FigureCanvas):
def __init__(self):
super(MatPlotLibImage, self).__init__(self.fig)
self.axes = self.fig.add_subplot(111)
def LoadImage():
image = mpImage.imread("myImage.png")
imgplot = self.axes.imshow(image, interpolation="nearest")
# plt.axis("Off") -> Doesn't do anything as far as I can tell
imgplot.axes.set_axis_off() # Gets rid of frame
imgplot.axes.get_xaxis().set_visible(False) # Turn off x-axis
imgplot.axes.get_yaxis().set_visible(False) # Turn off y-axis
If I add this widget to a QDockWidget I get the following result:
As you can see it renders with a large white padding around the content. I cannot seem to remove this and everything I'm turning up online is focused on removing the padding when saving the image, not displaying. Does anyone know how to remove this padding at display time? Thanks in advance.
You may use subplots_adjust to get rid of the margins. I.e.
self.fig.subplots_adjust(bottom=0, top=1, left=0, right=1)
This will tell the figure not to use any margins around its child axes. You may then still get some white space to one direction, which is due to the canvas aspect ratio not being the same as the image aspect. However, I think that you don't want to change the image aspect and so this remaining margin would acutally be desired.

Graphing in Tkinter frame, updating info in Tkinter

So I am currently working on a basic stock program, and I have been able to get my graphs (of stock data from the last month) on my tkinter window any tips on how to actively update my tkinter window would be great! (FYI I am very new to programming, this is my first year, so please try to explain in basic terms!) Heres my code:
import numpy as np
import datetime as dt
import yahoo_finance as yf
import matplotlib.pyplot as plt
from Tkinter import *
import quandl
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
root=Tk()
root.geometry('1400x875')
root.title("Stock Information")
fmain=Frame(root, width=1400, height=900, bg='orange',bd=5)
fmain.place(x=100, y=0)
today=dt.date.today()
thirty_day_graph_frame=Frame(fmain, width=645, height=400,bg='green4',bd=5)
thirty_day_graph_frame.place(x=0, y=444)
thirty_days=dt.timedelta(days=43)
thirty_days_ago=today-thirty_days
five_yrs_graph_frame=Frame(fmain, width=645, height=400, bg='yellow2',bd=5)
five_yrs_graph_frame.place(x=655, y=444)
five_years=dt.timedelta(days=1825)
five_years_ago=today-five_years
def stock_info(stock_name):
stock=yf.Share(stock_name)
stock_price=stock.get_price()
name_price_label=Label(fmain, text=(stock_name,':', stock_price),font=("Times New Roman",23))
name_price_label.place(x=400, y=10)
day_high=quandl.get("WIKI/"+str(stock_name)+".2",start_date=str(today),end_date=str(today))
high_price_label=Label(fmain, text=(str(day_high)), font=("Times New Roman",20))
high_price_label.place(x=400, y=100)
thirty_day_data = quandl.get("WIKI/"+str(stock_name), start_date=str(thirty_days_ago), end_date=str(today),column_index=4) #So quandl.get gives a lot of info, so the column_index=4 is just getting closing prices
five_year_data = quandl.get("WIKI/"+str(stock_name),start_date=str(five_years_ago), end_date=str(today), column_index=4)
thirty_day_fig = plt.figure(figsize=(8,4))
plt.plot(thirty_day_data)
canvas = FigureCanvasTkAgg(thirty_day_fig, master=thirty_day_graph_frame)
plot_widget = canvas.get_tk_widget()
plot_widget.place(x=0,y=0)
five_year_fig=plt.figure(figsize=(8,4))
plt.plot(five_year_data)
canvas1=FigureCanvasTkAgg(five_year_fig, master=five_yrs_graph_frame)
plot_widget1=canvas1.get_tk_widget()
plot_widget1.place(x=1,y=0)
root.after(5000, stock_info, stock_name)
apple_button=Button(root,text='AAPL', command=lambda:stock_info('AAPL'))
tesla_button=Button(root,text='TSLA', command=lambda:stock_info('TSLA'))
google_button=Button(root,text='GOOG', command=lambda:stock_info('GOOG'))
apple_button.place(x=10, y=15)
tesla_button.place(x=10, y=45)
google_button.place(x=10,y=75)
root.mainloop()
The reason your graphs are plotted from the start is because of the way you assign commands to your buttons. One way to fix this is to assign the command as a lambda expression:
apple_button = Button(root, text='AAPL', command=lambda:stock_info('AAPL'))
To let your GUI update itself over time, you can create a loop using the root.after() method:
# Define the figure and canvas outside the loop
fig = plt.Figure()
a = fig.add_subplot(111)
canvas = FigureCanvasTkAgg(fig, master=f1)
canvas.get_tk_widget().grid()
def stock_info(stock_name):
# Get stock data and plot it on the GUI
...
a.cla()
a.plot(data)
canvas.draw()
# Schedule the function to call itself again after 5 seconds
root.after(5000, stock_info, stock_name)

Dynamically change the shape of bokeh Figure

I am building a web app that will display images as part of a data analysis pipeline. For this, I need to dynamically change the width and height of a Figure object in bokeh.
With the following code, the shape of the Figure is changed, but the change only takes effect after I resize my browser window, even if the browser window resize is ever so small.
import bokeh.plotting
import bokeh.models
import bokeh.layouts
# set up the interface
fig1 = bokeh.plotting.figure()
button = bokeh.models.Button(label='scramble')
# define a callback and connect it
def callback():
fig1.width = int(fig1.width * .8)
button.on_click(callback)
# add everything to the document
bokeh.plotting.curdoc().add_root(bokeh.layouts.column(button, fig1))
Is there some update method which I need to run? I have read about "next tick callbacks" but I don't understand if that is relevant.
The above behavior occurs both with firefox and chromium on my gnome system.
The reason this is happening is because the layout is not getting updated. Although your code changes the figure's property value you have to recompute all values in the Document solver for an actual resize to happen.
Here is the line in BokehJS where the resize hook happens:
https://github.com/bokeh/bokeh/blob/master/bokehjs/src/coffee/document.coffee#L92
After resize is called at the document level, resize objects re-render:
https://github.com/bokeh/bokeh/blob/master/bokehjs/src/coffee/models/layouts/layout_dom.coffee#L61
The problem is that there's not currently, to the best of my knowledge, an exposed way to re-trigger the document resize event.
However you can do it client side. Here's working code using CustomJS:
test.py
from bokeh.io import show
from bokeh.layouts import column
from bokeh.models import Button, CustomJS
from bokeh.plotting import figure
fig = figure()
button = Button(label='scramble')
button.callback = CustomJS(args=dict(fig=fig), code="""
var old_width = fig.width;
var doc = fig.document;
fig.width = old_width * 0.8;
doc.resize();
""")
col = column(button, fig)
show(col)
This can be run with python test.py.
Note you could also do this with bokeh server replacing the last line show(col) with curdoc().add_root(col), but I didn't do that to emphasize that this is a client-side solution.
There is a way to dynamically resize bokeh charts with built in functionality. For example,
fig = plotting.figure(width=1200, height=900, title="Dynamic plot".format(chartType), sizing_mode='scale_width')
The key option being sizing_mode='scale_width'
The width and height commands serve as initial values. There are other options for sizing_mode so I would look into that.

Save an animation at the final step as a figure

I wrote a script which animates the results I obtained, using matplotlib.
Besides the animation I got, I wanted to save the figure at the final step of the animation; just before the animation is repeated. I defined a save-flag, to avoid the figure being saved over and over. You can see the simplified version of my code below:
#!/usr/bin/env python
import numpy as np
from matplotlib import pyplot as plt
import matplotlib.animation as animation
x = np.array(range(12))
y = np.array([i**2 for i in range(12)])
fig = plt.figure()
ax = plt.axes(xlim = (0,15), ylim = (0,150))
line, = ax.plot([],[], 'o-')
def init():
line.set_data([], [])
return line,
save_flag = False
def animate(i):
i = (i+1)%(len(x)+1)
line.set_data(x[0:i], y[0:i])
global save_flag
if (save_flag == False) and (i == (len(x)-1)):
print "\nThe figure is being saved!\n"
fig.savefig("foo" + ".png")
save_flag = True
return line,
ani = animation.FuncAnimation(fig, animate, repeat=True, blit=True, init_func=init)
plt.show()
If you run the script, you will probably see that, at the end of the first loop, the the animation becomes erroneous. This error is due to the blit, which is set True. However, if it is set to False, then the figure repeats itself as it should be.
Why is there such a problem; could it be a bug? (My Python version is 2.7.5+.)
Is there a better way to save a figure at the end of an animation?

matplotlib detect object upon mouse event

Is there any way to detect the matplotlib object the mouse is focused on ?
this a piece of code that illustrates what i want
self.canvas.mpl_connect("motion_notify_event", self.on_focus)
def on_focus(self, event):
# get mouse position in figure
figPos = (event.x,event.y)
# get mouse position in axes if focusing on an axes
axesPos = event.xdata, event.ydata
# get axes instance if mouse is focusing on an axes
axes = event.inaxes
# get object (any matplotlib object, Text, Box, ...) mouse is focused on
obj = event.??????
thanks
Try with Axes.hitlist:
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(range(10),range(10))
def on_focus(event):
print ax.hitlist(event)
fig.canvas.mpl_connect("motion_notify_event", on_focus)
plt.show()
But if you only want highlight you can use the built-in:
fig.canvas.mpl_connect("motion_notify_event",fig.canvas.onHilite)