我正在尝试使用Tkinter.Scale生成一个可更改matplotlib中数据的滑块,但是在实时更新绘图而不需要每次都重新生成绘图窗口的过程中,我遇到了问题。
如果我以这种方式运行代码,则效果很好,但是每次我移动难以从视觉上聚焦的滑块时,它都会创建一个新窗口。
import matplotlib.pyplot as plt
import numpy as np
import tkinter as tk
master = tk.Tk()
def update(val):
plt.close()
global idx
idx = np.array(w.get())
t1 = np.arange(0.0, 5.0, 0.1)
a1 = np.sin(idx*np.pi *t1)
a2 = np.sin((idx/2)*np.pi*t1)
a3 = np.sin((idx/4)*np.pi*t1)
a4 = np.sin((idx/8)*np.pi*t1)
"""Plotting of data"""
fig, (ax1, ax2, ax3, ax4) = plt.subplots(4, sharey = False) # create figure
plt.tight_layout()
ax1.plot(t1, a1)
ax2.plot(t1, a2)
ax3.plot(t1, a3)
ax4.plot(t1, a4)
w = tk.Scale(master, from_=0, to=10, command = update)
w.pack()
tk.mainloop()
我希望滑块每次都简单地重新绘制数据,但是,当我移动命令以创建该函数之前的图形时,如下所示,当我移动滑块时,它不再更新图形,而只是当滑块关闭时。
import matplotlib.pyplot as plt
import numpy as np
import tkinter as tk
fig, (ax1, ax2, ax3, ax4) = plt.subplots(4, sharey = False) # create figure
plt.tight_layout()
master = tk.Tk()
def update(val):
ax1.cla() # clears the entire current figure but leaves the window
ax2.cla()
ax3.cla()
ax4.cla()
global idx
idx = np.array(w.get())
t1 = np.arange(0.0, 5.0, 0.1)
a1 = np.sin(idx*np.pi *t1)
a2 = np.sin((idx/2)*np.pi*t1)
a3 = np.sin((idx/4)*np.pi*t1)
a4 = np.sin((idx/8)*np.pi*t1)
"""Plotting of data"""
ax1.plot(t1, a1)
ax2.plot(t1, a2)
ax3.plot(t1, a3)
ax4.plot(t1, a4)
w = tk.Scale(master, from_=0, to=10, command = update)
w.pack()
tk.mainloop()
有没有人对如何仅获取数据(而不是整个窗口)有任何想法来随着滑块的移动而更新?抱歉,是否已经有人问过,但是我找不到。我无法使用matplotlib滑块选项,因为在实际脚本中,我正在清除从.txt文件提取的字符串变量,而不是整数。
答案 0 :(得分:0)
从对问题的描述中,我假设您希望某些图随着滑块的每个刻度线而更新,并且您不希望每次都重新创建该图。您提供的代码甚至无法创建图,因为您对fig
不做任何事情。也就是说,您需要进行一些更改。
首先在tkinter IMO中创建图时,最好在matplotlib中使用FugureCanvasTkAgg方法。这将使我们能够将所有内容绘制到画布上。我们还希望cla()
而不是close()
每个坐标轴。如果您关闭该图,则需要对其进行重建,因此最好的方法是每次更新时都要清除轴。
在这里,我将首先使用滑块的零值构建图,然后让该函数仅更新子图。这使我们可以保持绘图完整无缺,只更改子图中的线条,因此我们不必每次都重新创建绘图。
看看下面的代码。
更新:在工具栏中添加了内容,并为ImportanceOfBeingErnest条注释添加了“ TkAgg”。
import numpy as np
import tkinter as tk
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
master = tk.Tk()
def update(val):
global idx, chart_frame, ax1, ax2, ax3, ax4
ax1.cla()
ax2.cla()
ax3.cla()
ax4.cla()
idx = np.array(w.get())
t1 = np.arange(0.0, 5.0, 0.1)
a1 = np.sin(idx*np.pi *t1)
a2 = np.sin((idx/2)*np.pi*t1)
a3 = np.sin((idx/4)*np.pi*t1)
a4 = np.sin((idx/8)*np.pi*t1)
ax1.plot(t1, a1)
ax2.plot(t1, a2)
ax3.plot(t1, a3)
ax4.plot(t1, a4)
canvas.draw()
w = tk.Scale(master, from_=0, to=10, command = update)
w.grid(row=0, column=0)
idx = np.array(w.get())
fig, (ax1, ax2, ax3, ax4) = plt.subplots(4, sharey = False)
plt.tight_layout()
canvas = FigureCanvasTkAgg(fig, master)
canvas.get_tk_widget().grid(row=0, column=1)
toolbar_frame = tk.Frame(master)
toolbar_frame.grid(row=1, column=1, sticky="w")
NavigationToolbar2TkAgg(canvas, toolbar_frame)
canvas.draw()
master.mainloop()
我个人认为使用非OOP方法编写代码令人沮丧并且难以管理。我认为用OOP编写会更好,所以这是我的示例。
import numpy as np
import tkinter as tk
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
class MyPlot(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.w = tk.Scale(self, from_=0, to=10, command = self.update)
self.w.grid(row=0, column=0)
fig, (self.ax1, self.ax2, self.ax3, self.ax4) = plt.subplots(4, sharey = False)
plt.tight_layout()
self.canvas = FigureCanvasTkAgg(fig, self)
self.canvas.get_tk_widget().grid(row=0, column=1)
toolbar_frame = tk.Frame(self)
toolbar_frame.grid(row=1, column=1, sticky="w")
NavigationToolbar2TkAgg(self.canvas, toolbar_frame)
self.canvas.draw()
def update(self, val):
self.ax1.cla()
self.ax2.cla()
self.ax3.cla()
self.ax4.cla()
idx = np.array(self.w.get())
t1 = np.arange(0.0, 5.0, 0.1)
a1 = np.sin(idx*np.pi *t1)
a2 = np.sin((idx/2)*np.pi*t1)
a3 = np.sin((idx/4)*np.pi*t1)
a4 = np.sin((idx/8)*np.pi*t1)
self.ax1.plot(t1, a1)
self.ax2.plot(t1, a2)
self.ax3.plot(t1, a3)
self.ax4.plot(t1, a4)
self.canvas.draw()
if __name__ == '__main__':
app = MyPlot()
app.mainloop()
我会使用draw_idle()
作为ImportanceOfBeingErnest的说法,但是我目前看到的是一个错误,该错误可能导致绘图无法正确绘制。
以该屏幕截图为例。在快速向上滑动小节并释放鼠标键之后,该图的值通常无法正确更新。如果我在向上滑动滑杆的同时放开了鼠标按钮,只会发生接缝。
这可能是仅与Tkinter或matplotlib和Tkinter的某种组合有关的问题。我想象它没有绘制最后一个滑块值,因为Tkinter的mainloop由于我释放了鼠标而没有处于空闲状态,因为这是Tkinter中的一个事件,可能正在干扰。
答案 1 :(得分:0)
现有代码的一个简单解决方法是实际显示图形(fig.show
),并确保每次移动滑块(fig.canvas.draw_idle()
)时都重新绘制图形。
import matplotlib.pyplot as plt
import numpy as np
import tkinter as tk
fig, (ax1, ax2, ax3, ax4) = plt.subplots(4, sharey = False) # create figure
plt.tight_layout()
master = tk.Tk()
def update(val):
ax1.cla() # clears the entire current figure but leaves the window
ax2.cla()
ax3.cla()
ax4.cla()
global idx
idx = np.array(w.get())
t1 = np.arange(0.0, 5.0, 0.1)
a1 = np.sin(idx*np.pi *t1)
a2 = np.sin((idx/2)*np.pi*t1)
a3 = np.sin((idx/4)*np.pi*t1)
a4 = np.sin((idx/8)*np.pi*t1)
"""Plotting of data"""
ax1.plot(t1, a1)
ax2.plot(t1, a2)
ax3.plot(t1, a3)
ax4.plot(t1, a4)
fig.canvas.draw_idle()
fig.show()
w = tk.Scale(master, from_=0, to=10, command = update)
w.pack()
tk.mainloop()
如果不一定要提供tk滑块,则可以使用内置Slider
使用以下合适的解决方案。这具有完全独立于平台和后端的优势。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
fig, axes = plt.subplots(4, sharey = False) # create figure
plots = [ax.plot([])[0] for ax in axes]
fig.tight_layout()
fig.subplots_adjust(bottom=0.12)
t1 = np.arange(0.0, 5.0, 0.1)
def update(idx):
a1 = np.sin(idx*np.pi *t1)
a2 = np.sin((idx/2)*np.pi*t1)
a3 = np.sin((idx/4)*np.pi*t1)
a4 = np.sin((idx/8)*np.pi*t1)
for plot, a in zip(plots, [a1,a2,a3,a4]):
plot.set_data(t1, a)
plot.axes.relim()
plot.axes.autoscale()
fig.canvas.draw_idle()
update(5)
slider = Slider(fig.add_axes([.1,.04,.6,.03]), "Label", 0,10,5)
slider.on_changed(update)
plt.show()