【问题标题】:Destroying and Recreating a Panel wx.Python销毁和重建面板 wx.Python
【发布时间】:2012-02-04 02:16:21
【问题描述】:

这是我一生的祸根:

Traceback(最近一次调用最后一次): OnDestroy 中的文件“C:\Users\User\Documents\PYTHON\GAME FILES\simple.py”,第 18 行 自我毁灭() 文件“C:\Python26\lib\site-packages\wx-2.8-msw-ansi\wx_core.py”,第 14610 行,在 getattr_ 中 引发 PyDeadObjectError(self.attrStr % self._name) wx._core.PyDeadObjectError: MyPanels 对象的 C++ 部分已被删除,属性 > 不再允许访问。

我的目标:拥有两个面板,左侧面板杀死并通过按钮“重新创建”右侧面板/相同的副本,模拟用户如何在左侧面板中选择他们想要查看的内容和让右侧面板显示此内容。这就是我在更复杂的程序中尝试做的事情,但是失败了,所以我想在这里学习如何破坏和重新创建一个空白面板。仍然是同样可怕的错误。

代码:

import wx
import sys
import traceback


def show_error():
    message = ''.join(traceback.format_exception(*sys.exc_info()))
    dialog = wx.MessageDialog(None, message, 'Error!', wx.OK|wx.ICON_ERROR)
    dialog.ShowModal()

class MyPanels(wx.Panel):

 def __init__(self, parent, id):
        wx.Panel.__init__(self, parent)
        self.parent = parent

 def OnDestroy(self, event):

    self.Destroy()

 def OnTest(self, event):
    print "Hello"

class MyFrame(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(1000, 480))
        self.parent = parent

        self.panel = MyPanels(self, -1)
        self.panel.SetBackgroundColour("grey")

        self.panel.leftpanel = MyPanels(self.panel, 1)
        self.panel.rightpanel = MyPanels(self.panel, 1)
        self.panel.leftpanel.SetBackgroundColour("red")
        self.panel.rightpanel.SetBackgroundColour("green")

        self.panel.basicsizer = wx.BoxSizer(wx.HORIZONTAL)
        self.panel.basicsizer.Add(self.panel.leftpanel, 1, wx.EXPAND)
        self.panel.basicsizer.Add(self.panel.rightpanel, 1, wx.EXPAND)
        self.panel.SetSizer(self.panel.basicsizer)

        button =  wx.Button(self.panel.leftpanel, 1, 'DIE DIE DIE', (50, 130))
        buttonres = wx.Button(self.panel.leftpanel, 2, 'Resurrect', (50, 230))
        buttonextra = wx.Button(self.panel.leftpanel, 3, 'Test', (50, 330))

        self.Bind(wx.EVT_BUTTON, self.panel.rightpanel.OnDestroy, id = 1)
        self.Bind(wx.EVT_BUTTON, self.CreateNewPanel, id = 2)
        self.Bind(wx.EVT_BUTTON, self.panel.rightpanel.OnTest, id = 3)




    def CreateNewPanel(self, event):
        self.panel.rightpanel = MyPanels(self.panel, 1)
        self.panel.rightpanel.SetBackgroundColour("green")

        self.panel.basicsizer.Add(self.panel.rightpanel, 1, wx.EXPAND)
        self.panel.SetSizer(self.panel.basicsizer)

        self.panel.rightpanel.Refresh()
        self.panel.leftpanel.Refresh()
        self.panel.leftpanel.Layout()
        self.panel.leftpanel.Update()
        self.panel.Layout()
        self.Update()


        self.Show(True)
        self.Centre()



def main():
    app = wx.App()
    try:
        frame = MyFrame(None, -1, 'Die.py')
        frame.Show()
        app.MainLoop()
    except:
        show_error()

if __name__ == '__main__':
    main()

基本上,我似乎(终于)让复活按钮工作了,至少在“新”面板填充了适当的空间方面。但如果我再次尝试杀死它,就会出现可怕的错误。

我一直在研究错误消息,似乎我得到了这个,因为 self.Destroy() 破坏了程序中某些东西仍然需要的东西。我发现这很难理解,因为我认为我正在创建一个完全相同的副本,直到相同的名称。

我认为唯一可以是“杀死”按钮本身。就好像它需要原来的右面板再次销毁,它被锁定在其唯一的类引用中,而不是它的右面板名称。我添加了第三个按钮来测试它。第三个按钮的代码几乎相同,只是调用了 MyPanels 类中的不同方法。而且......它在杀戮和复活之后仍然有效。但是再按Kill 就不行了。它们之间的唯一区别是 OnDestroy 方法使用“self”。

