在模拟过程中更新 matplotlib 图表

8
我试图实现一个在环境模拟过程中更新的 matplotlib 图形。以下类在我的测试中运行良好,但在实际环境使用时无法更新图形。在环境模拟期间,该图形会显示,但没有线条被绘制。我猜测是 .draw() 没有按照我想象的方式工作。有人能找出问题所在吗?
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"])

当我运行代码时,我通常只写:env.simulate()

这里代码可以正常运行:

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)

你在哪个环境下工作?(Spyder,Jupyter等,Linux命令行等)你是否正在寻找适用于Qt5独立绘图窗口的工具? - Han-Kwang Nienhuys
还有,您能提供一个 MCVE 吗?或者即使不是“minimal”,至少是一个CVE吗?问题中的代码无法正常运行。 - Han-Kwang Nienhuys
请定义“我的环境”。 - predmod
2个回答

6
原来你的代码按照设定方式运行良好。下面是您提供的代码唯一存在的问题:
self.vis.graphs_dict["VariableY"]["graph"].x.append(self.internal_step)
self.vis.graphs_dict["VariableY"]["graph"].y.append(150)

您正在绘制一条线并正确地更新画布,但是您不断追加完全相同的(x, y)坐标。因此模拟确实更新了线条,但线条简化为一个点。您的测试用例没有出现这种情况。您可以通过添加以下代码行来使用您的代码运行一个虚拟示例:
self.internal_step += 5

在添加新的x点之前,您将产生一条水平线。
如果这解决了您的问题,请告诉我。

很遗憾,这并不能解决我的问题,因为每次调用.append(self.internal_step)时,self.internal_step都不相同。 - Phil
能否提供更多细节?当我运行您的代码并修改 internal_step 时,您的代码可以正常工作,并正确地实时更新图形。 - craymichael

3

这可能不是最优雅的方法,但是我在想要在执行期间更新绘图时使用 plt.pause(0.1)。它会暂停 0.1 秒,并强制显示所有绘图。(作为额外奖励,在 IPython 的 %debug 中也可以工作)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接