python - 如何在两个图表之间切换并保持单选按钮和滑块更新正常工作?
问题描述
我使用 Tkinter 和 matplotlib 在 Python 中制作了一个图形查看器 GUI 程序,在其中我在两个图形之间切换。
我有三个问题我不知道如何解决:
- 移动停止更新的滑块后,无法更改单选按钮。
- 切换图表后无法更改单选按钮。
- 我想在带有 1 个子图和 2 个子图的图形之间切换,但是当我切换到带有滑块和单选栏的 2 个子图的图形时,我无法回到第一个。
我认为问题可能出在我更新滑块和单选按钮的方式上
这是代码:
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
# Seperated out config of plot to just do it once
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.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 # chessboard
self.Z2 = func3(X, Y)
def draw_graph_one(self):
self.plot_data()
self.ax.remove()
self.ax = plt.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()
self.ax.remove()
self.ax = plt.subplot(1, 2, 1)
self.ax = plt.imshow(self.Z1, cmap=plt.cm.gray, interpolation='nearest',
extent=self.extent)
self.ax = plt.subplot(1, 2, 2)
self.a = plt.imshow(self.Z2, cmap=plt.cm.viridis, alpha=.9, interpolation='bilinear',
extent=self.extent)
self.b = plt.imshow(self.Z1, cmap=plt.cm.gray, interpolation='nearest',
extent=self.extent)
self.canvas.draw()
plt.subplots_adjust(left=0.15, bottom=0.15)
slider_ax = plt.axes([0.06, 0.25, 0.0225, 0.5])
alfa_slider = Slider(slider_ax,
label="Transparency",
valmin=0,
valmax=1,
valinit=0,
orientation="vertical"
)
alfa_slider.on_changed(self.update_slider)
rax = plt.axes([0.06, 0.05, 0.15, 0.15])
radio = RadioButtons(rax, ('Reds', 'Greens', 'Blues', 'Oranges', 'Wistia', 'plasma', 'inferno'), active=0)
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,toolbar):
print("you pressed {}".format(event.key))
key_press_handler(event, self.canvas, toolbar)
def _quit(self):
self.master.quit() # stops mainloop
def switch_graphs(self):
# Need to call the correct draw, whether we're on graph one or two
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()
我试图在函数内部输入 update_slider 和 update_radio 函数,但我不能使用 self.canvas.draw()。.remove() 和 .cla() 也不会清除窗口。
解决方案
首先回答你的最后一个问题,你的大问题是你试图使用一个图形来显示两个单独的图形,而不了解 pyplot 的实际工作原理。正如我将在下面概述的那样,这样做是可能的,但是当您在图表之间切换时,您会丢失对图表所做的任何修改。我建议阅读pyplot 教程的使用多个轴和图形部分。与 MATLAB 和 pyplot 一样,关键点在于,所有绘图函数都应用于最后一个图形和轴。
当您调用时self.draw_graph_two()
,您创建的最后一个轴是RadioButtons
. 因此,pyplot 将这些轴作为当前轴。然后,当您第二次调用self.draw_graph_one()
时(第一次是在您初始化时),它会在当前图形和当前轴上绘制绘图,它们在哪里RadioButtons
。
要解决此问题,请在尝试绘制新图形之前调用andplt.clf()
的第一行。这将清除图形,允许从头开始绘制新图(这是您的两个绘制函数所做的)。这允许您在两个绘图之间连续切换,尽管每次切换绘图都会被清除并且对绘图的任何修改都会丢失。您也可以删除这些行,因为它们会导致错误并且不需要。draw_graph_one
draw_graph_two
self.ax.remove()
对于您的前两个问题,可以通过阅读RadioButtons
和Slider
此处的文档来回答这些问题。您必须保留对小部件的引用,以使它们保持响应。所以修改你draw_graph_two
的返回alfa_slider
并radio
保持这些小部件响应。或者,将它们声明为实例变量也会保留引用(self.alfa_slider
和self.radio
)。
所以你的工作图查看器是:
import matplotlib
import tkinter as tk
matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.backend_bases import key_press_handler
from matplotlib.widgets import Slider, RadioButtons
# Seperated out config of plot to just do it once
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 = tk.Frame(self.master)
self.fig, self.ax = config_plot()
self.graphIndex = 0
self.canvas = FigureCanvasTkAgg(self.fig, self.master)
self.config_window()
self.plot_data()
self.draw_graph_one()
self.frame.pack(expand=True, fill=tk.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=tk.TOP, fill=tk.BOTH, expand=1)
self.button = tk.Button(self.master, text="Quit", command=self._quit)
self.button.pack(side=tk.BOTTOM)
self.button_switch = tk.Button(self.master, text="Switch Graphs", command=self.switch_graphs)
self.button_switch.pack(side=tk.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 # chessboard
self.Z2 = func3(X, Y)
def draw_graph_one(self):
plt.clf()
self.ax = plt.imshow(self.Z2, cmap=plt.cm.viridis, alpha=.9, interpolation='bilinear',
extent=self.extent)
self.canvas.draw()
def draw_graph_two(self):
plt.clf()
self.ax = plt.subplot(1, 2, 1)
self.ax = plt.imshow(self.Z1, cmap=plt.cm.gray, interpolation='nearest',
extent=self.extent)
self.ax = plt.subplot(1, 2, 2)
self.a = plt.imshow(self.Z2, cmap=plt.cm.viridis, alpha=.9, interpolation='bilinear',
extent=self.extent)
self.b = plt.imshow(self.Z1, cmap=plt.cm.gray, interpolation='nearest',
extent=self.extent)
self.canvas.draw()
plt.subplots_adjust(left=0.15, bottom=0.15)
slider_ax = plt.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 = plt.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(1-alpha_)
self.canvas.draw()
def update_radio(self,label_):
self.b.set_cmap(label_)
self.canvas.draw()
def on_key_press(self,event,toolbar):
print("you pressed {}".format(event.key))
key_press_handler(event, self.canvas, toolbar)
def _quit(self):
self.master.quit() # stops mainloop
def switch_graphs(self):
# Need to call the correct draw, whether we're on graph one or two
self.graphIndex = (self.graphIndex + 1) % 2
if self.graphIndex == 0:
self.draw_graph_one()
else:
self.draw_graph_two()
def main():
root = tk.Tk()
matplotlibSwitchGraphs(root)
root.mainloop()
if __name__ == '__main__':
main()
注意我还做了一些其他的轻微修改。不要使用from tkinter import *
,你已经在上面导入了它,使用import *
是一个坏习惯。我颠倒了滑块,这样透明度才有意义。self.plot_data()
只调用一次,__init__
因为它不会改变。
推荐阅读
- python - 如何使用标签而不是问号在 requests.get() 中发送参数?
- python - Speedtest-cli 捕获输出
- higher-kinded-types - 双变量的实际例子?
- excel - 定义 Excel 求解器的范围
- javascript - 为什么while循环以字符串停止,而不是整数?
- python - 使用 matplotlib.imshow 从多个部分组成 rgb 图像
- r - 是否有计算生存概率的威布尔估计的函数
- python - 当有打印指令时,线程没有做他们的工作
- javascript - 如何将 Arduino 代码在线编译成 .hex 文件(与 Arduino 一起闪烁)?
- r - 如何编写具有固定和随机效应的线性模型