wxPython中的TextCtrl部分被删除
我正在用Python为学校项目制作一个图形界面,但遇到了一个错误,我不太明白为什么会这样。
在代码中,出现了这样的错误信息:self.prompt.addAnswer(i, self.ansControls[i].GetValue()) 文件 "C:\Python27\lib\site-packages\wx-3.0-msw\wx_core.py",第16712行,getattr 报错:PyDeadObjectError(对象已死),意思是TextCtrl这个对象已经被删除了,不能再访问它了。
我明白这个错误的意思,就是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()
1 个回答
ansControls
目前被定义为一个类变量。这意味着在任何窗口中定义的控件都会被添加到这个变量里。
你在第一次创建控件时,它会被添加到这个类中,但这个窗口是属于实例的。所以当你销毁这个类时,实例也会被销毁,但指向它的 Python 对象仍然存在。
然后你打开第二个窗口,添加更多控件,接着进入一个循环去处理这些控件。此时,循环中的第一个控件将不再有有效的 C++ 对象支持,这样就会出错。
不太清楚为什么它们被定义为类变量,但你要么需要保留一个指向根窗口的指针,要么在父窗口被删除时删除类控件,或者(更简单的做法)直接把 ansControls
改成实例变量,而不是类变量……