不知何故,我想我必须停止将 Kill 按钮的事件绑定到原始右侧面板的“自我”。我完全迷失了。我已经绝望地尝试告诉左侧面板刷新/更新自身。没有。过去两天一直在努力解决这个问题。我是 python 和 wxpython 的新手,在这里完全不知所措,但请帮帮我。

已编辑-

好的,刚才有一个迷你脑电波。我认为一旦事件与 Bind 命令绑定,它将永远绑定到该小部件,除非它被更改。这就是为什么它仍然坚持死板。 所以如果我解绑然后重新绑定,这样就解决了吗?

我已将按钮名称更改为 self.button。然后在 def CreateNewPanel() 方法中,我将 Unbind 命令放入...然后立即将复制粘贴的 Bind 命令放入其中。它现在似乎工作了!!!!

但是……谁能告诉我: 1)这是正确的解决方案还是我想出的一些麻烦的解决方法 和 2)为什么当我打电话时,为什么整个画面会在我的屏幕上跳半英寸 self.panel.rightpanel.Refresh()?

** 编辑 -

现在我似乎能够根据 LeftPanel 的按钮销毁和重新创建 RightPanel,我已尝试在复杂程序上进行管理。 该复杂程序旨在根据用户在 LeftPanel 上选择名称的 listctrl 来在 RightPanel 中显示角色的信息(bio + pic)

所以它与此非常相似:它不是有一个空的 RightPanel,而是有大量的小部件。

它确实有效......在一定程度上。如果我单击查看角色 X,RightPanel 会自行销毁,然后使用角色 Y 的小部件重新创建自己。耶。但它也会暂时在 RightPanel 的左上角闪烁一个小方块,其中清楚地包含所有小部件的副本。

该方块几乎立即消失,但每次单击更改 RightPanel 的内容时都清晰可见。这很丑陋。我就是无法摆脱它!我已经尝试在 .Layout() 各处和所有内容中打点,不。无法弄清楚为什么它会出现然后消失!是我无法摆脱的程序延迟吗?或者我在使用我的尺码器时犯了一些错误?必须强调 - 小部件按我的意图完美布置......所以尺寸器肯定工作正常......对吗? **

