Python figureCanvasTkAgg animation - python

I'm using Python 3.6 on Ubuntu 17.10. This is an MVC that I tried to do for my actually long code.
I have to make an animated graph of the convolution of two signals, but when I graph, the screen turns gray and the graph just appears.
from tkinter import *
from tkinter import ttk
import matplotlib
matplotlib.use("TkAgg")
from matplotlib import style
style.use("ggplot")
from matplotlib import pyplot as plt
import matplotlib.animation as animation
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import numpy as np
from scipy import signal as sn
import time
tim = np.arange(0,10)
tim1 = np.arange(0,10)
root = Tk()
root.geometry('1280x700')
root.resizable(width = False, height = False)
root.configure(bg = fondo)
root.title('Convolusión')
f2 = Figure(figsize=(3,2), dpi=100)
k2 = f2.add_subplot(1, 1, 1)
plot = FigureCanvasTkAgg(f2, root)
plot.show()
plot.get_tk_widget().place(x=50,y=470)
k3.clear()
ycc = sn.convolve(s1,s2,mode="full")
t = np.arange(min(tim)+min(tim1),max(tim)+max(tim1)+1)
i=0
tim = [0]*len(t)
y = [0]*len(t)
for x in t:
tim[i] = t[i]
y[i] = ycc[i]
k3.clear()
k3.plot(tim,y)
plot = FigureCanvasTkAgg(self.f3, self.root)
plot.show()
plot.get_tk_widget().place(x=450,y=470)
i=i+1
plt.pause(0.1)

Related

How do you use matplotlib in python?

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?

Put a Matplotlib plot as a QGraphicsItem/into a QGraphicsView

So I have a very basic plot layout described below (with x and y values changed for brevity):
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
import numpy as np
figure = Figure()
axes = figure.gca()
axes.set_title(‘My Plot’)
x=np.linspace(1,10)
y=np.linspace(1,10)
y1=np.linspace(11,20)
axes.plot(x,y,’-k’,label=‘first one’)
axes.plot(x,y1,’-b’,label=‘second one’)
axes.legend()
axes.grid(True)
And I have designed a GUI in QT designer that has a GraphicsView (named graphicsView_Plot) that I would like to put this graph into and I would like to know how I would go about putting this graph into the GraphicsView. Barring starting over and using the QT based graphing ability I don’t really know how (if possible) to put a matplotlib plot into this graphics view. I know it would be a super simple thing if I can convert it into a QGraphicsItem as well, so either directly putting it into the GraphicsView or converting it to a QGraphicsItem would work for me.
You have to use a canvas that is a QWidget that renders the matplotlib instructions, and then add it to the scene using addWidget() method (or through a QGraphicsProxyWidget):
import sys
from PyQt5 import QtWidgets
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import numpy as np
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
scene = QtWidgets.QGraphicsScene()
view = QtWidgets.QGraphicsView(scene)
figure = Figure()
axes = figure.gca()
axes.set_title("My Plot")
x = np.linspace(1, 10)
y = np.linspace(1, 10)
y1 = np.linspace(11, 20)
axes.plot(x, y, "-k", label="first one")
axes.plot(x, y1, "-b", label="second one")
axes.legend()
axes.grid(True)
canvas = FigureCanvas(figure)
proxy_widget = scene.addWidget(canvas)
# or
# proxy_widget = QtWidgets.QGraphicsProxyWidget()
# proxy_widget.setWidget(canvas)
# scene.addItem(proxy_widget)
view.resize(640, 480)
view.show()
sys.exit(app.exec_())

How do I change the subplot parameters having a Figure in a window in Tkinter ? As for example I want to add the xlabel and ylabel

