wxpython 中 Destroy() 的简单错误?

2 投票
2 回答
745 浏览
提问于 2025-04-17 06:09

我遇到了一个有趣的问题。

这个程序是一个简单的图片查看器,可以在一个列表框里显示不同的图片。列表框里列出了图片的名字。你可以把一张图片加载到列表框中的某个项目上。点击列表框里的任何一个项目,就能看到对应的图片。不过,出于某种原因,Destroy()这个功能似乎不太正常。如果你不太明白我在说什么,可以运行下面的代码:

IMAGE_NAME=[]
IMAGE_DATA=[]
IMAGE_LISTSEL=[]
import sys
import wx
def deletepic(self,parent):
    try:
        bitmap1.Destroy()
        bmp1.Destroy()
    except:
        print sys.exc_info()
def sendnewpic(self,parent):
    global scroll_img
    deletepic(self,parent)  
    print IMAGE_DATA[IMAGE_LISTSEL[0]]
    if IMAGE_DATA[IMAGE_LISTSEL[0]]!='':
        try:
            print IMAGE_DATA[IMAGE_LISTSEL[0]]
            bmp1 = wx.Image(IMAGE_DATA[IMAGE_LISTSEL[0]], wx.BITMAP_TYPE_ANY).ConvertToBitmap()
            bitmap1 = wx.StaticBitmap(scroll_img, -1, bmp1, (0, 0))
        except:
            pass
def areachange(self,pg):
    print pg
    try:
        if IMAGE_DATA[IMAGE_LISTSEL[0]]=='':
            deletepic(self,parent)
    except:
        pass
    if pg=="Images":
        self.images_area.Show()
    else:
        self.images_area.Hide()
class imageMax(wx.Panel):
    pass

class imageTab(imageMax):
    def imagesel(self,parent):
        IMAGE_LISTSEL[:] = []
        IMAGE_LISTSEL.append(self.listBox.GetSelection())
        sendnewpic(self,parent)
    def newAddImage(self,parent):
        IMAGE_NAME.append('hi');
        IMAGE_DATA.append('');
        self.listBox.Set(IMAGE_NAME)
        self.listBox.SetSelection(len(IMAGE_NAME)-1)
        self.imagesel(self) #making it a selected image, globally

    def reName(self,parent):
        sel = self.listBox.GetSelection()
        text = self.listBox.GetString(sel)
        renamed = wx.GetTextFromUser('Rename item', 'Rename dialog', text)
        if renamed != '':
            IMAGE_NAME.pop(sel)
            IMAGE_NAME.insert(sel,renamed)
            self.listBox.Set(IMAGE_NAME)
            self.listBox.SetSelection(sel)
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.listBox = wx.ListBox(self, size=(200, -1), choices=IMAGE_NAME, style=wx.LB_SINGLE)
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        btnSizer = wx.BoxSizer(wx.VERTICAL) #change to horizontal for side by side
        self.sizerMain = wx.BoxSizer()
        self.listBox.Bind(wx.EVT_LISTBOX_DCLICK, self.reName)
        self.listBox.Bind(wx.EVT_LISTBOX, self.imagesel)
        btn = wx.Button(self, label="Create New",size=(200, 40))
        btnTwo = wx.Button(self, label="Test 2",size=(200, 40))
        btn.Bind(wx.EVT_BUTTON, self.newAddImage)
        self.sizer.Add(self.listBox, proportion=1, flag=wx.TOP | wx.EXPAND | wx.LEFT, border=5)
        btnSizer.Add(btn, 0, wx.ALL, 5)
        btnSizer.Add(btnTwo, 0, wx.ALL, 5)
        self.sizer.Add(btnSizer)
        self.sizerMain.Add(self.sizer, proportion=0, flag=wx.BOTTOM | wx.EXPAND, border=0)
        self.SetSizer(self.sizerMain)

