基于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)
关键修改说明
- 存储曲线对象:在
plotdata中,将每条绘制的Line2D对象存入self.curves列表,后续通过这个列表直接控制曲线可见性。 - 动态生成复选框:新增
update_checkboxes方法,根据当前self.curves的数量生成对应复选框,每个复选框绑定toggle_curve函数。 - 可见性切换逻辑:
toggle_curve遍历复选框变量,调用set_visible()设置对应曲线的显示/隐藏状态,最后刷新画布。 - 重构绘图初始化:将画布和子图的初始化单独放到
init_plot方法,避免重复创建导致的资源浪费。
内容的提问来源于stack exchange,提问作者asger rosenkranz
相关产品推荐
相关产品推荐

