wxPython + weakref proxy = closing wx.Frame does not yield None

wxPython + weakref proxy = closing wx.Frame does not yield None

我目前正在制作一个 Python 带有两个 windows 的 wxWigets 应用程序。第一个是主要的"controller"window,第二个是作为数据展示window.

我想为第一个 window 设置一些机制,以了解第二个 window 已经生成的位置,如果是,是否已被用户关闭。我虽然考虑使用 Python 的 weakref.proxy(),因为基于我对语言的一点理解,似乎如果一个对象是 closed/destroyed/deallocated/GC'ed,任何调用我的代理的尝试return 会是一个 None 值,可以方便地使用 Python 的 is None / is not None 运算符进行检查。

只要 window 生成一次,代理就会按预期工作,如果 window 尚未生成,则 returns None,或者否则引用该对象。但是一旦我关闭辅助 window,代理对象将不会按预期恢复为 None,我的应用程序将崩溃并显示 ReferenceError: weakly-referenced object no longer exists.

我记得我以前曾尝试解决这个问题,我发现最有效的解决方案是根据内部 wx class 检查对象的 class 名称,例如:

if windowObject.__class__.__name__ is not "_wxPyDeadObject": #do stuff

然而,这对我来说似乎是一个非常 hackish 的解决方案,我想知道除了上述之外是否还有更好的出路。下面是一些重现我这个问题的基本代码。

import wx
import weakref


class SillyWindow(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, parent=None, title="Spawned Window")
        self.Show()


class ExWindow(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, parent=None, title="Main Window")

        self.panel = wx.Panel(self)
        self.button = wx.Button(self.panel, label="Spawn window!")
        self.Bind(wx.EVT_BUTTON, self.spawn, self.button)
        self.txt = wx.TextCtrl(self.panel, pos=(0,100))

        self.wind = None

        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.update, self.timer)
        self.timer.Start(50)
        self.Show()

    def spawn(self,event):
        if self.wind is None:  # Preventing multiple spawning windows
            self.wind = weakref.proxy(SillyWindow())

    def update(self,event):
        if self.wind is not None:
            self.txt.SetValue(str(self.wind))
        else:
            self.txt.SetValue("None")


app = wx.App(False)
frame = ExWindow()
app.MainLoop()

如您所见,当一个 wx 小部件对象被销毁时,Python 代理对象的 class 与一个更改它的代理对象交换,以便在您尝试使用它时引发异常。它也有一个 __nonzero__ 方法,所以你可以做这样的事情,而不是挖掘对象的内部来找到它的 class name:

if not windowObject:
    # it has been destroyed already
    return

要记住的另一件事是顶级 windows 在关闭或调用其 Destroy 方法时不会被销毁。相反,它们被添加到待处理的删除列表中,一旦不再有待处理的事件,该列表就会被处理。您可以通过调用框架的 IsBeingDeleted 方法来测试这种情况(已关闭但尚未销毁)。此外,UI 对象的 C++ 部分也持有它们自己对 Python 对象的引用,尽管当 C++ 对象被销毁时它会被 decref。因此,其中一些或所有这些事情可能会干扰您的 weafref 方法。就我个人而言,我会像上面那样使用 if 语句。短的。甜的。简单。

P.S。我在这里提到的一些细节是 wxPython Classic 特有的,在 Phoenix 中的处理方式不同。然而,使用像上面这样的 if 语句仍然有效。