在python的两个TKinter“页面”中使用FigureCanvasTkAgg

2024-06-17 09:45:05 发布

您现在位置:Python中文网/ 问答频道 /正文

我试着用matplotlib动画函数更新两个TKinter“页面”,每个页面都有不同的绘图。

问题是只有一页正确地显示了情节。

代码如下:

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循环中调用的最后一个on for F in ( GraphPageA, GraphPageB):

我还尝试使用不同的updategraph函数,每个图一个,但结果是相同的。

如何让两个TKinter页面绘制这两个不同的图形? 我正在使用Python2.7。


Tags: importselfplotmatplotlibinitcontainerdefshow
1条回答
网友
1楼 · 发布于 2024-06-17 09:45:05
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的一些讨厌的内部细节),非初始可见轴的绘制事件永远不会被触发(或者在创建动画之前被触发),因此第二个动画甚至永远不会启动。通过向类中添加canvas属性,您可以在每次切换显示的图形时显式触发draw_idle

请注意,我还:

  • 将动画函数拆分为每个图形的函数
  • 更新现有艺术家而不是创建新的艺术家
  • 留在调试打印中

在更一般的级别上,您可能应该将GraphPage*类重构为一个参数化类,该类负责:

  • 创造并留住它的艺术家
  • 创建并保持其动画对象
  • 根据字符串键(而不是类类型)切换显示的图形。

例如:

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()

这显然引起了我今天的注意。。

相关问题 更多 >