Tkinter+matplotlib切换图表时如何保证单选框与滑块正常更新?
修复后完整可运行代码
import matplotlib import tkinter as Tk matplotlib.use("TkAgg") import matplotlib.pyplot as plt import numpy as np from tkinter import * from matplotlib.backends.backend_tkagg import ( FigureCanvasTkAgg, NavigationToolbar2Tk) from matplotlib.backend_bases import key_press_handler from matplotlib.widgets import Slider, RadioButtons def config_plot(): fig, ax = plt.subplots() ax.set(xlabel='time (s)', ylabel='voltage (mV)', title='Graph One') return (fig, ax) class matplotlibSwitchGraphs: def __init__(self, master): self.master = master self.frame = Frame(self.master) self.fig, self.ax = config_plot() self.graphIndex = 0 self.canvas = FigureCanvasTkAgg(self.fig, self.master) # 预声明控件实例属性,避免被垃圾回收 self.alfa_slider = None self.radio = None self.config_window() self.draw_graph_one() self.frame.pack(expand=YES, fill=BOTH) def config_window(self): self.canvas.mpl_connect("key_press_event", self.on_key_press) toolbar = NavigationToolbar2Tk(self.canvas, self.master) toolbar.update() self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1) self.button = Button(self.master, text="Quit", command=self._quit) self.button.pack(side=BOTTOM) self.button_switch = Button(self.master, text="Switch Graphs", command=self.switch_graphs) self.button_switch.pack(side=BOTTOM) def plot_data(self): def func3(x, y): return (1 - x / 2 + x ** 5 + y ** 3) * np.exp(-(x ** 2 + y ** 2)) dx, dy = 0.05, 0.05 x = np.arange(-3.0, 3.0, dx) y = np.arange(-3.0, 3.0, dy) X, Y = np.meshgrid(x, y) self.extent = np.min(x), np.max(x), np.min(y), np.max(y) self.Z1 = np.add.outer(range(8), range(8)) % 2 self.Z2 = func3(X, Y) def draw_graph_one(self): self.plot_data() # 清空画布所有axes,包括之前的滑块、单选框区域 self.fig.clear() ax = self.fig.add_subplot(111) self.ax = ax.imshow(self.Z2, cmap=plt.cm.viridis, alpha=.9, interpolation='bilinear', extent=self.extent) self.canvas.draw() def draw_graph_two(self): self.plot_data() # 清空画布所有axes self.fig.clear() ax1 = self.fig.add_subplot(1, 2, 1) ax1.imshow(self.Z1, cmap=plt.cm.gray, interpolation='nearest', extent=self.extent) ax2 = self.fig.add_subplot(1, 2, 2) self.a = ax2.imshow(self.Z2, cmap=plt.cm.viridis, alpha=.9, interpolation='bilinear', extent=self.extent) self.b = ax2.imshow(self.Z1, cmap=plt.cm.gray, interpolation='nearest', extent=self.extent) self.fig.subplots_adjust(left=0.15, bottom=0.15) slider_ax = self.fig.add_axes([0.06, 0.25, 0.0225, 0.5]) # 控件存为实例属性避免回收 self.alfa_slider = Slider(slider_ax, label="Transparency", valmin=0, valmax=1, valinit=0, orientation="vertical" ) self.alfa_slider.on_changed(self.update_slider) rax = self.fig.add_axes([0.06, 0.05, 0.15, 0.15]) # 控件存为实例属性避免回收 self.radio = RadioButtons(rax, ('Reds', 'Greens', 'Blues', 'Oranges', 'Wistia', 'plasma', 'inferno'), active=0) self.radio.on_clicked(self.update_radio) self.canvas.draw() def update_slider(self,alpha_): self.b.set_alpha(alpha_) self.canvas.draw() def update_radio(self,label_): self.b.set_cmap(label_) self.canvas.draw() def on_key_press(self,event): print("you pressed {}".format(event.key)) key_press_handler(event, self.canvas, self.canvas.toolbar) def _quit(self): self.master.quit() self.master.destroy() def switch_graphs(self): self.graphIndex = (self.graphIndex + 1) % 2 if self.graphIndex == 0: self.draw_graph_one() else: self.draw_graph_two() def main(): root = Tk() matplotlibSwitchGraphs(root) root.mainloop() if __name__ == '__main__': main()
核心修改说明
- 滑块、单选框控件存为实例属性,避免方法执行完后被Python垃圾回收导致回调失效,解决拖动滑块、点击单选框无响应的问题
- 每次绘制新图表前调用
self.fig.clear()清空画布上所有axes,包括滑块、单选框的专属axes,不会有残留元素影响布局,解决无法切回单子图的问题 - 所有绘图操作都绑定到实例的figure对象,不使用plt全局方法,避免画布元素混乱
- 修正了按键事件回调的参数错误,避免按键触发时报错
内容的提问来源于stack exchange,提问作者Gerard
相关产品推荐
相关产品推荐

