python - TextCtrl 的 wxPython C++ 部分被删除

标签 python python-2.7 wxpython

我正在尝试用 Python 为一个学校项目组合一个图形用户界面,但我遇到了一个错误,而且我不太明白为什么。

self.prompt.addAnswer(i, self.ansControls[i].GetValue()) File "C:\Python27\lib\site-packages\wx-3.0-msw\wx_core.py", line 16712, in getattr raise PyDeadObjectError(self.attrStr % self._name) wx._core.PyDeadObjectError: The C++ part of the TextCtrl object has been deleted, attribute access no longer allowed.

我明白错误的含义,TextCtrl 对象不再存在,所以我无法访问它。我不明白为什么 TextCtrl 对象不再存在。流程如下:

框架出现时带有标签、TextCtrl 和按钮。用户输入数据并点击下一步。一切顺利。然后创建了同一个 PromptFrame 类的不同实例,同样的事情发生了。然而这一次,当用户点击下一步时,我得到了上述错误。这是代码:

后台运行节目的服务:

class AppService(object):

    prompts = [Prompt_1, Prompt_2, Prompt_3, Prompt_4, Prompt_5, Prompt_6, Prompt_7,
            Prompt_8, Prompt_9, Prompt_10, Prompt_11, Prompt_12, Prompt_13, Prompt_14,
             Prompt_15, Prompt_16, Prompt_17, Prompt_18, Prompt_19]

    skippedPromptIndices = []

    def __init__(self):
        print "Service Started"
        PromptFrame(self, self.prompts[0], 0, len(self.prompts))

    def doBack(self, curIndex, curPrompt):
        if curIndex >= 0:
            self.prompts[curIndex] = curPrompt
            PromptFrame(self, self.prompts[curIndex - 1], curIndex - 1, len(self.prompts))
        else:
            posCurIndex = (curIndex * -1) - 1
            self.prompts[posCurIndex] = curPrompt

            backIndex = self.skippedPromptIndices.index(curIndex) - 1
            nextPromptIndex = 0
            if backIndex < 0:
                nextPromptIndex = len(self.prompts) - 1
            else:
                nextPromptIndex = self.skippedPromptIndices[backIndex]

            PromptFrame(self, self.prompts[(nextPromptIndex * -1) - 1], nextPromptIndex, len(self.prompts))

    def doSkip(self, curIndex, curPrompt):
        skipIndex = (curIndex + 1) * -1
        if self.skippedPromptIndices.count(skipIndex) > 0:
            self.skippedPromptIndices.remove(skipIndex)

        self.skippedPromptIndices.append(skipIndex)
        self.doNext(curIndex, curPrompt)

    def doNext(self, curIndex, curPrompt):
        if curIndex >= 0:
            self.prompts[curIndex] = curPrompt
        else:
            self.prompts[(curIndex * -1) - 1] = curPrompt


        if (curIndex >= 0 and curIndex < (len(self.prompts) - 1)):
            PromptFrame(self, self.prompts[curIndex + 1], curIndex + 1, len(self.prompts))
        elif len(self.skippedPromptIndices) > 0:
            skipIndex = self.skippedPromptIndices.pop(0)
            nextIndex = (skipIndex * -1) - 1
            PromptFrame(self, self.prompts[nextIndex], skipIndex, len(self.prompts))
        else:
            dlg = wx.MessageDialog(self, "Done!", "Message", wx.OK)
            dlg.ShowModal() # Shows it
            dlg.Destroy() # finally destroy it when finished.

这是 PromptFrame 类:

class PromptFrame(wx.Frame):

    appService = None
    prompt = None
    promptIndex = 0
    numPrompts = 0
    ansControls = []

    def __init__(self, appService, prompt=testPrompt, promptIndex=0, numPrompts=0):
        print "New Prompt Frame!"
        self.appService = appService
        self.prompt = prompt
        self.promptIndex = promptIndex
        self.numPrompts = numPrompts

        wx.Frame.__init__(self, None, wx.ID_ANY, title=prompt.title)
        self.panel = wx.Panel(self, wx.ID_ANY)
        self.panel.SetBackgroundColour('#2FC0E0') #0013E3, #66DFFA, #2FC0E0
        self.Maximize()
        self.CreateStatusBar() # A Statusbar in the bottom of the window


        # Setting up the menu.
        filemenu= wx.Menu()
        menuAbout= filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
        menuExit = filemenu.Append(wx.ID_EXIT,"E&xit"," Terminate the program")

        # Creating the menubar.
        menuBar = wx.MenuBar()
        menuBar.Append(filemenu,"&File") # Adding the "filemenu" to the MenuBar
        self.SetMenuBar(menuBar)  # Adding the MenuBar to the Frame content.

        # Creating fonts for the controls
        titleFont = wx.Font(24, wx.DECORATIVE, wx.ITALIC, wx.BOLD)
        qFont = wx.Font(12, wx.FONTFAMILY_DECORATIVE, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)

        #Creating layout
        vertSizer = wx.BoxSizer(wx.VERTICAL)

        lblTitle = wx.StaticText(self.panel, wx.ID_ANY, prompt.title)
        lblTitle.SetFont(titleFont)

        vertSizer.Add(lblTitle, 0, wx.ALIGN_CENTER | wx.TOP, border=30)
        vertSizer.Add(wx.StaticLine(self.panel, wx.ID_ANY), 0, wx.EXPAND)
        vertSizer.AddStretchSpacer(2)

        for i in range(len(prompt.questions)):
            if prompt.qTypes[i] == 0:
                lbl = wx.StaticText(self.panel, wx.ID_ANY, prompt.questions[i], size=(200, -1))
                lbl.SetFont(qFont)
                lbl.Wrap(200)                                    
                ans = wx.TextCtrl(self.panel, wx.ID_ANY, size=(200,-1))
                if not prompt.answers[i] == None:
                    ans.SetValue(prompt.answers[i])
                self.ansControls.append(ans)

                horizSizer = wx.BoxSizer(wx.HORIZONTAL)
                horizSizer.Add((30, -1), 0)
                horizSizer.Add(lbl, 0)
                horizSizer.Add((20, -1), 0)
                horizSizer.Add(self.ansControls[len(self.ansControls) - 1], 0)

                vertSizer.Add(horizSizer, 0)
                vertSizer.AddStretchSpacer(1)

        print self.ansControls

        vertSizer.Add(wx.StaticLine(self.panel, wx.ID_ANY), 0, wx.EXPAND)

        self.btnBack = wx.Button(self.panel, wx.ID_ANY, "Back")
        self.btnSkip = wx.Button(self.panel, wx.ID_ANY, "Skip")
        self.btnNext = wx.Button(self.panel, wx.ID_ANY, "Next")

        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
        btnSizer.Add(self.btnBack, 0, wx.RIGHT, border=30)
        btnSizer.Add(self.btnSkip, 0)
        btnSizer.Add(self.btnNext, 0, wx.LEFT, border=30)
        btnSizer.AddStretchSpacer(1)

        vertSizer.AddStretchSpacer(2)
        vertSizer.Add(btnSizer, 0, wx.ALIGN_CENTER)        
        vertSizer.AddStretchSpacer(2)

        lblPage = wx.StaticText(self.panel, wx.ID_ANY, "Page: " + str(self.promptIndex) + "/" + str(self.numPrompts))
        vertSizer.Add(lblPage, 0, wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT | wx.ALL, border=20)

        self.panel.SetSizer(vertSizer)

        # Events.
        self.Bind(wx.EVT_MENU, self.OnExit, menuExit)
        self.Bind(wx.EVT_MENU, self.OnAbout, menuAbout)
        self.Bind(wx.EVT_BUTTON, self.OnBack, self.btnBack)
        self.Bind(wx.EVT_BUTTON, self.OnSkip, self.btnSkip)
        self.Bind(wx.EVT_BUTTON, self.OnNext, self.btnNext)

        self.Show()

    def OnAbout(self,e):
        # Create a message dialog box
        aboutString = "This program was designed by students of Worcester Polytechnic Institute to aid water supply " \
                  "officials apply the World Health Organization's Water Safety Plans."
        dlg = wx.MessageDialog(self, aboutString, "About Water Safety Plan Helper", wx.OK)
        dlg.ShowModal() # Shows it
        dlg.Destroy() # finally destroy it when finished.

    def OnExit(self,e):
        self.Close(True)  # Close the frame.

    def OnBack(self, e):
        if (self.promptIndex > 0):
            self.SaveAns()
            self.appService.doBack(self.promptIndex, self.prompt)
            self.Close(True)
        else:
            errorString = "There are no previous pages to go back to"
            dlg = wx.MessageDialog(self, errorString, "Error", wx.OK)
            dlg.ShowModal() # Shows it
            dlg.Destroy() # finally destroy it when finished.

    def OnSkip(self, e):
        self.SaveAns()
        self.appService.doSkip(self.promptIndex, self.prompt)
        self.Close(True)

    def OnNext(self, e):
        self.SaveAns()
        self.appService.doNext(self.promptIndex, self.prompt)
        self.Close(True)

    def SaveAns(self):
        print self.ansControls
        for i in range(len(self.ansControls)):
            if self.prompt.qTypes[i] == 0:
                self.prompt.addAnswer(i, self.ansControls[i].GetValue())    

谢谢大家的帮助!

编辑:这是我的init.py 文件:

from MainFrame import MainFrame
import wx

app = wx.App(False)
frame = MainFrame(None, "My App")
app.MainLoop() 

最佳答案

ansControls 当前定义为类变量。这意味着在任何窗口中定义的任何控件都会添加到其中。

你在第一个实例中创建一个控件,它被添加到类中,但是窗口属于实例。所以当你销毁类时,实例被销毁,但指向它的 python 对象仍然存在。

然后您打开第二个窗口,添加更多控件,然后点击循环遍历它们。循环中的第一个将不再有有效的 C++ 对象,并且会失败。

不确定为什么将它们定义为类变量,但您也需要保留指向根窗口的指针,在删除父窗口时删除类控件,或者(更简单)只制作 ansControls 实例变量而不是类变量...

关于python - TextCtrl 的 wxPython C++ 部分被删除,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22765537/

相关文章:

python - 使用 Python 和 wxPython 的 3D/4D 图形?

python - 为什么使用 wxPython 时会出现两个窗口?

python - 如何更改 Python AssertionError 中的消息?

python - 在 Python 中调用实例变量时遇到问题

python - 如何使用python多次训练SVM分类器?

python - python2.7 中最少的非官方​​枚举支持 - flufl.enum 或 enum34?

python - 执行 subprocess.Popen ('su' 时挂起,shell=True)

python - 检查时间控制并更改 listrCtrl 中的颜色

python - 使用 python 在 Azure Event Hub 中收集 Websocket 流数据

python - Pandas 多重过滤器 str.contains 或 not contains