Nice Windows icons with wxPython

Sunday 30 January 2005This is almost 20 years old. Be careful.

I’ve been building a small GUI program with wxPython, and it’s mostly a straightforward experience (if you’re accustomed to the way GUIs tend to be built). wxGlade helps a lot with building the visible components of the UI. But I was going nuts trying to figure out why my Windows icons didn’t look right. I went to the trouble of reading a technote (Icons in Win32) to decide which sizes and depths of icon I needed to provide to get a nice looking taskbar icon and alt-tab icon. But my taskbar icon always came out smeared, clearly having been scaled down from a 32×32 image.

I was ready to start reverse-engineering Windows’ algorithm for picking the icon image, when I thankfully decided to look into the wxGlade-generated code first. Here’s what it gave me for setting the icon in my main frame:

_icon = wx.EmptyIcon()
_icon.CopyFromBitmap(wx.Bitmap("myapp.ico", wx.BITMAP_TYPE_ANY))
self.SetIcon(_icon)

This clearly seems to be picking a single image from the icon, then setting it as the icon. That defeats the whole idea of a multi-image icon. No wonder Windows seems to be scaling a large image for the taskbar. The .ico bitmap loader probably just picks the highest quality image it finds.

Digging deeper into the wxWidgets docs, I found wxIconBundle and wxTopLevelWindow.SetIcons, and came up with this instead:

ib = wx.IconBundle()
ib.AddIconFromFile("myapp.ico", wx.BITMAP_TYPE_ANY)
self.SetIcons(ib)

This works like a charm, giving me pixel-perfect icons for the taskbar and the alt-tab image. Bliss! I couldn’t find much discussion of this issue, or use of these methods in existing code, and I don’t know what it does on other platforms, but it works great on Windows.

Comments

[gravatar]
I too found out that my icon was blurry. I decided to use a 16x16 image instead and did this:
===
image = wxImage('img16x16.png', wxBITMAP_TYPE_PNG).ConvertToBitmap()
icon = wxEmptyIcon()
icon.CopyFromBitmap(image)
self.SetIcon(icon)
===
which is essentially the same as your first method, and I found that it gave me some sharp looking icon. The key, for me, was really to use a smaller image. Now, if only I knew how to draw properly :-)
[gravatar]
Awesome. That's just what I was looking for. I've been pulling out hairs trying to get that working!
[gravatar]
This is much better than creating a separate file for the smaller image. Thanks! However, when I add an icon this way for a wx.ProgressDialog, the icon overwrites the window title. Is there a way to correct this?
[gravatar]
I don't think ProgressDialogs usually have icons, do they? It's bad that wxPython lets you add the icon and then draws it badly, but I think you should not put the icon on it. No?
[gravatar]
Great tip. I found that loading an icon (wxWidgets 2.4.2) always picks the largest image in the .ico file. Do you know a way to use the 16x16 version (if available) given a .ico with multiple sizes. Otherwise, wx shows the largest image from the .ico file - visually looks like "zoomed in" (or cropped) (instead of scaled). (I set the image size to 16x16 using SetWidth and SetHeight methods).

Also, anyway to determined if a .ico is actually a properly encoded (uses the correct .ico file format) ? We have an offline blog editor
(Zoundry, http://forums.zoundry.com/viewtopic.php?t=108) and one of the things it does is download favicons from a blog or other sites (such as Delicious). We have come across a few cases where the .ico file is either corrupted or not in the correct .ico format. That is, some users have reported the "Image Handler Not Found" wx dialog popup on some icons.

Add a comment:

Ignore this:
Leave this empty:
Name is required. Either email or web are required. Email won't be displayed and I won't spam you. Your web site won't be indexed by search engines.
Don't put anything here:
Leave this empty:
Comment text is Markdown.