Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating a matplotlib figure during simulation

I try to implement a matplotlib figure that updates during the simulation of my environment.

The following Classes works fine in my test but doesn't update the figure when I use it in my environment. During the simulation of the environment, the graph is shown, but no lines are plotted.

My guess is that .draw() is not working how I think it does.

Can anyone figure out the issue here?

class Visualisation:
    def __init__(self, graphs):
        self.graphs_dict = {}
        for graph in graphs:
            fig = plt.figure()
            ax = fig.add_subplot(111)
            line, = ax.plot(graph.x, graph.y, 'r-')
            self.graphs_dict[graph.title] = {"fig": fig, "ax": ax, "line": line, "graph": graph}
            self.graphs_dict[graph.title]["fig"].canvas.draw()
        plt.ion()
        plt.show()

    def update(self, graph):
        graph = self.graphs_dict[graph.title]["graph"]
        self.graphs_dict[graph.title]["line"].set_xdata(graph.x)
        self.graphs_dict[graph.title]["line"].set_ydata(graph.y)
        self.graphs_dict[graph.title]["fig"].canvas.flush_events()
        x_lim, y_lim = self.get_lim(graph)
        self.graphs_dict[graph.title]["ax"].set_xlim(x_lim)
        self.graphs_dict[graph.title]["ax"].set_ylim(y_lim)
        self.graphs_dict[graph.title]["fig"].canvas.draw()

    @staticmethod
    def get_lim(graph):
        if graph.x_lim is None:
            x = np.array(graph.x)
            y = np.array(graph.y)
            x_lim = [x.min(), x.max()]
            y_lim = [y.min(), y.max()]
        else:
            x_lim = graph.x_lim
            y_lim = graph.y_lim
        return x_lim, y_lim

class Graph:
    def __init__(self, title, x, y, x_label="", y_label=""):
        """
        Sets up a graph for Matplotlib
        Parameters
        ----------
        title : String
            Title of the plot
        x : float
        y : float
        x_label : String
            x Label
        y_label : String
            y Label
        """
        self.title = title
        self.x = x
        self.y = y
        self.x_label = x_label
        self.y_label = y_label
        self.x_lim, self.y_lim = None, None

    def set_lim(self, x_lim, y_lim):
        self.x_lim = x_lim
        self.y_lim = y_lim

class Environment:
    def __init__(self, [..], verbose=0):
        """verbose : int
            0 - No Visualisation
            1 - Visualisation
            2 - Visualisation and Logging"""            

        self.vis = None
        self.verbose = verbose         

                    [......]

    def simulate(self):
        for _ in range(self.n_steps):
            [...]
            self.visualize()

    def visualize(self):
        if self.verbose == 1 or self.verbose == 2:
            if self.vis is None:
                graphs = [Graph(title="VariableY", x=[], y=[])]
                graphs[0].set_lim(x_lim=[0, 100], y_lim=[0, 300])
                self.vis = Visualisation(graphs=graphs)
            else:
                self.vis.graphs_dict["VariableY"]["graph"].x.append(self.internal_step)
                self.vis.graphs_dict["VariableY"]["graph"].y.append(150)
                self.vis.update(self.vis.graphs_dict["VariableY"]["graph"])

When I run the code I more or less just write: env.simulate().

The code runs fine here:

class TestSingularVisualisation(unittest.TestCase):
    def setUp(self):
        self.graph = Graph(title="Test", x=[0], y=[0])
        self.vis = Visualisation(graphs=[self.graph])

class TestSingleUpdate(TestSingularVisualisation):
    def test_repeated_update(self):
        for i in range(5):
            self.graph.x.append(i)
            self.graph.y.append(np.sin(i))
            self.vis.update(self.graph)
            time.sleep(1)
like image 375
Phil Avatar asked Jun 16 '20 12:06

Phil


1 Answers

Turns out your code works the way it is set up. Here is the sole problem with the code you provided:

self.vis.graphs_dict["VariableY"]["graph"].x.append(self.internal_step)
self.vis.graphs_dict["VariableY"]["graph"].y.append(150)

You are plotting a line and correctly updating the canvas, however, you keep appending the exact same (x, y) coordinate. So the simulation does update the line, but the line simplifies to a point. Your test case does not do this. You can run a dummy example with your code by simply adding a line like this:

self.internal_step += 5

before adding the new x point, and you will produce a horizontal line.

Let me know if this solves your problem.

like image 120
craymichael Avatar answered Sep 23 '22 00:09

craymichael