【问题讨论】:

    标签: wxpython


    【解决方案1】:

    我认为这里的问题是您以一种非常奇怪的方式将所有内容联系在一起。您通常不会通过执行 MyPanel.panel = wx.Panel() 创建嵌套面板。相反,您可以按照创建任何面板的常规方式来创建它们:

    panelOne = wx.Panel(self)
    panelTwo = wx.Panel(self)
    panelThree = MyPanels(parent, id)
    

    我也会避免像您那样创建尺寸器。我使用类似的东西

    mySizer = wx.BoxSizer()
    

    我最终清理了一堆奇怪的垃圾来完成这项工作。我没有看到销毁面板的理由,所以我只是将其隐藏起来:

    import wx
    import sys
    import traceback
    
    def show_error():
        message = ''.join(traceback.format_exception(*sys.exc_info()))
        dialog = wx.MessageDialog(None, message, 'Error!', wx.OK|wx.ICON_ERROR)
        dialog.ShowModal()
    
    class MyPanels(wx.Panel):
    
        def __init__(self, parent, id):
            wx.Panel.__init__(self, parent)
            self.parent = parent
    
    class MyFrame(wx.Frame):
        def __init__(self, parent, id, title):
            wx.Frame.__init__(self, parent, id, title, size=(1000, 480))
            self.parent = parent
    
            self.panel = wx.Panel(self, -1)
            self.panel.SetBackgroundColour("grey")
    
            self.leftpanel = MyPanels(self.panel, 1)
            self.rightpanel = MyPanels(self.panel, 1)
            self.leftpanel.SetBackgroundColour("red")
            self.rightpanel.SetBackgroundColour("green")
    
            self.basicsizer = wx.BoxSizer(wx.HORIZONTAL)
            self.basicsizer.Add(self.leftpanel, 1, wx.EXPAND)
            self.basicsizer.Add(self.rightpanel, 1, wx.EXPAND)
            self.panel.SetSizer(self.basicsizer)
    
            button =  wx.Button(self.leftpanel, 1, 'DIE DIE DIE', (50, 130))
            buttonres = wx.Button(self.leftpanel, 2, 'Resurrect', (50, 230))
            buttonextra = wx.Button(self.leftpanel, 3, 'Test', (50, 330))
    
            self.Bind(wx.EVT_BUTTON, self.destroyPanel, button)
            self.Bind(wx.EVT_BUTTON, self.CreateNewPanel, buttonres)
    
        def CreateNewPanel(self, event):
            self.rightpanel = MyPanels(self.panel, 1)
            self.rightpanel.SetBackgroundColour("green")
            self.basicsizer.Add(self.rightpanel, 1, wx.EXPAND)
            self.panel.Layout()
    
            self.Show(True)
            self.Centre()
    
        def destroyPanel(self, event):
            self.rightpanel.Hide()
            self.panel.Layout()
    
    def main():
        app = wx.App()
        try:
            frame = MyFrame(None, -1, 'Die.py')
            frame.Show()
            app.MainLoop()
        except:
            show_error()
    
    if __name__ == '__main__':
        main()
    

    这个练习让我想起了我几年前的面板切换文章:http://www.blog.pythonlibrary.org/2010/06/16/wxpython-how-to-switch-between-panels/

    也许这对你也有帮助。

    【讨论】:

    • 嗨,我对嵌套面板有点困惑。可能是因为我在对象引用上苦苦挣扎。在某个时候,可能是当我第一次开始用 self 开头的对象时。 ,我假设他们只能通过他们的父母访问。重写了代码,它确实适用于简化的 refs。
    • 第二点 - 重新破坏与隐藏。我选择销毁是因为这个脚本只是为了隔离如何销毁和重新创建。在我的正确程序中,顶部有一个工具栏,左侧有一个 listctrl,它们用于更改 RightPanel 中的显示。用户可以通过工具栏在区域之间切换,也可以在字符之间切换。所以我认为销毁会比隐藏更简单,否则程序会在加载和加载隐藏面板时进行处理,这会占用内存。
    • 但我很想知道 panel.destroy 与 panel.hide 相比有什么优势/劣势。我想知道我遇到的闪烁的小方块是否是使用破坏和重新创建的结果......也许它正在减慢程序的速度?编辑了关于这个奇怪的小方块的主要帖子,它几乎立即消失但不能错过,从而使程序看起来很糟糕。我很想知道我是否真的应该考虑重新组织程序以使隐藏面板变得可行。
    • 其实真的应该感谢你介绍我隐藏和显示。我仍然不知道我的代码中的什么缺陷导致了闪烁的方块,但我现在可以掩盖它了。创建 RightPanel 时,我会立即调用 hide。然后我调用在 RightPanel 上弹出新小部件的函数。这时会出现闪烁的方块。但是由于面板被隐藏,它无法显示。拿那个,正方形。然后我告诉它显示。因此,不同 RightPanel 字符之间的过渡现在都很平滑,重要的是没有正方形。但还是不明白是什么原因造成的。
    • 可能最简单的隐藏闪烁伪影的方法是使用 Freeze 和 Thaw 方法。在这个例子中我选择了 Hide() ,因为你把所有东西都绑在一起了。当一切都像这样捆绑在一起时,使用 Destroy() 似乎会无意中破坏元素。不过,将事物分开应该可以让您再次使用 Destroy。
    【解决方案2】:
    1. 是的,这是正确的解决方案,尽管更 Pythonic 的方法是在销毁面板的同时取消绑定 Die 按钮,以便多次按下该按钮不会引发异常。应该这样做:

      self.Bind(wx.EVT_BUTTON, self.OnPanelDestroy, id = 1)
      
      def OnPanelDestroy(self, event):
          self.Unbind(wx.EVT_BUTTON, id = 1)
          self.panel.rightpanel.OnDestroy(event)
      
      def CreateNewPanel(self, event):
          self.panel.rightpanel = MyPanels(self.panel, 1)
          self.panel.rightpanel.SetBackgroundColour("green")
          self.Bind(wx.EVT_BUTTON, self.OnPanelDestroy, id = 1)
      
    2. 如果我理解正确,那是因为调用 self.Centre() 而不是 self.panel.rightpanel.Refresh()。如果您将其注释掉,框架将不会在屏幕上居中,而是保持原样。

    【讨论】:

    • 您好,非常感谢!您对 self.Centre() 的看法是正确的,我已经对其进行了调整,并且它不再在屏幕上跳跃。我已经把这一切都弹回到原来的正确程序中(基本上就像这样,只有一个人名的listctrl而不是LeftPanel上的按钮,它会根据被选中的人在RightPanel上产生大量的小部件,包括角色简介和图片),并且它现在可以工作了……虽然奇怪的是,我在 RightPanel 的左上角一直有一个小方块……布局很完美,除了这个方块出现了大约 1 秒。 困惑
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多