1

I want to build an interface with certain real-time graphs showing the results of some experiments. For this, I decided to use a combination of glade(UI), gtk, python, and matplotlib. I was working with some basics and I was able to plot some real-time graphs.

Now, I have some trouble using Funcanimation for real-time animations. Below, the code import a glade file with four scrolled windows and I want to display some animation in each scrolled windows. I tired the animation without plotting inside the canvas (inside the scrolled window) and it works!. But when I tried to run this, the callback function by Funcanimation (update_line) is not even triggering. What is actually I'm doing wrong here. I am new to python as well.

Thanks

#!/usr/bin/env python
import sys
import os
import time
import psutil as p
import threading
import numpy as np
from gi.repository import Gtk
from gi.repository import GObject
from matplotlib.figure import Figure
import matplotlib.animation as animation
from matplotlib.backends.backend_gtk3cairo import FigureCanvasGTK3Cairo as FigureCanvas
import matplotlib.pyplot as plt




class windowSignals:
    def on_mainWindow_destroy(self, widget):
        Gtk.main_quit()

def main():
    builder = Gtk.Builder()
    builder.add_from_file("window.glade")
    builder.connect_signals(windowSignals())
    window = builder.get_object("mainWindow")
    sw = builder.get_object("scrolledWindow1")


    def update_line(num, data, line):
        data.pop(0)
        data.append(np.random.random())
        line.set_ydata(data)

        return line,


    fig1 = plt.figure()
    data = [0.0 for i in xrange(100)]
    l, = plt.plot(data, 'r-')

    plt.ylim(-1, 1)
    line_ani = animation.FuncAnimation(fig1, update_line, 25, fargs=(data, l), interval=50, blit=True)
    can = FigureCanvas(fig1)
    sw.add_with_viewport(can)
    can.draw()

    window.show_all()
    Gtk.main()

if __name__ == "__main__":
    main()
Warlock
  • 164
  • 3
  • 18
  • So did you try to return the funcanimation from the function and put `Gtk.main()` outside of it? – ImportanceOfBeingErnest Feb 06 '18 at 15:08
  • @unutbu Since every GUI uses an event loop, the argument is not clear to me. Why would `FuncAnimation` work in just any other GUI, but not GTK? What is special about GTK that would prevent `FuncAnimation` from working? – ImportanceOfBeingErnest Feb 06 '18 at 15:15
  • @unutbu Sorry, but what you are saying is basically that it would not be possible to use `FuncAnimation` in a GTK GUI. This is clearly wrong. There might be particularities involved which make this harder to use, but there is nothing that should prevent it from being used at all. Or, if there is, this would be a bug, one needs to investigate. – ImportanceOfBeingErnest Feb 06 '18 at 15:29
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/164628/discussion-between-unutbu-and-importanceofbeingernest). – unutbu Feb 06 '18 at 15:29
  • Hallo both (since i cannot tag you both), as ImportanceOfBeingErnest stated, i can't call plt.show() since im calling my own Gtk GUI – Warlock Feb 06 '18 at 16:05
  • @Warlock: I think ImportanceOfBeingErnest is correct. In particular, here you should use `fig = Figure()`, not `fig = plt.figure()`. And also create the Canvas before creating the FuncAnimation. Here is a [runnable example](https://ideone.com/bhc3nA). – unutbu Feb 06 '18 at 17:37
  • @ImportanceOfBeingErnest: It seems Warlock is saving a reference to the `FuncAnimation`. But your other two suggestions do apply. Would you like to reopen and post that as an answer? – unutbu Feb 06 '18 at 17:40
  • @unutbu As said, if there is no duplicate target, one should indeed reopen. Since I cannot test anything myself, I wouldn't provide an answer here. Why don't you provide one and summarize your findings which result from the discussion. If you feel like it you can make it a community answer. – ImportanceOfBeingErnest Feb 06 '18 at 17:44
  • Thanks to both.. – Warlock Feb 07 '18 at 12:49

1 Answers1

2

Many thanks to @ImportanceOfBeingErnest who deserves credit for basically everything that is correct in this post :)

Here is a slightly modified, runnable version of your code (without Glade) which uses FuncAnimation inside a GTK app.

Three things to note:

  • A reference to the FuncAnimation object must be kept lest the object (and its timer) be garbage collected.

  • The FigureCanvas should be created before the FuncAnimation, since FuncAnimation creates a timer by calling fig.canvas.new_timer(). If the canvas has not yet been created, fig.canvas is None and you get an AttributeError.

  • If Gtk is not your default backend, use matplotlib.figure.Figure instead of plt.figure here.


import numpy as np
from gi.repository import Gtk
from gi.repository import GObject
from matplotlib.figure import Figure
from matplotlib.backends.backend_gtk3cairo import FigureCanvasGTK3Cairo as FigureCanvas
import matplotlib.pyplot as plt
import matplotlib.animation as animation

class MyApp(object):
    def __init__(self):
        window = Gtk.Window()
        window.connect("delete-event", Gtk.main_quit)
        window.set_default_size(400, 400)
        sw = Gtk.ScrolledWindow()
        window.add(sw)

        self.fig = fig = Figure()
        self.canvas = FigureCanvas(fig)
        sw.add_with_viewport(self.canvas)

        ax = fig.add_subplot(111)
        data = np.zeros(100)
        self.line, = ax.plot(data, 'r-')
        ax.set_ylim(-1, 1)
        self.ani = animation.FuncAnimation(
            self.fig, self.update_line, interval=100, frames=50, repeat=True)

        window.show_all()

    def update_line(self, *args):
        data = 2*(np.random.random(100)-0.5)
        self.line.set_ydata(data)
        self.canvas.draw()
        return True

if __name__ == "__main__":
    app = MyApp()
    Gtk.main()
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677