Animation with a set number of iterations

Hello,
I’ve looked at the animation examples and was able to write something like this:

from graph_tool.all import *
from graph_tool.generation import graph_union
from gi.repository import Gtk, Gdk, GdkPixbuf, GObject, GLib
from numpy.random import *
import sys, os, os.path
import time

iternum = 100
i = 0

g = load_graph(f"ACH-000681_OLAPARIB_i1.xml")

graphs = [*range(iternum)]
for i in range(iternum):
    graphs[i] = load_graph(f"ACH-000681_OLAPARIB_i{i+1}.xml")

pos = sfdp_layout(g)
#pos = fruchterman_reingold_layout(g)
#pos = random_layout(g)


# We will filter out vertices which are in the "Recovered" state, by masking
# them using a property map.
removed = g.new_vertex_property("bool")

ON = [1,1,1,1] #white
OFF = [0,0,0,1] #black
INC = [0,20,0,1] #green
DEC = [20,0,0,1] #red

state = g.new_vertex_property("vector<double>")
for v in g.vertices():
    state[v] = ON

# Newly infected nodes will be highlighted in red

# If True, the frames will be dumped to disk as images.
offscreen = sys.argv[1] == "offscreen" if len(sys.argv) > 1 else False
max_count = 500
if offscreen and not os.path.exists("./frames"):
    os.mkdir("./frames")
if not offscreen:
    win = GraphWindow(g, pos, geometry=(500, 400),
                      edge_color=[0.6, 0.6, 0.6, 1],
                      vertex_fill_color=state,
                      vertex_halo_color=[0.8, 0, 0, 0.6])
else:
    count = 0
    win = Gtk.OffscreenWindow()
    win.set_default_size(500, 400)
    win.graph = GraphWidget(g, pos,
                            edge_color=[0.6, 0.6, 0.6, 1],
                            vertex_fill_color=state,
                            vertex_halo_color=[0.8, 0, 0, 0.6])
    win.add(win.graph)


def update_state():
    global i
    removed.a = False

    for v in g.vertices():
        if i != 0:
            if v in graphs[i].vertices():
                if graphs[i].vertex_properties["change"][v] > 0:
                    state[v] = graphs[i].vertex_properties["change"][v]*INC
                elif graphs[i].vertex_properties["change"][v] < 0:
                    state[v] = graphs[i].vertex_properties["change"][v]*DEC
                else:
                    state[v] = ON
            else:
                state[v] = OFF
        if state[v] == OFF:
            removed[v] = True
    time.sleep(1)

    # Filter out the recovered vertices
    g.set_vertex_filter(removed.t(lambda x: 1-x))
    
    # The following will force the re-drawing of the graph, and issue a
    # re-drawing of the GTK window.
    win.graph.regenerate_surface()
    win.graph.queue_draw()
    
    if offscreen:
        global count
        pixbuf = win.get_pixbuf()
        pixbuf.savev(r'./frames/sirs%06d.png' % count, 'png', [], [])
        if count > max_count:
            sys.exit(0)
        count += 1

    i = i+1
    if i == 100:
        return False

    return True

# Bind the function above as an 'idle' callback.
cid = GLib.idle_add(update_state)

# We will give the user the ability to stop the program by closing the window.win.connect("delete_event", Gtk.main_quit)
win.connect("delete_event", Gtk.main_quit)

# Actually show the window, and start the main loop.
win.show_all()
Gtk.main()

using the idle callback and updating the global variables i wanted to simulate a for loop, since the values i have are already stored in the graphs array with the sequence of graphs in that.
the problem is that it isn’t animating, it only shows a single frame of probably the last iteration:

how do i fix this?

OK, i’ve fixed the underlying problems with some help, but i still have an important problem, the background. i think the graphs are drawn on top of each other so the disappearing edges are not actually distinguished and the background is transparent textured but not actually transparent (like the image ive uploaded). i want the background to be solid white so it looks better on animation. in fact when i make the animation window while the code works fullscreen it actually has a white background and the disappearing edges are visible as well.

this is my code:

from graph_tool.all import *
from gi.repository import Gtk, GObject, GLib, Gdk
import sys, os

iternum = 100
i = 0
max_change = 10  # Maximum change value for scaling (adjust as needed)

# Load initial graph
g = load_graph("ACH-000681_OLAPARIB_i1.xml")

# Load all graph iterations
graphs = [load_graph(f"ACH-000681_OLAPARIB_i{i+1}.xml") for i in range(iternum)]

# Layout
pos = sfdp_layout(g)

# Vertex Color Properties
ON = [1, 1, 1, 1]  # White
OFF = [0, 0, 0, 1]  # Black

state = g.new_vertex_property("vector<double>")
remove_edge = g.new_edge_property("bool")  # Edge filter property

for v in g.vertices():
    state[v] = ON

# GTK Setup
offscreen = sys.argv[1] == "offscreen" if len(sys.argv) > 1 else False
max_count = 500
count = 0

if offscreen and not os.path.exists("./frames"):
    os.mkdir("./frames")

win = GraphWindow(g, pos, geometry=(500, 400), vertex_fill_color=state)

def update_state():
    global i, count
    if i >= iternum:
        if offscreen:
            print("Animation complete.")
        return False  # Stop after all frames are processed

    # Reset all filters
    g.clear_filters()

    current_graph = graphs[i]

    # Update node states
    for v in g.vertices():
        if i != 0:
            if v in current_graph.vertices():
                change_val = current_graph.vertex_properties["change"][v]

                if change_val > 0:
                    # Darker green for larger positive changes
                    green_intensity = max(0, 1 - (change_val / max_change))
                    state[v] = [0, green_intensity, 0, 1]  # Darker green as change increases
                elif change_val < 0:
                    # Darker red for larger negative changes
                    red_intensity = max(0, 1 - (-change_val / max_change))
                    state[v] = [red_intensity, 0, 0, 1]  # Darker red as change decreases
                else:
                    state[v] = ON
            else:
                state[v] = OFF  # Node disappears

    # Remove edges between ON nodes and OFF nodes
    for e in g.edges():
        source, target = e.source(), e.target()
        remove_edge[e] = state[source] == OFF or state[target] == OFF  # Remove edges involving OFF nodes

    g.set_edge_filter(remove_edge, inverted=True)  # Apply edge filter

    # Redraw
    win.graph.regenerate_surface()
    win.graph.queue_draw()

    # Offscreen frame saving logic
    if offscreen:
        window = win.get_window()
        if window:
            pixbuf = Gdk.pixbuf_get_from_window(window, 0, 0, 500, 400)
            if pixbuf:
                pixbuf.savev(f'./frames/frame_{count:06d}.png', 'png', [], [])
        if count > max_count:
            sys.exit(0)
        count += 1

    i += 1
    return True  # Continue animation

# Schedule animation updates
GLib.timeout_add(1000, update_state)

win.connect("delete_event", Gtk.main_quit)
win.show_all()
Gtk.main()

Can you help me please, it is a bit time sensitive, i can add needed files if necessary.