You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Tkinter界面中使用Matplotlib绘图的问题求助

解决方案

方案1:独立弹出绘图窗口(符合首选需求)

核心思路是用Tkinter的Toplevel窗口承载Matplotlib图表,替代原生plt.show()窗口,实现:

  • 主窗口关闭时绘图窗口同步关闭
  • 按钮在绘图窗口打开时禁用,关闭后自动恢复
  • 避免重复打开多个绘图窗口

修改后的完整代码示例:

import tkinter as tk
from tkinter import ttk
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from functools import partial

class YourApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Main Window")
        
        # 初始化Pandastable(假设已完成相关配置)
        # self.pt = Table(self.root, ...)
        # self.pt.pack(fill=tk.BOTH, expand=True)
        
        # 底部按钮容器
        self.button_frame = ttk.Frame(self.root)
        self.button_frame.pack(side=tk.BOTTOM, fill=tk.X, padx=5, pady=5)
        
        self.button1 = ttk.Button(self.button_frame, width=25, text="Plot 0")
        self.button1.pack(side="left", padx=2)
        
        # 记录绘图窗口实例,防止重复打开
        self.plot_window = None

    def func1(self, dictionary):
        # 关闭已存在的绘图窗口
        if self.plot_window is not None and self.plot_window.winfo_exists():
            self.plot_window.destroy()
        
        # 禁用按钮
        self.button1['state'] = 'disabled'
        
        # 创建独立绘图窗口
        self.plot_window = tk.Toplevel(self.root)
        self.plot_window.title("Antenna A Ch 1 RSSI")
        
        # 绘制箱线图
        df = pd.DataFrame.from_dict(dictionary)
        fig, ax = plt.subplots(figsize=(8, 5))
        df.boxplot(column='RSSI', by='SenderID', ax=ax)
        ax.set_title('Antenna A Ch 1 RSSI')
        ax.set_xlabel('SenderID')
        ax.set_ylabel('RSSI')
        plt.suptitle('')  # 移除默认的分组标题
        
        # 将图表嵌入Toplevel窗口
        canvas = FigureCanvasTkAgg(fig, master=self.plot_window)
        canvas.draw()
        canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 绑定绘图窗口关闭事件,恢复按钮状态
        def on_plot_close():
            self.button1['state'] = 'normal'
            self.plot_window.destroy()
            self.plot_window = None
        
        self.plot_window.protocol("WM_DELETE_WINDOW", on_plot_close)
        
        # 主窗口关闭时同步关闭绘图窗口
        self.root.protocol("WM_DELETE_WINDOW", self.on_main_close)

    def on_main_close(self):
        if self.plot_window is not None and self.plot_window.winfo_exists():
            self.plot_window.destroy()
        self.root.destroy()

    def clicked(self, event):
        try:
            rclicked = self.pt.get_row_clicked(event)
            cclicked = self.pt.get_col_clicked(event)
            clicks = (rclicked, cclicked)
        except: 
            print('Error getting clicked row/column')
        if clicks:
            try:
                ROW = self.pt.model.getRecordAtRow(clicks[0])
                # 用partial绑定参数,避免lambda的变量捕获问题
                self.button1.configure(command=partial(
                    self.func1, 
                    self.unfiltered_df.loc[self.unfiltered_df['ReceiverID'] == ROW[0]]
                ))
            except:
                print('No record found at:', clicks)

if __name__ == "__main__":
    root = tk.Tk()
    app = YourApp(root)
    root.mainloop()

关键改进点:

  • 用Toplevel替代原生Matplotlib窗口,实现窗口生命周期与主窗口绑定
  • 新增self.plot_window跟踪窗口实例,防止重复打开
  • 绘图窗口关闭时触发回调恢复按钮状态
  • 主窗口关闭前先关闭绘图窗口,避免残留进程
  • 使用functools.partial绑定按钮命令参数,解决lambda的变量捕获BUG

方案2:主窗口内嵌入图表(布局整洁版)

若不需要独立窗口,可在主窗口中划分专门的图表区域,避免布局混乱:

import tkinter as tk
from tkinter import ttk
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from functools import partial

class YourApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Main Window")
        
        # 顶部容器:Pandastable + 图表区域
        self.top_container = ttk.Frame(self.root)
        self.top_container.pack(fill=tk.BOTH, expand=True)
        
        # Pandastable放置区域
        # self.pt_frame = ttk.Frame(self.top_container)
        # self.pt_frame.pack(fill=tk.BOTH, expand=True)
        # self.pt = Table(self.pt_frame, ...)
        # self.pt.pack(fill=tk.BOTH, expand=True)
        
        # 图表容器(初始为空)
        self.chart_container = ttk.Frame(self.top_container)
        self.chart_container.pack(fill=tk.BOTH, expand=True)
        
        # 底部按钮容器
        self.button_frame = ttk.Frame(self.root)
        self.button_frame.pack(side=tk.BOTTOM, fill=tk.X, padx=5, pady=5)
        
        self.button1 = ttk.Button(self.button_frame, width=25, text="Plot 0")
        self.button1.pack(side="left", padx=2)
        
        # 记录当前图表实例
        self.current_chart = None

    def func2(self, dictionary):
        # 禁用按钮
        self.button1['state'] = 'disabled'
        
        # 清除旧图表
        if self.current_chart is not None:
            self.current_chart.get_tk_widget().destroy()
        
        # 绘制新图表
        df = pd.DataFrame.from_dict(dictionary)
        fig, ax = plt.subplots(figsize=(8, 4))
        df.boxplot(column='RSSI', by='SenderID', ax=ax)
        ax.set_title('Antenna A Ch 1 RSSI')
        ax.set_xlabel('SenderID')
        ax.set_ylabel('RSSI')
        plt.suptitle('')
        
        # 嵌入图表到容器
        canvas = FigureCanvasTkAgg(fig, master=self.chart_container)
        canvas.draw()
        canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        self.current_chart = canvas
        
        # 添加关闭图表按钮
        close_btn = ttk.Button(self.chart_container, text="关闭图表", command=self.close_chart)
        close_btn.pack(side=tk.BOTTOM, pady=2)

    def close_chart(self):
        if self.current_chart is not None:
            self.current_chart.get_tk_widget().destroy()
            self.current_chart = None
        self.button1['state'] = 'normal'

    def clicked(self, event):
        try:
            rclicked = self.pt.get_row_clicked(event)
            cclicked = self.pt.get_col_clicked(event)
            clicks = (rclicked, cclicked)
        except: 
            print('Error getting clicked row/column')
        if clicks:
            try:
                ROW = self.pt.model.getRecordAtRow(clicks[0])
                self.button1.configure(command=partial(
                    self.func2, 
                    self.unfiltered_df.loc[self.unfiltered_df['ReceiverID'] == ROW[0]]
                ))
            except:
                print('No record found at:', clicks)

if __name__ == "__main__":
    root = tk.Tk()
    app = YourApp(root)
    root.mainloop()

关键改进点:

  • 用top_container和button_frame分离布局,图表独立放置在专门容器中
  • 每次绘图前清除旧图表,保持界面整洁
  • 新增"关闭图表"按钮,点击后恢复按钮状态
  • 布局逻辑清晰,主窗口关闭时图表自动销毁

内容的提问来源于stack exchange,提问作者Oshua

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 18:33:19