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

基于Tkinter的Python绘图:实现曲线显示切换功能

问题:Tkinter中动态切换任意数量曲线的显示与隐藏

我正在用Tkinter实现绘图功能,需要让用户能够切换单条曲线的显示/隐藏状态,方便对比多条曲线。例如当图中有5条曲线时,用户可切换某一条的可见性,隐藏后对比剩余4条,再次操作即可恢复显示。

目前已实现数据绘制并添加了按钮,但缺少核心的切换功能函数——该函数需要支持用户指定的任意循环数量(1到100均可)。同时也接受其他绘图方案建议,比如Seaborn或Matplotlib的相关方法(已了解隐藏整个绘图的方法,但不符合需求)。

核心难点在于循环数量不固定:如果用复选框绑定set_visible(self.var.get())这类方式,需要提前知晓绘图总数,而当前循环数量是随用户输入动态变化的,参考过相关方案但未完全解决。

TLDR:不清楚如何在循环数量动态变化的场景下,让用户自主控制对应循环曲线的可见性。


现有代码

import tkinter as tk
from tkinter import ttk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import pandas as pd  # 假设使用pandas处理Excel数据

class CVplotter(tk.Toplevel):
    def __init__(self,parent, NOC,ExcelData,ActiveMass):
        super().__init__(parent)
        
        self.ExcelData=ExcelData
        self.NOC = NOC
        self.NOP = len(self.NOC)
        self.ActiveMass = ActiveMass

        self.widgets()
        self.layout()
        self.plotdata()
        
    def plotdata(self):
        for j in list(self.NOC):
            print(f"dette er {j}")
            print(f"dette er = {self.NOC}")
            new_list = []
            new_list2 = []
            rest_list = []
            for i in range(100):
                temp = self.ExcelData["Cycle:Command"][self.ExcelData["Cycle:Command"] == f"{j} : {i}"].index.tolist()
                if temp:
                    new_list.append(temp[0])
                    print(f"running on cycle {j} {new_list}")
            for m in new_list:
                if self.ExcelData["Cmd Type"][m] == "CC-CVC" or self.ExcelData["Cmd Type"][m] == "CCD":
                    print("Is this slow")
                    new_list2.append(m)
                else:
                    print("or this")
                    rest_list.append(m)
            a = 0
            for n in new_list2:
                self.vmV = self.ExcelData["V(mV)"][n+1:rest_list[a]]/1000
                self.cmAh = self.ExcelData["C(mAh)"][n+1:rest_list[a]]/self.ActiveMass

                self.b.plot(self.vmV,self.cmAh)
                a += 1    

        
    def showhide(self):
        self.fig = Figure(figsize=(6,6))
        self.b = self.fig.add_subplot(111)
        
        self.b.set_title (f"Plotting Cycles{self.NOC}", fontsize=16)
        self.b.set_ylabel("Y", fontsize=14)
        self.b.set_xlabel("X", fontsize=14)
        self.canvas = FigureCanvasTkAgg(self.fig,master = self)
        self.canvas.get_tk_widget().pack()
        self.canvas.draw()

        
    def widgets(self):
        self.ShowHideV = tk.IntVar()
        self.ShowHideE = ttk.Entry(self,textvariable=self.ShowHideV)    
        self.ShowHideB = ttk.Button(self,text="Show/Hide plot",command = self.showhide)
            
    def layout(self):
        self.fig = Figure(figsize=(6,6))
        self.b = self.fig.add_subplot(111)
        
        self.b.set_title (f"Plotting Cycles{self.NOC}", fontsize=16)
        self.b.set_ylabel("Y", fontsize=14)
        self.b.set_xlabel("X", fontsize=14)
        self.canvas = FigureCanvasTkAgg(self.fig,master = self)
        self.canvas.get_tk_widget().pack()
        self.canvas.draw()
        
        self.ShowHideE.pack()
        self.ShowHideB.pack()

解决方案:动态绑定曲线与复选框

