0

I would like to make an equivalent of the function FuncAnimation from matplotlib.animation, in which I could control the current plotted data using the scrollbar.

Say you have a data array which contains data points to be plotted at each time i. When using FuncAnimation, you first need to define a function ( here animate(i) ) which will be called for each time i = 1 to len(data[:,0]) :

def animate(i):
    ax.plot(data[i,:])

anim = FuncAnimation(fig, animate, interval=100, frames=len(data[:,0]))
plt.draw()
plt.show()

but you cannot control the time i, like with a play/stop functionality. What I would like to do is to call the function animate(i), with i being the position of the scrollbar.

I found this example ( using the events from matplotlib: https://matplotlib.org/3.2.1/users/event_handling.html ) but the mpl_connect doesn't have a "scrollbar_event".

import tkinter
from random import randint
import matplotlib as plt
import numpy as np

from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg, NavigationToolbar2Tk)
# Implement the default Matplotlib key bindings.
from matplotlib.backend_bases import key_press_handler
from matplotlib.figure import Figure

root = tkinter.Tk()
root.wm_title("Embedding in Tk")

#create figure
fig = Figure(figsize=(5, 4), dpi=100)
ax = fig.add_axes([0,0,1,1])
ax.imshow(np.array([[0,10],[23,40]]))

#create canvas with figure
canvas = FigureCanvasTkAgg(fig, master=root)  # A tk.DrawingArea.

canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1)

def on_key_press(event):
    ax.clear()
    ax.imshow(np.array([[randint(0,30),randint(0,30)],[randint(0,30),randint(0,30)]]))
    canvas.draw_idle()
    key_press_handler(event, canvas)
    print("you pressed {}".format(event.key))

#connect canvas to event function
canvas.mpl_connect("key_press_event", on_key_press)

def _quit():
    root.quit()     # stops mainloop
    root.destroy()  # this is necessary on Windows to prevent
                    # Fatal Python Error: PyEval_RestoreThread: NULL tstate

button = tkinter.Button(master=root, text="Quit", command=_quit)
button.pack(side=tkinter.BOTTOM)

tkinter.mainloop()

2 Answers 2

1

Actually the scroll functionality is given by matplotlib widgets !! The example below works fine :

import matplotlib
import tkinter as Tk
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from myPytHeader import *


matplotlib.use('TkAgg')

root = Tk.Tk()
root.wm_title("Embedding in TK")

fig = plt.Figure(figsize=(8, 6))
canvas = FigureCanvasTkAgg(fig, root)
canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)


nDt = nbLines("grid.dat")
nDx = nbGridPoints("grid.dat")
grid = np.zeros( (nDt,nDx) ) ; loadData("grid.dat", grid)
valu = np.zeros( (nDt,nDx) ) ; loadData("valu.dat", valu)

ax=fig.add_subplot(111)
fig.subplots_adjust(bottom=0.25)

ax_time = fig.add_axes([0.12, 0.1, 0.78, 0.03])
s_time = Slider(ax_time, 'Time', 0, nDt, valinit=0, valstep=1)


def update(val):
    frame = int(s_time.val)
    ax.clear()
    ax.set(xlim=(-0.05, 1.05), ylim=(-0.05, 1.25))
    ax.grid()
    ax.scatter(grid[frame,:], valu[frame,:], color='b', marker='.')
    fig.canvas.draw_idle()

s_time.on_changed(update)

Tk.mainloop()
Sign up to request clarification or add additional context in comments.

Comments

0

After all these years I've found solutions to my problems here, I am in debt !!! Here is the final solution I came with. I hope it can be useful to someone someday somehow !!

import matplotlib
import numpy as np
import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.widgets import Slider
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg


# load data
nDt = 1000
nDx = 400
grd = np.zeros( (nDt,nDx) )
val = np.zeros( (nDt,nDx) )
for t in np.arange(nDt):
    for x in np.arange(nDx):
        grd[t,x] = x / nDx
        val[t,x] = (x / nDx) * (t/nDt) * np.sin(10 * 2*np.pi * (t-x)/nDt)


matplotlib.use('TkAgg')

root = tk.Tk()
root.wm_title("Embedding in TK")

fig = plt.Figure(figsize=(8, 6))
canvas = FigureCanvasTkAgg(fig, root)
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)

ax=fig.add_subplot(111)
fig.subplots_adjust(bottom=0.25)
ax.set(xlim=(-0.05, 1.05), ylim=(-1.05, 1.05))
ax.grid()
scat = ax.scatter(grd[0,:], val[0,:], color='b', marker='.')

ax_time = fig.add_axes([0.12, 0.1, 0.78, 0.03])
s_time = Slider(ax_time, 'Time', 0, nDt, valinit=0, valstep=1)
i_anim = 0
i_relative = 0
i_current = 0

def updateGraph(i):
    y_i = val[i,:]
    scat.set_offsets(np.c_[grd[i,:], y_i])


def updateFromAnim(i):
    global i_anim
    global i_current
    global i_relative
    i_anim = i
    i_current = i + i_relative
    s_time.set_val(i_current)
    updateGraph(i_current)

def updateFromScroll(val):
    global i_anim
    global i_current
    global i_relative
    i_relative = int(s_time.val) - i_anim
    i_current = int(s_time.val)
    updateGraph(i_current)

def onClick():
    global anim_running
    if anim_running:
        anim.event_source.stop()
        anim_running = False
    else:
        anim.event_source.start()
        anim_running = True

start_button = tk.Button(root, text="START/STOP", command=onClick)
start_button.pack()

anim_running = True
anim = FuncAnimation(fig, updateFromAnim, interval=100, frames=nDt)


s_time.on_changed(updateFromScroll)

tk.mainloop()

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.