在两个 TKinter 中使用 FigureCanvasTkAgg "pages"
Using FigureCanvasTkAgg in two TKinter "pages"
我正在尝试获取两个 TKinter 'pages',每个都使用 matplotlib 动画函数更新不同的情节。
问题是只有其中一页正确显示了剧情。
代码如下:
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
import Tkinter as tk
import ttk
a = Figure(figsize=(4,4))
plot_a = a.add_subplot(111)
b = Figure(figsize=(4,4))
plot_b = b.add_subplot(111)
x = [1,2,3,4,5]
y_a = [1,4,9,16,25]
y_b = [25,16,9,4,1]
def updateGraphs(i):
plot_a.clear()
plot_a.plot(x,y_a)
plot_b.clear()
plot_b.plot(x,y_b)
class TransientAnalysis(tk.Tk):
def __init__(self,*args,**kwargs):
tk.Tk.__init__(self,*args,**kwargs)
tk.Tk.wm_title(self, "Transient Analysis GUI: v1.0")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in ( GraphPageA, GraphPageB):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(GraphPageA)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class GraphPageA(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
button1 = ttk.Button(self, text="Show Graph B",
command = lambda: controller.show_frame(GraphPageB))
button1.grid(row=1, column=0,pady=20,padx=10, sticky='w')
canvasA = FigureCanvasTkAgg(a, self)
canvasA.show()
canvasA.get_tk_widget().grid(row=1, column=1, pady=20,padx=10, sticky='nsew')
class GraphPageB(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
button1 = ttk.Button(self, text="Show Graph A",
command = lambda: controller.show_frame(GraphPageA))
button1.grid(row=1, column=0,pady=20,padx=10, sticky='w')
canvasB = FigureCanvasTkAgg(b, self)
canvasB.show()
canvasB.get_tk_widget().grid(row=1, column=1, pady=20,padx=10, sticky='nsew')
app = TransientAnalysis()
app.geometry("800x600")
aniA = animation.FuncAnimation(a, updateGraphs, interval=1000)
aniB = animation.FuncAnimation(b, updateGraphs, interval=1000)
app.mainloop()
更具体地说,正确显示情节的页面是 for 循环中最后调用的页面 for F in ( GraphPageA, GraphPageB):
我也尝试使用不同的 updateGraphs 函数,每个图一个,但结果是一样的。
如何让两个 TKinter 页面绘制这两个不同的图形?
我正在使用 Python 2.7.
from __future__ import print_function
import matplotlib
import numpy as np
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
import Tkinter as tk
import ttk
a = Figure(figsize=(4, 4))
plot_a = a.add_subplot(111)
plot_a.set_xlim([0, 2*np.pi])
plot_a.set_ylim([-1, 1])
lna, = plot_a.plot([], [], color='orange', lw=5)
b = Figure(figsize=(4, 4))
plot_b = b.add_subplot(111)
plot_b.set_xlim([0, 2*np.pi])
plot_b.set_ylim([-1, 1])
lnb, = plot_b.plot([], [], color='olive', lw=5)
x = np.linspace(0, 2*np.pi, 1024)
def updateGraphsA(i):
lna.set_xdata(x)
lna.set_ydata(np.sin(x + i * np.pi / 10))
print('in A')
def updateGraphsB(i):
lnb.set_xdata(x)
lnb.set_ydata(np.sin(x - i * np.pi / 10))
print('in B')
class TransientAnalysis(tk.Tk):
def __init__(self, *args, **kwargs):
self._running_anim = None
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "Transient Analysis GUI: v1.0")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (GraphPageA, GraphPageB):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(GraphPageA)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
frame.canvas.draw_idle()
class GraphPageA(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
button1 = ttk.Button(self, text="Show Graph B",
command=(
lambda: controller.show_frame(GraphPageB)))
button1.grid(row=1, column=0, pady=20, padx=10, sticky='w')
canvasA = FigureCanvasTkAgg(a, self)
canvasA.show()
canvasA.get_tk_widget().grid(
row=1, column=1, pady=20, padx=10, sticky='nsew')
self.canvas = canvasA
class GraphPageB(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
button1 = ttk.Button(self, text="Show Graph A",
command=(
lambda: controller.show_frame(GraphPageA)))
button1.grid(row=1, column=0, pady=20, padx=10, sticky='w')
canvasB = FigureCanvasTkAgg(b, self)
canvasB.show()
canvasB.get_tk_widget().grid(
row=1, column=1, pady=20, padx=10, sticky='nsew')
self.canvas = canvasB
app = TransientAnalysis()
app.geometry("800x600")
aniA = animation.FuncAnimation(a, updateGraphsA, interval=1000, blit=False)
aniB = animation.FuncAnimation(b, updateGraphsB, interval=1000, blit=False)
app.mainloop()
问题是动画是在第一次绘制图形时启动的。无论出于何种原因(这将是 Tk 或 mpl 的一些令人讨厌的内部细节)非初始可见轴的绘制事件永远不会被触发(或者在 之前 动画被创建)所以第二个动画甚至从未开始。通过向 classes 添加 canvas
属性,您可以在每次切换显示的图表时明确触发 draw_idle
。
请注意,我还:
- 将动画函数拆分为每个图一个函数
- 更新现有艺术家而不是创建新艺术家
- 留在调试打印中
在更一般的层面上,您可能应该将 GraphPage*
classes 重构为一个参数化 class,它负责 :
- 创造并持有它的艺术家
- 创建并保存它的动画对象
- 根据字符串键切换显示的图形,而不是 class 类型。
例如:
from __future__ import print_function
import matplotlib
import numpy as np
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
import Tkinter as tk
import ttk
x = np.linspace(0, 2*np.pi, 1024)
class TransientAnalysis(tk.Tk):
pages = ((1, 'Switch to "-"', '-', '+', 'orange'),
(-1, 'Switch to "+"', '+', '-', 'olive'))
def __init__(self, *args, **kwargs):
self._running_anim = None
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "Transient Analysis GUI: v1.0")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for (direction, text, other_key, my_key, color) in self.pages:
frame = MovingSinGraphPage(direction, text, other_key,
my_key, color,
container, self)
self.frames[my_key] = frame
frame.grid(row=0, column=0, sticky="nsew")
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
frame.canvas.draw_idle()
class MovingSinGraphPage(tk.Frame):
def __init__(self, move_dir, text, other_key, my_key,
color, parent, controller):
self._sgn = np.sign(move_dir)
tk.Frame.__init__(self, parent)
button1 = ttk.Button(self, text=text,
command=(
lambda: controller.show_frame(other_key)))
button1.grid(row=1, column=0, pady=20, padx=10, sticky='w')
# make mpl objects
a = Figure(figsize=(4, 4))
plot_a = a.add_subplot(111)
# set up the axes limits and title
plot_a.set_title(my_key)
plot_a.set_xlim([0, 2*np.pi])
plot_a.set_ylim([-1, 1])
# make and stash the plot artist
lna, = plot_a.plot([], [], color=color, lw=5)
self._line = lna
# make the canvas to integrate with Tk
canvasA = FigureCanvasTkAgg(a, self)
canvasA.show()
canvasA.get_tk_widget().grid(
row=1, column=1, pady=20, padx=10, sticky='nsew')
# stash the canvas so that we can use it above to ensure a re-draw
# when we switch to this page
self.canvas = canvasA
# create and save the animation
self.anim = animation.FuncAnimation(a, self.update,
interval=100)
def update(self, i):
self._line.set_xdata(x)
self._line.set_ydata(np.sin(x + self._sgn * i * np.pi / 10))
app = TransientAnalysis()
app.geometry("800x600")
app.mainloop()
今天这显然引起了我的注意..
我正在尝试获取两个 TKinter 'pages',每个都使用 matplotlib 动画函数更新不同的情节。
问题是只有其中一页正确显示了剧情。
代码如下:
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
import Tkinter as tk
import ttk
a = Figure(figsize=(4,4))
plot_a = a.add_subplot(111)
b = Figure(figsize=(4,4))
plot_b = b.add_subplot(111)
x = [1,2,3,4,5]
y_a = [1,4,9,16,25]
y_b = [25,16,9,4,1]
def updateGraphs(i):
plot_a.clear()
plot_a.plot(x,y_a)
plot_b.clear()
plot_b.plot(x,y_b)
class TransientAnalysis(tk.Tk):
def __init__(self,*args,**kwargs):
tk.Tk.__init__(self,*args,**kwargs)
tk.Tk.wm_title(self, "Transient Analysis GUI: v1.0")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in ( GraphPageA, GraphPageB):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(GraphPageA)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class GraphPageA(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
button1 = ttk.Button(self, text="Show Graph B",
command = lambda: controller.show_frame(GraphPageB))
button1.grid(row=1, column=0,pady=20,padx=10, sticky='w')
canvasA = FigureCanvasTkAgg(a, self)
canvasA.show()
canvasA.get_tk_widget().grid(row=1, column=1, pady=20,padx=10, sticky='nsew')
class GraphPageB(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
button1 = ttk.Button(self, text="Show Graph A",
command = lambda: controller.show_frame(GraphPageA))
button1.grid(row=1, column=0,pady=20,padx=10, sticky='w')
canvasB = FigureCanvasTkAgg(b, self)
canvasB.show()
canvasB.get_tk_widget().grid(row=1, column=1, pady=20,padx=10, sticky='nsew')
app = TransientAnalysis()
app.geometry("800x600")
aniA = animation.FuncAnimation(a, updateGraphs, interval=1000)
aniB = animation.FuncAnimation(b, updateGraphs, interval=1000)
app.mainloop()
更具体地说,正确显示情节的页面是 for 循环中最后调用的页面 for F in ( GraphPageA, GraphPageB):
我也尝试使用不同的 updateGraphs 函数,每个图一个,但结果是一样的。
如何让两个 TKinter 页面绘制这两个不同的图形? 我正在使用 Python 2.7.
from __future__ import print_function
import matplotlib
import numpy as np
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
import Tkinter as tk
import ttk
a = Figure(figsize=(4, 4))
plot_a = a.add_subplot(111)
plot_a.set_xlim([0, 2*np.pi])
plot_a.set_ylim([-1, 1])
lna, = plot_a.plot([], [], color='orange', lw=5)
b = Figure(figsize=(4, 4))
plot_b = b.add_subplot(111)
plot_b.set_xlim([0, 2*np.pi])
plot_b.set_ylim([-1, 1])
lnb, = plot_b.plot([], [], color='olive', lw=5)
x = np.linspace(0, 2*np.pi, 1024)
def updateGraphsA(i):
lna.set_xdata(x)
lna.set_ydata(np.sin(x + i * np.pi / 10))
print('in A')
def updateGraphsB(i):
lnb.set_xdata(x)
lnb.set_ydata(np.sin(x - i * np.pi / 10))
print('in B')
class TransientAnalysis(tk.Tk):
def __init__(self, *args, **kwargs):
self._running_anim = None
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "Transient Analysis GUI: v1.0")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (GraphPageA, GraphPageB):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(GraphPageA)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
frame.canvas.draw_idle()
class GraphPageA(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
button1 = ttk.Button(self, text="Show Graph B",
command=(
lambda: controller.show_frame(GraphPageB)))
button1.grid(row=1, column=0, pady=20, padx=10, sticky='w')
canvasA = FigureCanvasTkAgg(a, self)
canvasA.show()
canvasA.get_tk_widget().grid(
row=1, column=1, pady=20, padx=10, sticky='nsew')
self.canvas = canvasA
class GraphPageB(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
button1 = ttk.Button(self, text="Show Graph A",
command=(
lambda: controller.show_frame(GraphPageA)))
button1.grid(row=1, column=0, pady=20, padx=10, sticky='w')
canvasB = FigureCanvasTkAgg(b, self)
canvasB.show()
canvasB.get_tk_widget().grid(
row=1, column=1, pady=20, padx=10, sticky='nsew')
self.canvas = canvasB
app = TransientAnalysis()
app.geometry("800x600")
aniA = animation.FuncAnimation(a, updateGraphsA, interval=1000, blit=False)
aniB = animation.FuncAnimation(b, updateGraphsB, interval=1000, blit=False)
app.mainloop()
问题是动画是在第一次绘制图形时启动的。无论出于何种原因(这将是 Tk 或 mpl 的一些令人讨厌的内部细节)非初始可见轴的绘制事件永远不会被触发(或者在 之前 动画被创建)所以第二个动画甚至从未开始。通过向 classes 添加 canvas
属性,您可以在每次切换显示的图表时明确触发 draw_idle
。
请注意,我还:
- 将动画函数拆分为每个图一个函数
- 更新现有艺术家而不是创建新艺术家
- 留在调试打印中
在更一般的层面上,您可能应该将 GraphPage*
classes 重构为一个参数化 class,它负责 :
- 创造并持有它的艺术家
- 创建并保存它的动画对象
- 根据字符串键切换显示的图形,而不是 class 类型。
例如:
from __future__ import print_function
import matplotlib
import numpy as np
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
import Tkinter as tk
import ttk
x = np.linspace(0, 2*np.pi, 1024)
class TransientAnalysis(tk.Tk):
pages = ((1, 'Switch to "-"', '-', '+', 'orange'),
(-1, 'Switch to "+"', '+', '-', 'olive'))
def __init__(self, *args, **kwargs):
self._running_anim = None
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "Transient Analysis GUI: v1.0")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for (direction, text, other_key, my_key, color) in self.pages:
frame = MovingSinGraphPage(direction, text, other_key,
my_key, color,
container, self)
self.frames[my_key] = frame
frame.grid(row=0, column=0, sticky="nsew")
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
frame.canvas.draw_idle()
class MovingSinGraphPage(tk.Frame):
def __init__(self, move_dir, text, other_key, my_key,
color, parent, controller):
self._sgn = np.sign(move_dir)
tk.Frame.__init__(self, parent)
button1 = ttk.Button(self, text=text,
command=(
lambda: controller.show_frame(other_key)))
button1.grid(row=1, column=0, pady=20, padx=10, sticky='w')
# make mpl objects
a = Figure(figsize=(4, 4))
plot_a = a.add_subplot(111)
# set up the axes limits and title
plot_a.set_title(my_key)
plot_a.set_xlim([0, 2*np.pi])
plot_a.set_ylim([-1, 1])
# make and stash the plot artist
lna, = plot_a.plot([], [], color=color, lw=5)
self._line = lna
# make the canvas to integrate with Tk
canvasA = FigureCanvasTkAgg(a, self)
canvasA.show()
canvasA.get_tk_widget().grid(
row=1, column=1, pady=20, padx=10, sticky='nsew')
# stash the canvas so that we can use it above to ensure a re-draw
# when we switch to this page
self.canvas = canvasA
# create and save the animation
self.anim = animation.FuncAnimation(a, self.update,
interval=100)
def update(self, i):
self._line.set_xdata(x)
self._line.set_ydata(np.sin(x + self._sgn * i * np.pi / 10))
app = TransientAnalysis()
app.geometry("800x600")
app.mainloop()
今天这显然引起了我的注意..