针对循环数量不固定的问题,核心思路是存储所有绘制的曲线对象,再根据曲线数量动态生成对应复选框,绑定可见性切换逻辑,修改后的完整代码如下:

import tkinter as tk
from tkinter import ttk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import pandas as pd

class CVplotter(tk.Toplevel):
    def __init__(self,parent, NOC,ExcelData,ActiveMass):
        super().__init__(parent)
        
        self.ExcelData=ExcelData
        self.NOC = NOC
        self.NOP = len(self.NOC)
        self.ActiveMass = ActiveMass

        # 新增:存储曲线对象和复选框变量
        self.curves = []
        self.check_vars = []

        self.init_plot()
        self.widgets()
        self.layout()
        self.plotdata()
        
    def init_plot(self):
        # 初始化绘图区域,避免重复创建画布/子图
        self.fig = Figure(figsize=(6,6))
        self.b = self.fig.add_subplot(111)
        self.b.set_title(f"Plotting Cycles{self.NOC}", fontsize=16)
        self.b.set_ylabel("Specific Capacity (mAh/g)", fontsize=14)
        self.b.set_xlabel("Voltage (V)", fontsize=14)
        self.canvas = FigureCanvasTkAgg(self.fig, master=self)
        
    def plotdata(self):
        self.curves.clear()  # 清空旧曲线数据
        curve_idx = 0
        for j in list(self.NOC):
            new_list = []
            new_list2 = []
            rest_list = []
            for i in range(100):
                temp = self.ExcelData["Cycle:Command"][self.ExcelData["Cycle:Command"] == f"{j} : {i}"].index.tolist()
                if temp:
                    new_list.append(temp[0])
            for m in new_list:
                if self.ExcelData["Cmd Type"][m] in ("CC-CVC", "CCD"):
                    new_list2.append(m)
                else:
                    rest_list.append(m)
            a = 0
            for n in new_list2:
                vmV = self.ExcelData["V(mV)"][n+1:rest_list[a]]/1000
                cmAh = self.ExcelData["C(mAh)"][n+1:rest_list[a]]/self.ActiveMass
                # 绘制曲线并存储Line2D对象
                line = self.b.plot(vmV, cmAh, label=f"Cycle {j}-{a}")[0]
                self.curves.append(line)
                a += 1
                curve_idx += 1
        self.b.legend()
        self.canvas.draw()
        # 绘制完成后更新复选框
        self.update_checkboxes()
        
    def toggle_curve(self):
        # 根据复选框状态切换对应曲线可见性
        for idx, var in enumerate(self.check_vars):
            if idx < len(self.curves):
                self.curves[idx].set_visible(var.get())
        self.canvas.draw()

    def update_checkboxes(self):
        # 清空旧复选框
        for widget in self.winfo_children():
            if isinstance(widget, ttk.Checkbutton):
                widget.destroy()
        self.check_vars.clear()
        # 动态生成新复选框
        for curve in self.curves:
            var = tk.BooleanVar(value=True)
            self.check_vars.append(var)
            chk = ttk.Checkbutton(self, text=curve.get_label(), variable=var, command=self.toggle_curve)
            chk.pack(side=tk.LEFT, padx=5, pady=5)
            
    def widgets(self):
        # 保留刷新按钮(可选)
        self.RefreshB = ttk.Button(self, text="Refresh Plot", command=self.plotdata)
            
    def layout(self):
        self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
        self.RefreshB.pack(pady=10)

关键修改说明

  1. 存储曲线对象:在plotdata中,将每条绘制的Line2D对象存入self.curves列表,后续通过这个列表直接控制曲线可见性。
  2. 动态生成复选框:新增update_checkboxes方法,根据当前self.curves的数量生成对应复选框,每个复选框绑定toggle_curve函数。
  3. 可见性切换逻辑:toggle_curve遍历复选框变量,调用set_visible()设置对应曲线的显示/隐藏状态,最后刷新画布。
  4. 重构绘图初始化:将画布和子图的初始化单独放到init_plot方法,避免重复创建导致的资源浪费。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 17:55:29