class MyNotebook(wx.Notebook):
    def __init__(self, *args, **kwargs):
        wx.Notebook.__init__(self, *args, **kwargs)

class MyPanel(imageTab):

    def OnClickTop(self, event):
        scroll_img.Scroll(600, 400)

    def OnClickBottom(self, event):
        scroll_img.Scroll(1, 1)

    def OnPageChanged(self, event):
        new = event.GetSelection()
        areachange(self,self.notebook.GetPageText(new))
        event.Skip()

    def OnPageChanging(self, event):
        event.Skip()
    def onOpenFile(self,parent):
        """ Open a file"""
        filename = wx.FileSelector()
        if (filename!=''):
            global bitmap1,bmp1,scroll_img
            if IMAGE_DATA[IMAGE_LISTSEL[0]]!='':
                deletepic(self,parent)
            bmp1 = wx.Image(filename, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
            bitmap1 = wx.StaticBitmap(scroll_img, -1, bmp1, (0, 0))
            scroll_img.SetScrollbars(1, 1, bmp1.GetWidth(), bmp1.GetHeight())
            IMAGE_DATA[IMAGE_LISTSEL[0]]=filename
            print IMAGE_DATA

    def __init__(self, *args, **kwargs):
        global bitmap1,bmp1,scroll_img
        wx.Panel.__init__(self, *args, **kwargs)
        self.notebook = MyNotebook(self, size=(225, -1))
#        self.button = wx.Button(self, label="Something else here? Maybe!")
        tab_images = imageTab(self.notebook)
        # add the pages to the notebook with the label to show on the tab
        self.notebook.AddPage(tab_images, "Pics",select=True)

        scroll_img = wx.ScrolledWindow(self, -1)
        scroll_img.SetScrollbars(1, 1, 600, 400)
        #self.button = wx.Button(scroll_img, -1, "Scroll Me", pos=(50, 20))
        #self.Bind(wx.EVT_BUTTON,  self.OnClickTop, self.button)
        #self.button2 = wx.Button(scroll_img, -1, "Scroll Back", pos=(500, 350))
        #self.Bind(wx.EVT_BUTTON, self.OnClickBottom, self.button2)

        self.images_area=wx.StaticBox(self, -1, '')
        self.sizerBox = wx.StaticBoxSizer(self.images_area,wx.HORIZONTAL)
        #self.load_file=wx.Button(self, label='Load File')
        #self.sizerBox.Add(self.load_file,0,wx.ALL,5)
        self.sizerBox2 = wx.BoxSizer()
        self.sizerBox.Add(scroll_img, 1, wx.EXPAND|wx.ALL, 10)
        self.sizerBox2.Add(self.sizerBox, 1, wx.EXPAND|wx.ALL, 10)
        self.sizer = wx.BoxSizer()
        self.sizer.Add(self.notebook, proportion=0, flag=wx.EXPAND)
#        self.sizer.Add(self.button, proportion=0)
        btnSizer = wx.BoxSizer() #change to horizontal for side by side
        btnTwo = wx.Button(self, label="Load File",size=(200, 40))
        btnTwo.Bind(wx.EVT_BUTTON,self.onOpenFile)

        bmp1 = None
        bitmap1 = None

        btnSizer.Add(btnTwo, 0, wx.TOP, 15)
        self.sizerBox2.Add(btnSizer)
        #self.sizerBox.Add(self.bitmap1)
        self.sizer.Add(self.sizerBox2, proportion=1, flag=wx.EXPAND)

        self.SetSizer(self.sizer)
        self.notebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
        areachange(self,self.notebook.GetPageText(0))

class MainWindow(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        self.panel = MyPanel(self)

        self.Show()

app = wx.App(False)
win = MainWindow(None, size=(600, 400))
app.MainLoop()

试试这个方法来看看问题出在哪里: 1. 你可以多次点击“创建新项目”按钮。 2. 然后点击“加载文件”。 3. 接着点击列表框中的任何一个项目(除了你当前选中的那个)。 4. 然后再返回到你原来选中的项目。 5. 最后再点击任何一个项目,除了你现在选中的那个。

这样做会出现一些问题,图片没有被删除,还会返回一个错误信息,比如: (, PyDeadObjectError('StaticBitmap对象的C++部分已经被删除,不再允许访问属性。',), ) 我仍然可以加载新的图片,但之前的图片却没有被删除。

这个问题很难用语言表达清楚,如果有人能帮我解决这个问题,我会非常感激。如果你需要更详细的解释,请留言。我非常感谢你们的关注。

2 个回答

0

有时候你把 bmp1 和 bitmap1 当作局部变量使用,有时候又当作全局变量。因为你在创建它们的多个实例时,没有把之前的引用保存下来,所以你会失去对已经存在的对象的引用。当你调用 Destroy() 方法时,你只是在销毁最近创建的那些实例。

试着把它们放到某种集合里(比如一个列表),这样你以后需要的时候就可以从列表中访问任何一个项目。另外,尽量避免使用全局变量。把你的变量存储在它们所属的对象实例中。

3

这里有你的代码修正了,当加载另一个图片时,会清除当前的图片。这个主要是通过使用 self.parent.bitmap.Destroy() 来实现的。

我对一些地方进行了修改,但没有改变你代码的结构,这样你可以更容易地识别出变化。我去掉了全局变量的调用。看看我是如何去掉全局的 IMAGE_LISTSEL 变量,并把它转换成类的属性的。这正是Robin和Fenikso想告诉你的。试着对 IMAGE_NAMEIMAGE_DATA 也做同样的处理。

虽然代码可以运行,但距离一个合格的wxpython代码还有很大差距。你可以在网上找到很多写得很好的wxpython代码示例。如果你有条件的话,我推荐你看看 wxPython in Action,作者是 Noel RappinRobin Dunn

IMAGE_NAME = []
IMAGE_DATA = []

import sys
import wx

def deletepic(self):
    try:
        self.parent.bitmap.Destroy()
    except:
        print sys.exc_info()

def sendnewpic(self):
    if self.parent.bitmap: deletepic(self)  
    if IMAGE_DATA[self.image_listsel] != '':
        try:
            print IMAGE_DATA[self.image_listsel]
            bmp = wx.Image(IMAGE_DATA[self.image_listsel], wx.BITMAP_TYPE_ANY).ConvertToBitmap()
            self.parent.scroll_img.SetScrollbars(1, 1, bmp.GetWidth(), bmp.GetHeight())
            self.parent.bitmap = wx.StaticBitmap(self.parent.scroll_img, -1, bmp, (0, 0))
            self.parent.Refresh()
        except:
            pass

def areachange(self, pg):
    print pg
    try:
        if IMAGE_DATA[self.image_listsel] == '':
            deletepic(self)
    except:
        pass

    if pg == "Images":
        self.images_area.Show()
    else:
        self.images_area.Hide()


class imageTab(wx.Panel):

    def __init__(self, parent, grandparent):
        wx.Panel.__init__(self, parent)
        self.parent = grandparent
        self.image_listsel = 0
        self.listBox = wx.ListBox(self, size=(200, -1), choices=IMAGE_NAME, style=wx.LB_SINGLE)
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        btnSizer = wx.BoxSizer(wx.VERTICAL) #change to horizontal for side by side
        self.sizerMain = wx.BoxSizer()
        self.listBox.Bind(wx.EVT_LISTBOX_DCLICK, self.reName)
        self.listBox.Bind(wx.EVT_LISTBOX, self.imagesel)
        btn = wx.Button(self, label="Create New",size=(200, 40))
        btnTwo = wx.Button(self, label="Test 2",size=(200, 40))
        btn.Bind(wx.EVT_BUTTON, self.newAddImage)
        self.sizer.Add(self.listBox, proportion=1, flag=wx.TOP | wx.EXPAND | wx.LEFT, border=5)
        btnSizer.Add(btn, 0, wx.ALL, 5)
        btnSizer.Add(btnTwo, 0, wx.ALL, 5)
        self.sizer.Add(btnSizer)
        self.sizerMain.Add(self.sizer, proportion=0, flag=wx.BOTTOM | wx.EXPAND, border=0)
        self.SetSizer(self.sizerMain)

    def imagesel(self, evt):
        self.image_listsel = self.listBox.GetSelection()
        sendnewpic(self)

    def newAddImage(self, evt):
        IMAGE_NAME.append('hi')
        IMAGE_DATA.append('')
        self.listBox.Set(IMAGE_NAME)
        self.listBox.SetSelection(len(IMAGE_NAME)-1)
        self.imagesel(None) #making it a selected image, globally

    def reName(self,parent):
        sel = self.listBox.GetSelection()
        text = self.listBox.GetString(sel)
        renamed = wx.GetTextFromUser('Rename item', 'Rename dialog', text)
        if renamed != '':
            IMAGE_NAME.pop(sel)
            IMAGE_NAME.insert(sel,renamed)
            self.listBox.Set(IMAGE_NAME)
            self.listBox.SetSelection(sel)


class MyPanel(wx.Panel):

    def __init__(self, *args, **kwargs):

        wx.Panel.__init__(self, *args, **kwargs)
        self.notebook = wx.Notebook(self, size=(225, -1))
# 
        self.tab_images = imageTab(self.notebook, self)
        # add the pages to the notebook with the label to show on the tab
        self.notebook.AddPage(self.tab_images, "Pics", select=True)

        self.scroll_img = wx.ScrolledWindow(self, -1)
        self.scroll_img.SetScrollbars(1, 1, 600, 400)

        self.images_area = wx.StaticBox(self, -1, '')
        self.sizerBox = wx.StaticBoxSizer(self.images_area, wx.HORIZONTAL)

        self.sizerBox2 = wx.BoxSizer()
        self.sizerBox.Add(self.scroll_img, 1, wx.EXPAND|wx.ALL, 10)
        self.sizerBox2.Add(self.sizerBox, 1, wx.EXPAND|wx.ALL, 10)
        self.sizer = wx.BoxSizer()
        self.sizer.Add(self.notebook, proportion=0, flag=wx.EXPAND)
#
        btnSizer = wx.BoxSizer() #change to horizontal for side by side
        btnTwo = wx.Button(self, label="Load File", size=(200, 40))
        btnTwo.Bind(wx.EVT_BUTTON, self.onOpenFile)

        self.bmp = None
        self.bitmap = None

        btnSizer.Add(btnTwo, 0, wx.TOP, 15)
        self.sizerBox2.Add(btnSizer)
        #
        self.sizer.Add(self.sizerBox2, proportion=1, flag=wx.EXPAND)

        self.SetSizer(self.sizer)
        self.notebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
        areachange(self, self.notebook.GetPageText(0))

    def OnClickTop(self, event):
        self.scroll_img.Scroll(600, 400)

    def OnClickBottom(self, event):
        self.scroll_img.Scroll(1, 1)

    def OnPageChanged(self, event):
        new = event.GetSelection()
        areachange(self, self.notebook.GetPageText(new))
        event.Skip()

    def OnPageChanging(self, event):
        event.Skip()

    def onOpenFile(self, evt):
        """ Open a file"""
        filename = wx.FileSelector()
        if filename != '':
            IMAGE_DATA[ self.tab_images.image_listsel] = filename
            self.tab_images.imagesel(None)
            print IMAGE_DATA


class MainWindow(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        self.panel = MyPanel(self)
        self.Show()


app = wx.App(False)
win = MainWindow(None, size=(600, 400))
app.MainLoop()

撰写回答