我制作了一个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()
答案 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)