如何在wxFrame的Matplotlib嵌入面板中实现动态竖线动画?
解决wxPython中Matplotlib动画在面板内不生效的问题
你的核心问题在于动画绑定的是plt新建的独立画布,而非Plot面板内的Matplotlib画布,且未在动画更新时刷新wx面板的画布,同时动画对象未被保留引用导致被垃圾回收。以下是修复后的完整代码:
# -*- coding: utf-8 -*- import wx import matplotlib as mpl from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg as NavigationToolbar import numpy as np from matplotlib.animation import FuncAnimation class Inputs_Panel(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 300), style=wx.TAB_TRAVERSAL) bSizer = wx.BoxSizer(wx.VERTICAL) self.m_show_plot_button = wx.Button(self, wx.ID_ANY, u"启动动画", wx.DefaultPosition, wx.DefaultSize, 0) bSizer.Add(self.m_show_plot_button, 0, wx.ALL | wx.EXPAND, 5) self.SetSizer(bSizer) self.Layout() # Connect Events self.m_show_plot_button.Bind(wx.EVT_BUTTON, self.m_show_plot_buttonOnButtonClick) def m_show_plot_buttonOnButtonClick(self, event): # 获取右侧Plot面板的画布和轴对象 vis_panel = self.Parent.Parent.m_vis_panel ax1 = vis_panel.ax1 figure = vis_panel.figure # 清空之前的绘图,避免重复绘制 ax1.clear() x = np.arange(0, 150, 1) y = np.arange(0, 150, 1) ax1.plot(x, y) ax1.set_xlim([0, 150]) ax1.set_ylim([0, 150]) # 在Plot面板的轴上创建竖线 v_line_GUI = ax1.axvline(0, color="cornflowerblue") def animate(i): # 更新竖线位置 v_line_GUI.set_xdata([i]) # 刷新wx面板内的Matplotlib画布 vis_panel.canvas.draw_idle() # 创建动画并绑定到Plot面板的画布,同时保留引用避免被回收 vis_panel.ani = FuncAnimation(figure, animate, frames=x.shape[0], interval=500, repeat=False) class Plot(wx.Panel): def __init__(self, parent, id=-1, dpi=None, **kwargs): wx.Panel.__init__(self, parent, id=id, **kwargs) self.figure = mpl.figure.Figure(dpi=dpi, figsize=(2, 2)) self.ax1 = self.figure.add_subplot(121) self.ax2 = self.figure.add_subplot(122) self.canvas = FigureCanvas(self, -1, self.figure) self.toolbar = NavigationToolbar(self.canvas) self.toolbar.Realize() # 添加动画对象属性,用于保存动画引用 self.ani = None sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.canvas, 1, wx.EXPAND) sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND) self.SetSizer(sizer) class MyFrame(wx.Frame): def __init__(self, parent): wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="Matplotlib动画演示", pos=wx.DefaultPosition, size=wx.Size(800, 600), style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL) self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) bSizer = wx.BoxSizer(wx.VERTICAL) self.m_splitter = wx.SplitterWindow(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.SP_3D) self.m_splitter.Bind(wx.EVT_IDLE, self.m_splitterOnIdle) self.m_input_panel = Inputs_Panel(self.m_splitter) self.m_vis_panel = Plot(self.m_splitter) self.m_splitter.SplitVertically(self.m_input_panel, self.m_vis_panel, 200) bSizer.Add(self.m_splitter, 1, wx.EXPAND, 5) self.SetSizer(bSizer) self.Layout() self.Centre(wx.BOTH) def m_splitterOnIdle(self, event): self.m_splitter.SetSashPosition(200) self.m_splitter.Unbind(wx.EVT_IDLE) if __name__ == "__main__": app = wx.App(redirect=False) frame = MyFrame(None) frame.Show(True) app.MainLoop()
关键修改说明:
- 移除了
plt.subplots()和plt.show(),改用Plot面板内已创建的figure和ax1对象,避免创建独立窗口。 - 在动画更新函数
animate中调用vis_panel.canvas.draw_idle(),强制刷新wx面板内的Matplotlib画布。 - 在Plot类中添加
self.ani属性,保存动画对象的引用,防止Python垃圾回收机制销毁动画。 - 添加了
ax1.clear(),避免每次点击按钮时重复绘制曲线。
内容的提问来源于stack exchange,提问作者TMoover
相关产品推荐
相关产品推荐

