[wxPython] Yet another disappearing bitmaps in list controls mystery...

I'm working on a by-name colour chooser, something that lets you choose a
colour from a list of names will little colour swatches (icons) off to the
side. I build the colour swatches from Numeric arrays then convert to
Image, then bitmap, and add them to the image list for the object. The code
seems fine (it's not finished, of course, but no errors/exceptions raised),
except that the icons are not actually showing up in the list control.

Notes:
  wxPython 2.3.0 (downloaded minutes ago), 2.2.5 has same result
  In the code, I'm keeping an explicit reference to each bitmap in a
dictionary, doesn't seem to help at all.
  Bitmaps return Ok() as true.
  There's no way I've yet found to convert to wxIcon from wxBitmap, so I
can't go that way :frowning: .
  You'll need to modify your (magic)imagelist to allow wxBitmapPtrs if you
want to see it running at all.
  wxImage.ConvertToBitmap() and wxBitmapFromImage() both return a wxBitmapPtr
instance, not a wxBitmap, not sure if that's normal, it requires a change to
the imagelist mixin regardless, as that checks for isinstance( wxBitmap,
icon ).

Any suggestions would be appreciated,
Mike

8<____________ colorlistcontrol.py _______
from wxPython.wx import *
from Numeric import *
from wxcontrols import magicimagelist

class ColorListControl( wxListCtrl, magicimagelist.MagicImageList ):
    """The color list control allows you to select
    from a list of (predefined) colours by name and
    a colour swatch (icon)"""
    def __init__(
        self, parent, id=-1,
        pos = wxDefaultPosition, size = wxDefaultSize,
        style = wxLC_SMALL_ICON , validator = wxDefaultValidator,
        name = "colorlist",
    ):
        wxListCtrl.__init__( self, parent, id, pos, size, style, validator,
name )
        self.items = {}
        self.bitmaps = {}
        self.SetupIcons( )
        self.GetInitialPopulation()

    def AddNamedColor( self, name, (r,g,b) ):
        """Add a single named color to the items and the display"""
        normal, selected = self.GetIcons( (r,g,b) )
        self.InsertImageStringItem( self.GetItemCount(), name, normal )
    def GetInitialPopulation( self ):
        """Populate with the default colours"""
## import csscolors
## items = csscolors.colors.items()
## items.sort()
        items = [("black", (0,0,0)), ("blue", (0,0,255))]
        for key, value in items:
            self.AddNamedColor( key, value )
    def GetIcon( self, (r,g,b) ):
        if self.bitmaps.get( (r,g,b)):
            return self.bitmaps.get( (r,g,b))
        data = zeros( (self.DEFAULTICONSIZE, self.DEFAULTICONSIZE, 3), "c")
        data[:,:] = (chr(r), chr(g), chr(b))
        image = wxEmptyImage( self.DEFAULTICONSIZE, self.DEFAULTICONSIZE )
        image.SetData( data.tostring())
        bitmap = wxBitmapFromImage( image ) #image.ConvertToBitmap()
        self.bitmaps[ (r,g,b) ] = bitmap
        print 'bitmap ok', bitmap.Ok()
        return bitmap

if __name__ == "__main__":
    class TestFrame(wxFrame):
        def __init__( self ):
            wxFrame.__init__( self, NULL, -1, "Test", size = (500,500) )
            ColorListControl( self )
    class App( wxPySimpleApp):
        def OnInit( self ):
            TestFrame().Show( 1)
            return 1
    App().MainLoop()

In the code, I'm keeping an explicit reference to each bitmap in a
dictionary, doesn't seem to help at all.
Bitmaps return Ok() as true.

The issues is that there is nothing keeping a reference to the image list.
Well, actually a reference to it in the MagicImageList mixin, but there is
no reference kept to the ColorListControl so its ref-count goes to zero and
cleaned up. BTW, if you hook an event handler to it then there will be a
reference help internally to wxPython for you and you won't have to worry
about this.

wxImage.ConvertToBitmap() and wxBitmapFromImage() both return a

wxBitmapPtr

instance, not a wxBitmap, not sure if that's normal,

Yep, it is. Any of the C++ methods that return a pointer to an existing
object will get wrapped in a wxClassNamePtr python object. Basically it
means that Python doesn't own the object so don't do anything special in the
__del__ method.

···

--
Robin Dunn
Software Craftsman
robin@AllDunn.com Java give you jitters?
http://wxPython.org Relax with wxPython!

Argh! Yup, that was it. It was necessary to hold a reference to the frame
and the list control before it would work, as both were being collected.
For reference, here's the revised code:

if __name__ == "__main__":
    class TestFrame(wxFrame):
        def __init__( self ):
            wxFrame.__init__( self, NULL, -1, "Test", size = (500,500) )
            self.control = ColorListControl( self )
    class App( wxPySimpleApp):
        def OnInit( self ):
            frame = TestFrame()
            self.frame = frame
            frame.Show( 1)
            self.SetTopWindow( frame )
            return 1
    App().MainLoop()

Sigh. Thanks Robin, you'd think I'd know this stuff by now. Any chance that
ListControls will add their own reference during SetImageList (I think you
mentioned something like it some time ago, but I think that was for the
bitmaps, not the image list).

Enjoy yourself,
Mike

[mailto:wxpython-users-admin@lists.wxwindows.org]On Behalf Of Robin Dunn

···

-----Original Message-----
From: wxpython-users-admin@lists.wxwindows.org
Sent: June 15, 2001 23:31
To: wxpython-users@lists.wxwindows.org
Subject: Re: [wxPython] Yet another disappearing bitmaps in list
controls mystery...

In the code, I'm keeping an explicit reference to each bitmap in a
dictionary, doesn't seem to help at all.
Bitmaps return Ok() as true.

The issues is that there is nothing keeping a reference to the image list.
Well, actually a reference to it in the MagicImageList mixin, but there is
no reference kept to the ColorListControl so its ref-count goes to zero and
cleaned up. BTW, if you hook an event handler to it then there will be a
reference help internally to wxPython for you and you won't have to worry
about this.

wxImage.ConvertToBitmap() and wxBitmapFromImage() both return a

wxBitmapPtr

instance, not a wxBitmap, not sure if that's normal,

Yep, it is. Any of the C++ methods that return a pointer to an existing
object will get wrapped in a wxClassNamePtr python object. Basically it
means that Python doesn't own the object so don't do anything special in the
__del__ method.

--
Robin Dunn
Software Craftsman
robin@AllDunn.com Java give you jitters?
http://wxPython.org Relax with wxPython!

_______________________________________________
wxpython-users mailing list
wxpython-users@lists.wxwindows.org
http://lists.wxwindows.org/mailman/listinfo/wxpython-users

Sorry for the dumb question, had I looked it up I'd have found it. Here's a
revised MagicImageList class that eliminates the problem (but only works for
the lastest versions of wxPython)...

8<____ magicimagelist.py _________
from wxPython.wx import *

class MagicImageList:
    '''Mix-in to provide "magic" growing image lists'''
    ### LAZYTREE and LISTCONTROL Methods
    DEFAULTICONSIZE = 16
    def SetupIcons(self, images=(), size=None):
        self.__size = size or self.DEFAULTICONSIZE
        self.__magicImageList = wxImageList (self.__size,self.__size)
        self.__magicImageListMapping = {}
        try:
            self.AssignImageList(
                self.__magicImageList, {
                    16:wxIMAGE_LIST_SMALL,
                    32:wxIMAGE_LIST_NORMAL,
                }[self.__size]
            )
        except TypeError:
            self.SetImageList (
                self.__magicImageList,
            )
        for image in images:
            self.AddIcon (image)
    def GetIcons (self, node):
        '''Get icon indexes for a given node, or None if no associated
node'''
        icon = self.GetIcon( node )
        if icon:
            index = self.AddIcon (icon)
            return index, index
        return None

    ### Local methods...
    def AddIcon(self, icon, mask = wxNullBitmap):
        '''Add an icon to the image list, or get the index if already
there'''
        index = self.__magicImageListMapping.get (id (icon))
        if index is None:
            if isinstance( icon, wxIconPtr ):
                index = self.__magicImageList.AddIcon( icon )
            elif isinstance( icon, wxBitmapPtr ):
                if isinstance( mask, wxColour ):
                    index = self.__magicImageList.AddWithColourMask( icon,
mask )
                else:
                    index = self.__magicImageList.Add( icon, mask )
            else:
                raise ValueError( '''Unexpected icon object %s, expected
wxIcon or wxBitmap'''%(icon))
            self.__magicImageListMapping [id (icon)] = index
        return index
    ### Customisation point...
    def GetIcon( self, node ):
        '''Get the actual icon object for a node'''
        if hasattr (node,"DIAGRAMICON"):
            return node.DIAGRAMICON
        elif hasattr( node, "getIcon" ) and callable(node.getIcon):
            return node.getIcon()
        return None

···

-----Original Message-----
From: wxpython-users-admin@lists.wxwindows.org
[mailto:wxpython-users-admin@lists.wxwindows.org]On Behalf Of Mike C.
Fletcher
Sent: June 16, 2001 04:31
To: wxpython-users@lists.wxwindows.org
Subject: RE: [wxPython] Yet another disappearing bitmaps in list
controls mystery...
...
Sigh. Thanks Robin, you'd think I'd know this stuff by now. Any chance that
ListControls will add their own reference during SetImageList (I think you
mentioned something like it some time ago, but I think that was for the
bitmaps, not the image list).
...