I would like to use matplotlib in my code but without FigureCanvasKivyAgg. I use kivy and I tried this but the plot it is not visible. This is my code:
import matplotlib
import matplotlib.pyplot as plt
x = [1,2,3,4]
y = [5,10,12,9]
plt.plot(x,y)
class ClassForPlot(Screen):
def __init__(self, **kw):
super().__init__(**kw)
self._app = App.get_running_app()
plt.show()
plt.show() doesn't work in init?
Related
I am trying to update a plot with serial data with matplotlib FuncAnimation. I am using the following example to embed the plot in a wx app.
Embedding a matplotlib figure inside a WxPython panel
However, the plot is not updated and only the initial plot is displayed.
In fact, the update function is never executed which is checked with print statements in try and except blocks. You may see the script here.
import wx
from matplotlib.figure import Figure as Fig
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg as NavigationToolbar
from collections import deque
import serial
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib as mlp
import numpy as np
# Class that inherits wx.Panel. The purpose is to embed it into
# a wxPython App. That part can be seen in main()
class Serial_Plot(wx.Panel):
def __init__(self, parent, strPort, id=-1, dpi=None, **kwargs):
super().__init__(parent, id=id, **kwargs)
self.figure = Fig(figsize=(20,20))
self.ax = self.figure.add_subplot(111)
self.canvas = FigureCanvas(self, -1, self.figure)
self.plot_data, = self.ax.plot([1,2,3,4],[1,2,3,4])
self.toolbar = NavigationToolbar(self.canvas)
self.toolbar.Realize()
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.canvas, 1, wx.EXPAND)
sizer.Add(self.toolbar, 0, wx.RIGHT | wx.EXPAND)
self.SetSizer(sizer)
self.Fit()
# Serial communication
self.ser = serial.Serial(strPort, 115200)
# Serial data initialized as deque. The serial readings from arduino
# are set to be one value per line.
self.vals = deque()
# matplotlib function animation
anim = animation.FuncAnimation(self.figure, self.update,
interval=20)
plt.show()
self.close
def update(self, i):
try:
print('trying')
# read serial line
data = float(self.ser.readline().decode('utf-8'))
print(data)
self.vals.append(data)
# update plot data
self.plot_data.set_data(range(len(self.vals)), self.vals)
except:
print('oops')
pass
return self.plot_data
def close(self):
# close serial
self.ser.flush()
self.ser.close()
def main():
app = wx.App(False)
frame = wx.Frame(None, -1, "WX APP!")
demo_plot = Serial_Plot(frame,'COM3')
frame.Show()
app.MainLoop()
if __name__ == "__main__":
main()
As I said in my previous answer(Embedding matplotlib FuncAnimation in wxPython: Unwanted figure pop-up), trying to use animation.FuncAnimation() and plt.show() within wxpython is not going to work, because you have 2 main.loops.
If we cull all of the wx.python from your original question and pare it down to the basics, we get this:
from collections import deque
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import random
vals = deque()
figure = plt.figure(figsize=(20,20))
ax = plt.axes(xlim=(0, 1000), ylim=(0, 5000))
plot_data, = ax.plot([], [])
def update(i):
data = float(random.randint(1000, 5000))
vals.append(data)
plot_data.set_data(range(len(vals)), vals)
return plot_data
anim = animation.FuncAnimation(figure, update, interval=20)
plt.show()
It works because the animation function is controlled by plt.show() i.e. matplotlib but once you introduce wxpython, it controls the main.loop not matplotlib.
I believe that you will have to use a wxpython timer to control the serial device read, feeding data into the plot, as demonstrated in my previous answer.
Embedding matplotlib FuncAnimation in wxPython: Unwanted figure pop-up
I did the following example code just to test how to integrate an animated matplotlib plot with pygtk. However, I get some unexpected behaviors when I run it.
First, when I run my program and click on the button (referred to as button1 in the code), there is another external blank window which shows up and the animated plot starts only after closing this window.
Secondly, when I click on the button many times, it seems that there is more animations which are created on top of each other (which gives the impression that the animated plot speeds up). I have tried to call animation.FuncAnimation inside a thread (as you can see in the comment at end of the function on_button1_clicked), but the problem still the same.
Thirdly, is it a good practice to call animation.FuncAnimation in a thread to allow the user to use the other functions of the gui ? Or should I rather create a thread inside the method animate (I guess this will create too many threads quickly) ? I am not sure how to proceed.
Here is my code:
import gtk
from random import random
import numpy as np
from multiprocessing.pool import ThreadPool
import matplotlib.pyplot as plt
import matplotlib.animation as animation
#from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas
#from matplotlib.backends.backend_gtkcairo import FigureCanvasGTKCairo as FigureCanvas
class HelloWorld:
def __init__(self):
interface = gtk.Builder()
interface.add_from_file('interface.glade')
self.dialog1 = interface.get_object("dialog1")
self.label1 = interface.get_object("label1")
self.entry1 = interface.get_object("entry1")
self.button1 = interface.get_object("button1")
self.hbox1 = interface.get_object("hbox1")
self.fig, self.ax = plt.subplots()
self.X = [random() for x in range(10)]
self.Y = [random() for x in range(10)]
self.line, = self.ax.plot(self.X, self.Y)
self.canvas = FigureCanvas(self.fig)
# self.hbox1.add(self.canvas)
self.hbox1.pack_start(self.canvas)
interface.connect_signals(self)
self.dialog1.show_all()
def gtk_widget_destroy(self, widget):
gtk.main_quit()
def on_button1_clicked(self, widget):
name = self.entry1.get_text()
self.label1.set_text("Hello " + name)
self.ani = animation.FuncAnimation(self.fig, self.animate, np.arange(1, 200), init_func=self.init, interval=25, blit=True)
'''
pool = ThreadPool(processes=1)
async_result = pool.apply_async(animation.FuncAnimation, args=(self.fig, self.animate, np.arange(1, 200)), kwds={'init_func':self.init, 'interval':25, 'blit':True} )
self.ani = async_result.get()
'''
plt.show()
def animate(self, i):
# Read XX and YY from a file or whateve
XX = [random() for x in range(10)] # just as an example
YY = [random() for x in range(10)] # just as an example
self.line.set_xdata( XX )
self.line.set_ydata( YY )
return self.line,
def init(self):
self.line.set_ydata(np.ma.array(self.X, mask=True))
return self.line,
if __name__ == "__main__":
HelloWorld()
gtk.main()
I am trying to run this code below but it is not working properly. I've followed the documentation from matplotlib and wonder what is wrong with this simple code below. I am tryting to animate this into jupyter notebook with anaconda distro. My python version is 2.7.10.
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
fig = plt.figure()
def init():
m = np.zeros(4800)
m[0] = 1.6
return m
def animate(i):
for a in range(1,4800):
m[a] = 1.6
m[a-1] = 0
return m
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=200, interval=20, blit=True)
plt.show()
You need to create an actual plot. Just updating a NumPy array is not enough.
Here is an example that likely does what you intend. Since it is necessary to access the same objects at multiple places, a class seems better suited as it allows to access instance attributes via self:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
class MyAni(object):
def __init__(self, size=4800, peak=1.6):
self.size = size
self.peak = peak
self.fig = plt.figure()
self.x = np.arange(self.size)
self.y = np.zeros(self.size)
self.y[0] = self.peak
self.line, = self.fig.add_subplot(111).plot(self.x, self.y)
def animate(self, i):
self.y[i - 1] = 0
self.y[i] = self.peak
self.line.set_data(self.x, self.y)
return self.line,
def start(self):
self.anim = animation.FuncAnimation(self.fig, self.animate,
frames=self.size, interval=20, blit=False)
if __name__ == '__main__':
ani = MyAni()
ani.start()
plt.show()
Is it possible to create a chaco plot with latex text? For example, if we wanted latex symbols in the title of this exampe:
from traits.api import HasTraits, Instance
from traitsui.api import View, Item
from chaco.api import Plot, ArrayPlotData
from enable.component_editor import ComponentEditor
from numpy import linspace, sin
class LinePlot(HasTraits):
plot = Instance(Plot)
traits_view = View(
Item('plot',editor=ComponentEditor(), show_label=False),
width=500, height=500, resizable=True, title="Chaco Plot")
def __init__(self):
super(LinePlot, self).__init__()
x = linspace(-14, 14, 100)
y = sin(x) * x**3
plotdata = ArrayPlotData(x=x, y=y)
plot = Plot(plotdata)
plot.plot(("x", "y"), type="line", color="blue")
plot.title = "sin(x) * x^3"
self.plot = plot
if __name__ == "__main__":
LinePlot().configure_traits()
I tried replacing title with $sin(x)^3$ to no avail, and wondered if this was possible? Screenshot below:
No, it is not (it is a matplotlib feature). But you could try to use unicode symbols for easy cases.
I'd like a rectangle to appear every time the user clicks on the line. I've gotten this to work procedurally like in this example: http://www.daniweb.com/software-development/python/code/216648 but once I implemented iPython compatibility and started using classes, I could no longer use the app.MainLoop() without the program crashing. How do I refresh a wx.Frame object from inside a class? Why does the self.figure.canvas.draw() not work?
The code is below. Open ipython with the -pylab option. x = [-10,10] and y = x are decent parameters for this problem.
import wx
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigCanv
from pylab import *
import IPython.ipapi
ip = IPython.ipapi.get()
import sys
class MainCanvas(wx.Frame):
""" Set up the canvas and plot on which the rectangle will lie """
def __init__(self, *args):
wx.Frame.__init__(self,None,-1, size=(550,350))
self.x = args[0]
self.y = args[1]
self.figure = plt.figure()
self.axes = self.figure.add_subplot(111)
self.axes.plot(*args)
self.line, = self.axes.plot(self.x, self.y, picker = 3,
visible = False)
self.canvas = FigCanv(self, -1, self.figure)
self.rect = patches.Rectangle((0, 0), 2, 2, visible=True)
self.axes.add_patch(self.rect)
self.figure.canvas.mpl_connect('pick_event', self.onPick)
def onPick(self, event):
""" Move rectangle to last click on line """
self.rect.set_x(event.mouseevent.xdata)
self.rect.set_y(event.mouseevent.ydata)
self.rect.set_visible(True)
print "rect x: ", self.rect.get_x()
print "rect y: ", self.rect.get_y()
self.figure.canvas.draw()
def run_this_plot(self, arg_s=''):
""" Run in iPython
Examples
In [1]: import demo
In [2]: runplot x y <z>
Where x, y, and z are numbers of any type
"""
args = []
for arg in arg_s.split():
try:
args.append(self.shell.user_ns[arg])
except KeyError:
raise ValueError("Invalid argument: %r" % arg)
mc = MainCanvas(*args)
ip.expose_magic("runplot", run_this_plot)
Thanks!
--Erin
It seems likely that matplotlib is set to use a backend other than wx. Try either setting this in the matplotlibrc file, or it can be set in the program (but it must be set before matplotlib is imported). Instructions are here.