I have an application that should get data from a sensor into a live graph, a subplot that is added into a Figure.
I have now a problem after adding the subplot that I don't know how to change the plot parameters as xlabel, ylabel. This works if I import plt, but not if I import a Figure that will be further added to the window in Tkinter.
#file livegraph.py
import matplotlib.animation as animation
import datetime
#this is a draft for the liveGraph class
#the objective is to get live data from a sensor
class liveGraph:
#by default define the interval as being 1000 mSec
intervalAnim = 1000
def __init__(self,fig):
self.xax = 0
self.xs = []
self.ys = []
self.ax = fig.add_subplot(111)
self.ax.set_xlabel('teeeest')
#fig.title('Graph test')
#fig.set_xlabel("Time")
#fig.ylabel("% SMS")
self.anim = animation.FuncAnimation(fig, self.animate, interval = self.intervalAnim)
def animate(self,i):
self.xs.append(self.xax)
self.ys.append(datetime.datetime.now().second)
self.xax+=1
self.ax.clear()
self.ax.plot(self.xs,self.ys)
if self.xax > 90:
self.anim.event_source.stop()
from tkinter import *
from matplotlib import style
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from livegraph import liveGraph
# Define the main_screen as a tkinter
app_screen = Tk() # create a GUI window
app_screen.geometry("1920x1080") # set the configuration of GUI window
app_screen.resizable(width=True,height=True)
app_screen.title("Testare izolator") # set the title of GUI window
style.use('bmh')
#figure represents the graphic part of the system
figure = Figure(figsize=(10, 5), facecolor='white',frameon=True)
figure.suptitle('This is the figure title', fontsize=12)
#figure.add_gridspec(10,10)
#this are some parameters that I can easily change if I am using plt
# plt.title('Graph test')
#plt.xlabel("Time")
#plt.ylabel("% SMS")
#x = [ 0, 1, 2, 3, 4 ]
#y = [ 0, 1, 2, 3, 4 ]
#lines = plt.plot(x, y)
#plt.grid()
#plt.axis([0,10,0,10])
#plt.setp(lines, color= "b")
canvas = FigureCanvasTkAgg(figure, app_screen)
canvas.get_tk_widget().pack(side=TOP, anchor =NW, padx=100, pady=10)
newAnimation = liveGraph(figure)
app_screen.mainloop() # start the GUI
You should use self.ax. to add elements
self.ax.set_xlabel('teeeest')
self.ax.set_title('Graph test')
self.ax.set_xlabel("Time")
self.ax.set_ylabel("% SMS")
but then there is other problem because self.ax.clear() removes these elements.
First method:
If you use self.ax.clear() then you remove labels and you have to put labels again and again
def animate(self, i):
self.xs.append(self.xax)
#self.ys.append(datetime.datetime.now().second)
self.ys.append(random.randint(0, 10))
self.xax += 1
self.ax.clear()
self.ax.plot(self.xs,self.ys)
if self.xax > 90:
self.anim.event_source.stop()
self.ax.set_xlabel('teeeest')
self.ax.set_title('Graph test')
self.ax.set_xlabel("Time")
self.ax.set_ylabel("% SMS")
Second method:
To add elements only once you have to remove self.ax.clear() and instead of plot() you should create empty plot in `init
self.ax = fig.add_subplot(111)
self.ax.set_xlabel('teeeest')
self.ax.set_title('Graph test')
self.ax.set_xlabel("Time")
self.ax.set_ylabel("% SMS")
self.line, = self.ax.plot([], [])
and in animation use set_data() to update data in existing plot
self.line.set_data(self.xs, self.ys)
but it will not rescale plot and you will have to do it manually (if you want to rescale it)
self.ax.relim() # recalculate limits
self.ax.autoscale_view(True,True,True) # rescale using limits
Full code for first method
import matplotlib.animation as animation
import datetime
import random
#this is a draft for the liveGraph class
#the objective is to get live data from a sensor
class liveGraph:
#by default define the interval as being 1000 mSec
intervalAnim = 1000
def __init__(self, fig):
self.xax = 0
self.xs = []
self.ys = []
self.ax = fig.add_subplot(111)
self.ax.set_xlabel('teeeest')
self.anim = animation.FuncAnimation(fig, self.animate, interval=self.intervalAnim)
def animate(self, i):
self.xs.append(self.xax)
#self.ys.append(datetime.datetime.now().second)
self.ys.append(random.randint(0, 10))
self.xax += 1
self.ax.clear()
self.ax.plot(self.xs,self.ys)
if self.xax > 90:
self.anim.event_source.stop()
self.ax.set_title('Graph test')
self.ax.set_xlabel("Time")
self.ax.set_ylabel("% SMS")
from tkinter import *
from matplotlib import style
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
#from livegraph import liveGraph
# Define the main_screen as a tkinter
app_screen = Tk() # create a GUI window
app_screen.geometry("1920x1080") # set the configuration of GUI window
app_screen.resizable(width=True, height=True)
app_screen.title("Testare izolator") # set the title of GUI window
style.use('bmh')
#figure represents the graphic part of the system
figure = Figure(figsize=(10, 5), facecolor='white', frameon=True)
figure.suptitle('This is the figure title', fontsize=12)
#figure.add_gridspec(10,10)
#this are some parameters that I can easily change if I am using plt
#plt.title('Graph test')
#plt.xlabel("Time")
#plt.ylabel("% SMS")
#x = [ 0, 1, 2, 3, 4 ]
#y = [ 0, 1, 2, 3, 4 ]
#lines = plt.plot(x, y)
#plt.grid()
#plt.axis([0,10,0,10])
#plt.setp(lines, color= "b")
canvas = FigureCanvasTkAgg(figure, app_screen)
canvas.get_tk_widget().pack(side=TOP, anchor=NW, padx=100, pady=10)
newAnimation = liveGraph(figure)
#canvas.draw()
app_screen.mainloop() # start the GUI
Full code for second method
import matplotlib.animation as animation
import datetime
import random
#this is a draft for the liveGraph class
#the objective is to get live data from a sensor
class liveGraph:
#by default define the interval as being 1000 mSec
intervalAnim = 1000
def __init__(self, fig):
self.xax = 0
self.xs = []
self.ys = []
self.ax = fig.add_subplot(111)
self.ax.set_xlabel('teeeest')
self.ax.set_title('Graph test')
self.ax.set_xlabel("Time")
self.ax.set_ylabel("% SMS")
# create empty plot at start
self.line, = self.ax.plot([], [])
self.anim = animation.FuncAnimation(fig, self.animate, interval=self.intervalAnim)
def animate(self, i):
self.xs.append(self.xax)
#self.ys.append(datetime.datetime.now().second)
self.ys.append(random.randint(0, 2))
self.xax += 1
# update data in existing plot
self.line.set_data(self.xs, self.ys)
# rescale plot (if you need it)
self.ax.relim() # recalculate limits
self.ax.autoscale_view(True,True,True) # rescale using limits
if self.xax > 90:
self.anim.event_source.stop()
from tkinter import *
from matplotlib import style
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
#from livegraph import liveGraph
# Define the main_screen as a tkinter
app_screen = Tk() # create a GUI window
app_screen.geometry("1920x1080") # set the configuration of GUI window
app_screen.resizable(width=True, height=True)
app_screen.title("Testare izolator") # set the title of GUI window
style.use('bmh')
#figure represents the graphic part of the system
figure = Figure(figsize=(10, 5), facecolor='white', frameon=True)
figure.suptitle('This is the figure title', fontsize=12)
#figure.add_gridspec(10,10)
#this are some parameters that I can easily change if I am using plt
#plt.title('Graph test')
#plt.xlabel("Time")
#plt.ylabel("% SMS")
#x = [ 0, 1, 2, 3, 4 ]
#y = [ 0, 1, 2, 3, 4 ]
#lines = plt.plot(x, y)
#plt.grid()
#plt.axis([0,10,0,10])
#plt.setp(lines, color= "b")
canvas = FigureCanvasTkAgg(figure, app_screen)
canvas.get_tk_widget().pack(side=TOP, anchor=NW, padx=100, pady=10)
newAnimation = liveGraph(figure)
#canvas.draw()
app_screen.mainloop() # start the GUI

tkinter keep a handle alive

Hey there I have the following code:
import matplotlib as mpl
import numpy as np
import sys
if sys.version_info[0] < 3:
import Tkinter as tk
else:
import tkinter as tk
import matplotlib.backends.tkagg as tkagg
from matplotlib.backends.backend_agg import FigureCanvasAgg
def draw_figure(canvas, figure, loc=(0, 0)):
""" Draw a matplotlib figure onto a Tk canvas
loc: location of top-left corner of figure on canvas in pixels.
Inspired by matplotlib source: lib/matplotlib/backends/backend_tkagg.py
"""
figure_canvas_agg = FigureCanvasAgg(figure)
figure_canvas_agg.draw()
figure_x, figure_y, figure_w, figure_h = figure.bbox.bounds
figure_w, figure_h = int(figure_w), int(figure_h)
photo = tk.PhotoImage(master=canvas, width=figure_w, height=figure_h)
# Position: convert from top-left anchor to center anchor
canvas.create_image(loc[0] + figure_w/2, loc[1] + figure_h/2, image=photo)
# Unfortunately, there's no accessor for the pointer to the native renderer
tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2)
# Return a handle which contains a reference to the photo object
# which must be kept live or else the picture disappears
return photo
# Create a canvas
w, h = 300, 200
window = tk.Tk()
window.title("A figure in a canvas")
canvas = tk.Canvas(window, width=w, height=h)
canvas.pack()
# Generate some example data
X = np.linspace(0, 2 * np.pi, 50)
Y = np.sin(X)
# Create the figure we desire to add to an existing canvas
fig = mpl.figure.Figure(figsize=(2, 1))
ax = fig.add_axes([0, 0, 1, 1])
ax.plot(X, Y)
# Keep this handle alive, or else figure will disappear
fig_x, fig_y = 100, 100
fig_photo = draw_figure(canvas, fig, loc=(fig_x, fig_y))
fig_w, fig_h = fig_photo.width(), fig_photo.height()
# Add more elements to the canvas, potentially on top of the figure
canvas.create_line(200, 50, fig_x + fig_w / 2, fig_y + fig_h / 2)
canvas.create_text(200, 50, text="Zero-crossing", anchor="s")
Code from here:
https://matplotlib.org/2.1.1/gallery/user_interfaces/embedding_in_tk_canvas_sgskip.html
However I would like to draw different figures in a function. But when I try to do that my handle always dies and I do not see any figure on my tkinter....
Any ideas how to do keep a handle alive inside a function?

Changing the image of a matplotlib button widget

I have a matplotlib button widget with an image. After clicking the button, I want to change the image to something else. I tried this:
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
from matplotlib.widgets import Button
FIGURE = plt.figure()
ICON_PLAY = mpimg.imread('./ui/images/play.png')
ICON_PAUSE = mpimg.imread('./ui/images/pause.png')
def play(event):
print "Starting"
start_button.image = ICON_PAUSE
start_button = Button(plt.axes([0.1, 0.1, 0.8, 0.8]), '', image=ICON_PLAY)
start_button.on_clicked(play)
plt.show()
The event handler is called, but the image is not changed. Does someone know how to change the image of a matplotlib button widget after construction?
The image of a matplotlib.widget.Button is an imshow plot in the axes of the button. Hence you may get the existing image from the axes via button_axes.images[0] and set new data on it, button_axes.images[0].set_data(ICON_PAUSE).
import matplotlib.pyplot as plt
from matplotlib.widgets import Button
fig = plt.figure()
ICON_PLAY = plt.imread('https://i.stack.imgur.com/ySW6o.png')
ICON_PAUSE = plt.imread("https://i.stack.imgur.com/tTa3H.png")
def play(event):
button_axes.images[0].set_data(ICON_PAUSE)
fig.canvas.draw_idle()
button_axes = plt.axes([0.3, 0.3, 0.4, 0.4])
start_button = Button(button_axes, '', image=ICON_PLAY)
start_button.on_clicked(play)
plt.show()

Categories