Introduction

When using py2exe, you can embed icons in the resource section of your exe file. In SmallApp and other places I've seen comments about not knowing how to do this, and so the author always resorts to including the .ico file separately with the exe and loading it the usual way. I was challenged to see whether it was possible and, if so, how to load an icon from the resource section of the exe itself. This is really intended only to be used when py2exe is invoked on the application, and in fact the results are different when running as a py2exe'd app and when invoked directly from Python.

I provide this not as an example of "how to do it", but merely how I managed to get it done. I suspect there may be much cleaner approaches, and readily admit this is a gross hack. I'm no Windows GUI programmer (though wxPython is helping me become one), so feel free to demonstrate your vastly superior knowledge by improving this recipe. :-)

What Objects are Involved

This requires the following modules, which might not normally be used with your wxPython app:

In addition, the recipe as currently written requires

Process Overview

The win32api.LoadResource function can retrieve raw data stored in the application's resource section. To use it you specify the type of resource to be retrieved, and an id which was specified when the item was stored there.

This data can be turned into a wx.Image using wx.ImageFromStream() and StringIO to make a file-like object for the stream.

From there you can convert the image into a wx.Bitmap and from there turn it into an icon object.

Lastly you use self.SetIcon() as you normally would to stick the icon on your application's title bar (or use the icon for whatever other purpose you have).

Special Concerns

Limitations, problems, etc:

Code Sample

   1 import wx
   2 import win32api, win32con
   3 import StringIO, binascii
   4 
   5 ICON_HEADER = binascii.unhexlify(
   6     '00 00 01 00 01 00 20 20 00 00 00 00 00 00 A8 08 00 00 16 00 00 00'.replace(' ', ''))
   7 
   8 class MyFrame( wx.Frame ) :
   9 
  10     def __init__( self, parent=None ) :
  11         wx.Frame.__init__( self, parent, wx.ID_ANY )
  12 
  13         # load icon that has an id of 1
  14         icon_data = win32api.LoadResource( 0, win32con.RT_ICON, 1 )
  15 
  16         stream = StringIO.StringIO( ICON_HEADER + icon_data )
  17         img = wx.ImageFromStream( stream, wx.BITMAP_TYPE_ICO )
  18         bmap = img.ConvertToBitmap()
  19 
  20         # desperate attempt to get an empty icon
  21         icon = wx.EmptyIcon()
  22         icon.CopyFromBitmap( bmap )
  23 
  24         # set title bar icon to use this one
  25         self.SetIcon( icon )
  26 
  27 
  28 if __name__ == '__main__' :
  29     app = wx.App( redirect=False )
  30     frame = MyFrame()
  31     frame.Show()
  32     app.MainLoop()

When run from the command line, this will set the title bar icon to the first icon used in the python.exe (or pythonw.exe) file. In order to get your own icon into the application, using py2exe, you need a setup.py file that looks something like this:

   1 from distutils.core import setup
   2 import py2exe
   3 setup(
   4     console=[
   5         dict(
   6             script='wxiconrecipe.py',
   7             icon_resources=[ (1, 'wxiconrecipe.ico') ],
   8             )
   9         ],
  10 )

Note that the 1 just before the icon file name is the id to be used to reference the icon resource, so make sure you use the same value when you call LoadResource() in the application.

Comments

Yuck! That's about all I have to say for this recipe for now, but as a fanatic at avoiding duplication, I didn't like having the same icon file stored as a resource in my exe and also stored right next to it as an .ico file. I was also interested in learning how I might access other resources that were stored in the file, such as the Manifest file (resource type 24) that can now be used on Windows XP.

In principle, using win32api.LoadResource() can also retrieve bitmaps, HTML, strings, fonts, and even user-defined resources (by name). For example, the .exe file generated by py2exe appears to have the bytecode for the main script frozen inside it and stored in a resource with the type name u'PYTHONSCRIPT'. (Calling win32api.EnumResourceTypes(0) will show all resource types present in the file. Integer values correspond to constants in win32con that start with RT_, such as win32con.RT_ICON (3), and so on.) A call to win32api.LoadResource( 0, u'PYTHONSCRIPT', 0 ) will retrieve this bytecode, if you happened to want to do that...

Now all I need to do is learn how to create other resources with names like that. I'm not sure py2exe supports it (yet?)...

Page originally created by:
-- Peter Hansen [ peter@engcorp.com ]
http://www.engcorp.com


Instead of wx.NullIcon you can use wx.EmptyIcon. This should work:

        icon = wx.EmptyIcon()
        icon.CopyFromBitmap(bmp)

More info can be found in the wxPython/demo/images.py script of the wxPythonSrc distribution.

--Mitch Chapman

A Better Way!

How to access a Win32 .exe's or .dll's icons from wxPython 2.6.1.0 The following information was discovered by looking at gdiimage.cpp in the wxPython source.

Supposing your py2exe setup.py file had the following icons:

"icon_resources": [ (1, "myicon1.ico"), (42, "myicon2.ico") ]

To programmatically determine the path of the currently running .exe, use:

import win32api
exeName = win32api.GetModuleFileName( win32api.GetModuleHandle(None) )

You can get the first icon (myicon1.ico) in the .exe with the following:

icon = wx.Icon( exeName, wx.BITMAP_TYPE_ICO )

You can also get the first icon (myicon1.ico) with a zero based index:

icon = wx.Icon( exeName + ";0", wx.BITMAP_TYPE_ICO )

Likewise, you can get the second icon (myicon2.ico) with:

icon = wx.Icon( exeName + ";1", wx.BITMAP_TYPE_ICO )

You can get an icon based on it's icon id, just specify the negated id number (myicon2.ico):

icon = wx.Icon( exeName + ";-42", wx.BITMAP_TYPE_ICO )

Because of the way it's implemented, you cannot specify ";-1", you will get incorrect operation or a crash.

If you wanted to set your window's title bar and task switch icons you would do the following in your wx.Frame _ _init_ _:

self.SetIcon(icon)

Here's an example program:

   1 import wx, win32api
   2 
   3 class MyFrame(wx.Frame):
   4     def __init__( self, parent=None ):
   5         wx.Frame.__init__( self, parent, wx.ID_ANY )
   6 
   7         # set window icon
   8         exeName = win32api.GetModuleFileName( win32api.GetModuleHandle(None) )
   9         icon = wx.Icon( exeName, wx.BITMAP_TYPE_ICO )
  10         self.SetIcon(icon)
  11 
  12 if __name__ == '__main__':
  13     app = wx.App( redirect=False )
  14     frame = MyFrame()
  15     frame.Show()
  16     app.MainLoop()

An Even Better Way!

Instead of using the win32api module to get the name of the executable, one can simply use sys.executable. e.g.

   1 import wx, sys
   2 
   3 class MyFrame( wx.Frame ) :
   4     def __init__( self, parent=None ) :
   5 
   6         wx.Frame.__init__( self, parent, wx.ID_ANY )
   7 
   8         # Set window title bar icon.
   9         if sys.platform == 'win32' :
  10             # Only do this on windows, so we don't
  11             #   cause an error dialog on all other platforms.
  12             exeName = sys.executable
  13             icon = wx.Icon( exeName, wx.BITMAP_TYPE_ICO )
  14             self.SetIcon( icon )
  15 
  16 if __name__ == '__main__' :
  17     app = wx.App( redirect=False )
  18     frame = MyFrame()
  19     frame.Show(True)
  20     app.MainLoop()

LoadIconFromWin32Resources (last edited 2011-03-28 23:24:43 by pool-71-244-98-82)

NOTE: To edit pages in this wiki you must be a member of the TrustedEditorsGroup.