W. Riv
W. Riv

Reputation: 63

Embedding Matplotlib live plot data from Arduino in tkinter canvas

I've only been using Python for a couple of weeks. I have no problems plotting the data coming in from the Arduino with Matplotlib. However the plot shows up as a pop-window and I would like that plot to only show up in a canvas in the root window of the GUI I'm making with tkinter. I've tried multiple combinations of things and I can't get it to work. If I just add the plot values to the code, let's say:

a.plot([1, 2, 3, 4, 5], [2, 3, 4, 5, 6, 7])

it works fine, so my main problem then is with the while loop when getting data from the Arduino. I've also tried the drawnow option to update the plot but I get the same exact result. Whatever I do I can't seem to get the plot from to stop showing up as a separate window.

Plot window with main GUI window in the back:

Plot window with main GUI window in the back

Here's the sample code that I'm using:

import serial
from tkinter import *
import numpy as np
from matplotlib import pyplot as plt
import matplotlib.animation as animation
from matplotlib import style
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg


root = Tk()
root.geometry('1200x700+200+100')
root.title('This is my root window')
root.state('zoomed')
root.config(background='#fafafa')


yar = []
plt.ion()
style.use('ggplot')
fig = plt.figure(figsize=(14, 4.5), dpi=100)
ax1 = fig.add_subplot(1, 1, 1)
ser = serial.Serial('com3', 9600)

def animate(i):
    while True:
        ser.reset_input_buffer()
        data = ser.readline().decode("utf-8")
        data_array = data.split(',')
        yvalue = float(data_array[1])
        yar.append(yvalue)
        print(yvalue)
        plt.ylim(0, 100)
        ax1.plot(yar, 'r', marker='o')
        plt.pause(0.0001)


plotcanvas = FigureCanvasTkAgg(fig, root, animate)
plotcanvas.get_tk_widget().grid(column=1, row=1)
ani = animation.FuncAnimation(fig, animate, interval=1000, blit=True)
plotcanvas.show()

root.mainloop()

Upvotes: 6

Views: 8938

Answers (1)

ImportanceOfBeingErnest
ImportanceOfBeingErnest

Reputation: 339250

The main loop of tk will take care of the animation, you therefore shouldn't use plt.ion() or plt.pause().

The animating function will be called every interval seconds. You cannot use a while True loop inside this function.

There is no reason whatsoever to supply the animating function to the FigureCanvasTkAgg.

Don't use blit=True unless you know what you're doing. With an interval of one second this is anyways not necessary.

Update the line instead of replotting it in every iteration step.

#import serial
from Tkinter import *
from matplotlib import pyplot as plt
import matplotlib.animation as animation
from matplotlib import style
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg


root = Tk()
root.geometry('1200x700+200+100')
root.title('This is my root window')
root.state('zoomed')
root.config(background='#fafafa')

xar = []
yar = []

style.use('ggplot')
fig = plt.figure(figsize=(14, 4.5), dpi=100)
ax1 = fig.add_subplot(1, 1, 1)
ax1.set_ylim(0, 100)
line, = ax1.plot(xar, yar, 'r', marker='o')
#ser = serial.Serial('com3', 9600)

def animate(i):
    #ser.reset_input_buffer()
    #data = ser.readline().decode("utf-8")
    #data_array = data.split(',')
    #yvalue = float(data_array[1])
    yar.append(99-i)
    xar.append(i)
    line.set_data(xar, yar)
    ax1.set_xlim(0, i+1)


plotcanvas = FigureCanvasTkAgg(fig, root)
plotcanvas.get_tk_widget().grid(column=1, row=1)
ani = animation.FuncAnimation(fig, animate, interval=1000, blit=False)

root.mainloop()

Upvotes: 6

Related Questions