2017-07-19 156 views
1

我有一个关于Python中的内存管理的问题,具体涉及到使用Tkinter的程序。首先我将以一个例子来展示我测试一些简单的内存管理:内存管理和Tkinter

import tkinter as tk 
from tkinter import ttk 

class Frame1(tk.Frame): 

    def __init__(self, parent, *args, **kwargs): 
     tk.Frame.__init__(self, parent, *args, **kwargs) 
     self.parent = parent 
     self.pack() 
     print("Frame1 created") 
     btn = ttk.Button(self, text = "PRESS", command = self.openFrame2) 
     btn.pack() 

    def __del__(self): 
     print("Frame1 destroyed") 

    def openFrame2(self): 
     self.pack_forget() 
     self.destroy() 
     frame2 = Frame2(self.parent) 

class Frame2(tk.Frame): 

    def __init__(self, parent, *args, **kwargs): 
     tk.Frame.__init__(self, parent, *args, **kwargs) 
     self.parent = parent 
     self.pack() 
     print("Frame2 created") 
     btn = ttk.Button(self, text = "CLICK", command = self.openFrame1) 
     btn.pack() 

    def __del__(self): 
     print("Frame2 destroyed") 

    def openFrame1(self): 
     self.pack_forget 
     self.destroy() 
     frame1 = Frame1(self.parent) 

root = tk.Tk() 
program = Frame1(root) 
root.mainloop() 

这个程序的目标很简单。用一个按钮创建一个包含Frame的Tkinter窗口。按下该按钮时,删除该帧,释放由该帧使用的内存,并创建一个不同的帧。

如果我按在PRESS -> CLICK -> PRESS我的预期结果的顺序按一下按钮是:

Frame1 created 
PRESS: 
Frame1 destroyed 
Frame2 created 
CLICK: 
Frame2 destroyed 
Frame1 created 
PRESS: 
Frame1 destroyed 
Frame2 created 

而不是我的实际结果是:

Frame1 created 
PRESS: 
Frame2 created 
CLICK: 
Frame1 created 
Frame2 destroyed 
PRESS: 
Frame2 created 
Frame1 destroyed 

我有点困惑在这里。 Frame2似乎正确呼叫__del__方法时,我呼吁self.destroy()Frame1不。为什么是这样?

+0

Python不保证何时(或甚至)是否调用'__del __()',它不是C++。最好忘记尝试使用它来做到这一点。而是考虑在派生类中重载'destroy()'。 – martineau

+0

FWIW:这什么都不做:'self.pack_forget'。即使你正确地调用了它('self.pack_forget()'),你之后立即删除这个小部件也是没有意义的。 –

+0

你看,这就是我的困惑,因为我是Python的新手,但知道C++。一个更好的选择是创建一个手动的'destroyMe'方法,该方法可以彻底删除对象正在使用的所有东西? – Skitzafreak

回答

1

您可能需要更改代码中的一些内容。

首先回答您的评论有一个问题:

会是更好的选择是创建一个越过并删除对象是手动使用一切人工destroyMe方法?

我们不需要使用pack.forget(),因为您正在销毁该框架。

使用容器上的.destroy()方法销毁该容器中的所有内容。没有必要一次一个地摧毁所有的东西。您可以在包含多个框架的框架上使用它们,并使用它们自己的一组小部件,并且会破坏主框架内所有框架中的所有内容。或者如果需要,您可以只定位一个小部件。我们不需要。相反,只需在self.destroy()命令后面移动打印语句即可。

看看下面的代码。它将按照您在问题中发布的预期顺序进行打印,同时在此过程中自行销毁。

import tkinter as tk 
from tkinter import ttk 


class Frame1(tk.Frame): 

    def __init__(self, parent, *args, **kwargs): 
     tk.Frame.__init__(self, parent, *args, **kwargs) 
     self.parent = parent 
     self.pack() 
     print("Frame1 created") 
     btn = ttk.Button(self, text = "PRESS", command = self.openFrame2) 
     btn.pack() 

    def openFrame2(self): 
     self.destroy() 
     print("Frame1 destroyed") 
     frame2 = Frame2(root) 


class Frame2(tk.Frame): 

    def __init__(self, parent, *args, **kwargs): 
     tk.Frame.__init__(self, parent, *args, **kwargs) 
     self.parent = parent 
     self.pack() 
     print("Frame2 created") 
     btn = ttk.Button(self, text = "CLICK", command = self.openFrame1) 
     btn.pack() 

    def openFrame1(self): 
     self.destroy() 
     print("Frame2 destroyed") 
     frame1 = Frame1(root) 


root = tk.Tk() 
frame1 = Frame1(root) 
root.mainloop() 
+0

在'openFrameX()'方法中,为什么要将调用'FrameX(self.parent)'的结果赋值给局部变量(当函数返回时立即抛出)? – martineau

+0

这不会导致程序明显的错误行为 - 只是浪费CPU周期,因为它没有完成任何事情。由于'.destroy()'可能会被多次调用,所以在我看来,在派生类中重载它并在那里调用'print()'会更好。 – martineau

+0

@martineau它只是OP原始代码的一部分。我甚至没有看到,因为我没有遇到任何错误。我想我可以重写代码来删除对象而不是框架。 (固定类型) –