从Tkinter主窗口弹出子窗口显示Matplotlib实时图表失败求助
问题:Tkinter弹出窗口中无法渲染实时Matplotlib图表
主窗口包含「SHOW & SEARCH」按钮,点击后弹出名为「jig status and search」的新窗口,期望在该窗口的图表区域显示实时更新的柱状图,但图表无法正常绘制。
用户提供的代码
from tkinter.ttk import Combobox from tkinter import * import pandas as pd import matplotlib.animation as animation from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg import matplotlib.pyplot as plt class controller(): def __init__(self, container,model): self.container = container self.model=model() self.container.title('Jig run count') self.container.geometry('280x140') def set_up(self): self.add_remove_bt.place(x=10, y=50) self.count_search_bt = Button(self.container, text=' SHOW & SEARCH ', command=self.show_and_search) self.count_search_bt.place(x=140, y=50) def show_and_search(self): show_and_search_windows=Tk() show_and_search_form=Tk.winfo_toplevel(show_and_search_windows) show_and_search_form.title("jig status and search") show_and_search_form.geometry('800x500') self.graph_area=Frame(show_and_search_form,width=450, height=300).place(x=5,y=5) self.search_area=Frame(show_and_search_form,width=300, height=300).place(x=460,y=5) self.table_area=Frame(show_and_search_form,width=760, height=100).place(x=5,y=310) self.fig, self.ax1=plt.subplots() self.canvas = FigureCanvasTkAgg(self.fig, self.graph_area) ani=animation.FuncAnimation(self.fig,self.plot_chart,interval=1000) self.canvas_widget = self.canvas.get_tk_widget() self.canvas_widget.pack() def plot_chart(self): df=pd.read_csv(self.model.df_path,index_col='Type') FAT_df=df.loc['FAT'].sort_values(by="running_time",ascending=False) print(FAT_df) x1=FAT_df['jig_name'].values y1=FAT_df['running_time'].values print(x1) print(y1) self.ax1.clear() self.ax1.bar(x1,y1,label="top jigs") self.ax1.show()
问题修复方案
1. 修正多窗口创建方式
Tkinter中创建子窗口必须使用Toplevel()而非Tk(),Tk()会初始化新的主循环,导致组件交互异常。直接基于主窗口创建Toplevel对象即可:
show_and_search_form = Toplevel(self.container)
2. 修复Frame赋值错误
Frame(...).place(...)返回None,因为place方法无返回值,导致self.graph_area指向空值,画布无法绑定到有效容器。拆分创建与布局步骤:
self.graph_area = Frame(show_and_search_form, width=450, height=300) self.graph_area.place(x=5, y=5) # 同理修复其他区域 self.search_area = Frame(show_and_search_form, width=300, height=300) self.search_area.place(x=460, y=5) self.table_area = Frame(show_and_search_form, width=760, height=100) self.table_area.place(x=5, y=310)
3. 正确绑定画布到Frame
将画布的父容器指定为self.graph_area,并使用place布局画布(避免与Frame的place冲突):
self.canvas = FigureCanvasTkAgg(self.fig, master=self.graph_area) self.canvas_widget = self.canvas.get_tk_widget() self.canvas_widget.place(x=0, y=0, width=450, height=300)
4. 移除错误的Axes方法
Matplotlib的Axes对象没有show()方法,更新图表后调用self.canvas.draw()刷新画布即可:
# 替换self.ax1.show() self.canvas.draw()
5. 保留动画对象引用
动画对象若未保存为实例变量,会被Python垃圾回收机制回收,导致动画停止。将ani改为实例属性:
self.ani = animation.FuncAnimation(self.fig, self.plot_chart, interval=1000)
修复后的完整代码
from tkinter.ttk import Combobox from tkinter import * import pandas as pd import matplotlib.animation as animation from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg import matplotlib.pyplot as plt class Controller(): def __init__(self, container, model): self.container = container self.model = model() self.container.title('Jig run count') self.container.geometry('280x140') # 补充原代码未定义的按钮,避免报错 self.add_remove_bt = Button(self.container, text='ADD/REMOVE') def set_up(self): self.add_remove_bt.place(x=10, y=50) self.count_search_bt = Button(self.container, text=' SHOW & SEARCH ', command=self.show_and_search) self.count_search_bt.place(x=140, y=50) def show_and_search(self): # 使用Toplevel创建子窗口 show_and_search_form = Toplevel(self.container) show_and_search_form.title("jig status and search") show_and_search_form.geometry('800x500') # 拆分Frame创建与布局 self.graph_area = Frame(show_and_search_form, width=450, height=300) self.graph_area.place(x=5, y=5) self.search_area = Frame(show_and_search_form, width=300, height=300) self.search_area.place(x=460, y=5) self.table_area = Frame(show_and_search_form, width=760, height=100) self.table_area.place(x=5, y=310) # 创建Matplotlib画布并绑定到graph_area self.fig, self.ax1 = plt.subplots(figsize=(4.5, 3)) # 匹配Frame尺寸 self.canvas = FigureCanvasTkAgg(self.fig, master=self.graph_area) self.canvas_widget = self.canvas.get_tk_widget() self.canvas_widget.place(x=0, y=0, width=450, height=300) # 保存动画引用,防止被回收 self.ani = animation.FuncAnimation(self.fig, self.plot_chart, interval=1000) def plot_chart(self): df = pd.read_csv(self.model.df_path, index_col='Type') FAT_df = df.loc['FAT'].sort_values(by="running_time", ascending=False) x1 = FAT_df['jig_name'].values y1 = FAT_df['running_time'].values self.ax1.clear() self.ax1.bar(x1, y1, label="top jigs") self.ax1.set_title("Top Jig Running Time") self.ax1.legend() # 刷新画布 self.canvas.draw() # 补充示例Model类,替换为实际业务逻辑 class Model: def __init__(self): self.df_path = "your_data.csv" # 替换为实际CSV文件路径 # 启动程序 if __name__ == "__main__": root = Tk() app = Controller(root, Model) app.set_up() root.mainloop()
内容的提问来源于stack exchange,提问作者Hoang Minh Nguyen
相关产品推荐
相关产品推荐

