不断增长的堆栈Tkinter(切换帧时)

时间:2018-07-12 17:54:51

标签: python tkinter stack-overflow

我制作了一个Tkinter python应用,当用户在帧之间单击时,该栈会导致堆栈不断增长。我不确定为什么会这样。我非常模糊地了解堆栈是如何工作的。每个Tkinter框架都是对象,我想它们的方法实际上并没有完成,而是随着用户来回点击程序而相互调用。

我认为这与我使用matplotlib的方式有关,创建与Tkinter框架分开的图形。也许将它们嵌入框架会有所帮助?

我想知道问题是否出在我如何切换帧之间。我在任何帧的方法中调用self.controller.show_frame(page_name)来切换引发哪个帧:

def show_frame(self, page_name):
    '''Show a frame for the given page name'''
    frame = self.frames[page_name]
    self.last_frame = self.raised_frame
    self.raised_frame = frame.__name__
    frame.tkraise()

    try:
        frame.re_init()
    except AttributeError as e:
        print_error(e)

我可以看到这种frame.re_init()方法存在问题,但是我想在每次抬起框架时进行更新。在每个Tkinter框架的方法中,基于我如何调用self.controller.show_frame(page_name)来增加堆栈似乎是有道理的,但是我不确定在Tkinter框架之间进行切换的更好的替代方法是什么。

看来这实际上是在帧之间切换的一种典型方法,所以我认为问题在于未嵌入的matplotlib图形。

这是一个简单的应用程序,与我的问题相同。运行此命令,然后来回单击几次。观察终端输出,您将看到堆栈如何增长。需要使用matplotlib动画。

import tkinter as tk                # python 3
from tkinter import font  as tkfont # python 3
import traceback
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
#import Tkinter as tk     # python 2
#import tkFont as tkfont  # python 2

class SampleApp(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")

        # the container is where we'll stack a bunch of frames
        # on top of each other, then the one we want visible
        # will be raised above the others
        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 (StartPage, PageOne, PageTwo):
            page_name = F.__name__
            frame = F(parent=container, controller=self)
            self.frames[page_name] = frame

            # put all of the pages in the same location;
            # the one on the top of the stacking order
            # will be the one that is visible.
            frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame("StartPage")

    def show_frame(self, page_name):
        '''Show a frame for the given page name'''
        print("current length of stack: {}".format(len(traceback.extract_stack())))
        frame = self.frames[page_name]
        frame.tkraise()

        try:
            frame.re_init()
        except AttributeError:
            pass

    def do_plot(self):
        plt.close()

        def data_gen(t=0):
            cnt = 0
            while cnt < 1000:
                cnt += 1
                t += 0.1
                yield t, np.sin(2*np.pi*t) * np.exp(-t/10.)


        def init():
            ax.set_ylim(-1.1, 1.1)
            ax.set_xlim(0, 10)
            del xdata[:]
            del ydata[:]
            line.set_data(xdata, ydata)
            return line,

        fig, ax = plt.subplots()
        line, = ax.plot([], [], lw=2)
        ax.grid()
        xdata, ydata = [], []



        def run(data):
            # update the data
            t, y = data
            xdata.append(t)
            ydata.append(y)
            xmin, xmax = ax.get_xlim()

            if t >= xmax:
                ax.set_xlim(xmin, 2*xmax)
                ax.figure.canvas.draw()
            line.set_data(xdata, ydata)

            return line,

        ani = animation.FuncAnimation(fig, run, data_gen, blit=False, interval=10,
                                      repeat=False, init_func=init)

        plt.show()


    def show_plot(self):
        pass

    def stop_plot(self):
        plt.close()


class StartPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self, text="This is the start page", font=controller.title_font)
        label.pack(side="top", fill="x", pady=10)

        button1 = tk.Button(self, text="Go to Page One",
                            command=self.one)
        button2 = tk.Button(self, text="Go to Page Two",
                            command=self.two)
        button1.pack()
        button2.pack()

    def one(self):
        self.controller.show_frame("PageOne")

    def two(self):
        self.controller.show_frame("PageTwo")

class PageOne(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self, text="This is page 1", font=controller.title_font)
        label.pack(side="top", fill="x", pady=10)
        button = tk.Button(self, text="Go to the start page",
                           command=lambda: controller.show_frame("StartPage"))
        button.pack()


    def re_init(self):
        self.controller.do_plot()
        self.controller.show_plot()


class PageTwo(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self, text="This is page 2", font=controller.title_font)
        label.pack(side="top", fill="x", pady=10)
        button = tk.Button(self, text="Go to the start page",
                           command=lambda: controller.show_frame("StartPage"))
        button.pack()


    def re_init(self):
        self.controller.do_plot()
        self.controller.show_plot()

if __name__ == "__main__":
    app = SampleApp()
    app.mainloop()

1 个答案:

答案 0 :(得分:0)

  

但是我不确定在Tkinter帧之间切换的更好的替代方法是什么。

如果要在切换到框架时重新创建框架,更好的解决方案是完全这样做:销毁旧页面并让show_frame调用创建每个页面的函数

例如,初始化窗口的代码可能如下所示:

class SampleApp(tk.Tk):
    def __init__(self, *args, **kwargs):
        ...
        self.current_page = None
        self.pages = {
            "Page One": PageOne,
            "Page Two": PageTwo,
            "Page Three": PageThree
        }

        # show the first page
        self.show_frame("Page One")

show_frame看起来像这样:

def show_frame(self, page_name):
    # remove the old page
    if self.current_page is not None:
        self.current_page.destroy()

    # create the new page
    page_cls = self.pages[page_name]
    self.current_page = page_cls(parent=self.container, controller=self)
    self.current_page.pack(fill="both", expand=True)