Francesco
Francesco

Reputation: 121

How to trigger again a callback function before the first call for that function has ended, if that function is an animation

Good morning,

What I am aiming for:

Module:

Attempt n1:

Problem for attempt n1:

Code for attempt n1 is basically this one

Attempt n2:

Here it is:

import tkinter as tk # Import tkinter
from tkinter import ttk # Import ttk
import threading
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from random import randint



def button():
    thread = threading.Thread(target=func)
    thread.start()

def func():
    fig = plt.figure()
    ax = fig.add_subplot(xlim=(0, 1),  ylim=(0, 1))
    point, = ax.plot([], [],  '-')
    CumulativeX,   CumulativeY=[], []
    def animate(i):
        CumulativeX.append(randint(0, 10)/10),  CumulativeY.append(randint(0, 10)/10)  
        point.set_data(CumulativeX,CumulativeY)
        return point
    ani = animation.FuncAnimation(fig, animate, interval=1000)
    plt.show()


win = tk.Tk() # Create instance of the Tk class
aButton = ttk.Button(win, text="Click Me!", command=button)
aButton.grid(column=0, row=0) # Adding a Button
win.mainloop() #  Start GUI

Upvotes: 1

Views: 149

Answers (1)

FiddleStix
FiddleStix

Reputation: 3731

Combining the example from jfaccioni's comment and your question code, we get something like the below (which works for me on Python 3.10.7)...

import tkinter
from random import randint

import matplotlib.animation as animation
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure

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

fig = Figure(figsize=(5, 4), dpi=100)
t = np.arange(0, 3, 0.01)
ax = fig.add_subplot()
(line,) = ax.plot(t, 2 * np.sin(2 * np.pi * t))
ax.set_xlabel("time [s]")
ax.set_ylabel("f(t)")

CumulativeX, CumulativeY = [], []

canvas = FigureCanvasTkAgg(fig, master=root)  # A tk.DrawingArea.
canvas.draw()

restart = [True]


def button():
    restart[0] = True


def update(frame, CumulativeX, CumulativeY, restart, *fargs):

    if restart[0] is True:
        CumulativeY.clear()
        CumulativeX.clear()
        restart[0] = False
    else:
        CumulativeX.append(randint(0, 10) / 10)
        CumulativeY.append(randint(0, 10) / 10)

    line.set_data(CumulativeX, CumulativeY)


_ = animation.FuncAnimation(
    fig, update, interval=1000, fargs=(CumulativeX, CumulativeY, restart)
)

# pack_toolbar=False will make it easier to use a layout manager later on.
toolbar = NavigationToolbar2Tk(canvas, root, pack_toolbar=False)
toolbar.update()

button_quit = tkinter.Button(master=root, text="Quit", command=root.destroy)
button_restart = tkinter.Button(master=root, text="Click Me!", command=button)

# Packing order is important. Widgets are processed sequentially and if there
# is no space left, because the window is too small, they are not displayed.
# The canvas is rather flexible in its size, so we pack it last which makes
# sure the UI controls are displayed as long as possible.
button_quit.pack(side=tkinter.BOTTOM)
button_restart.pack(side=tkinter.BOTTOM)
toolbar.pack(side=tkinter.BOTTOM, fill=tkinter.X)
canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=True)

tkinter.mainloop()

Though it's quite verbose, it's not very complicated:

  • we are updating the image once per second
  • the button() function sets restart[0] equal to True
  • the update() function with either add another random line segment (if restart[0] is False) or clear the figure and start again (if restart[0] is True)

Upvotes: 1

Related Questions