wxWidgets / Phoenix

wxPython's Project Phoenix. A new implementation of wxPython, better, stronger, faster than he was before.
http://wxpython.org/
2.29k stars 515 forks source link

Request: Overload for wx.Icon #1624

Open Metallicow opened 4 years ago

Metallicow commented 4 years ago

Description of the problem: wx.Icon only has so many overloads.

Icon()
Icon(icon)
Icon(name, type=BITMAP_TYPE_ANY, desiredWidth=-1, desiredHeight=-1)
Icon(loc)
Icon(bmp)

Basically I need something like this...

Icon(loc, desiredSize=32)
...or
wx.IconLocation(filename, desiredSize=wx.Size(32, 32))

The Problem If a wx.IconLocation is feed to a wx.Icon, then the largest size returned is 32x32 usually. By using pywin32 I have worked around this, but IconLocation should work like desired size when requested.

As far as it seems 32x32 is the biggest icon extracted from... Example:

path = r'C:\Program Files\Mozilla Firefox\firefox.exe'
wx.IconLocation(path)

OBVIOUSLY the icon in the *.exe has sizes to accommodate like 256. I think 512-1024 is highest sizes used sometimes. Anyhow... I need the highest/best resolution in some cases. Would prefer to not use extra pywin32 code I've wrote, but to have it built in. @RobinD42 Is this possible as a overload?....

Metallicow commented 4 years ago

Reference SampleApp.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
https://stackoverflow.com/questions/23263599/how-to-extract-128x128-icon-bitmap-data-from-exe-in-python

How to get biggest Icon from *.exe since wx.IconLocation seems to be limited to 16 and 32 sizes.
"""

# python Imports.
from __future__ import unicode_literals

import ctypes
import ctypes.util

# pywin32 Imports.
import win32con
import win32api
# import win32gui

USER32 = ctypes.windll.user32
KERNEL32 = ctypes.windll.kernel32

def GetWindowsExeIcon(path):
    """
    Get a *.exe icon.

    :return: HICON
    """
    # memcpy is used to copy data from resource storage to our buffer.
    libc = ctypes.CDLL(ctypes.util.find_library('c'))
    libc.memcpy.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t]
    libc.memcpy.restype = ctypes.c_char_p

    # Patch FindResourceW, ctypes.windll.kernel32.SizeofResource
    FindResourceW = KERNEL32.FindResourceW
    FindResourceW.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
    FindResourceW.restype = ctypes.c_void_p
    SizeofResource = KERNEL32.SizeofResource
    SizeofResource.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
    SizeofResource.restype = ctypes.c_size_t

    # Using LoadLibrary (rather than CreateFile) is required otherwise
    # LoadResource, FindResource and others will fail.
    hLib = win32api.LoadLibraryEx(path, 0, 2)

    # Get the icon groups, default is the first group.
    iconGroups = win32api.EnumResourceNames(hLib, win32con.RT_GROUP_ICON)
    groupName = iconGroups[0]
    print(groupName)
    hRes = win32api.LoadResource(hLib, win32con.RT_GROUP_ICON, groupName)
    mem_icon_dir = KERNEL32.LockResource(hRes)

    # We want 32 bit color icons, not 16 or 256 color.
    # Common icon sizes...
    # iconSizes = (16, 24, 32, 48, 64, 96, 128, 256, 512, 1024)
    iconSizes = (1024,)  # Hopefully return the biggest icon....
    for iconSize in iconSizes:
        iconName = USER32.LookupIconIdFromDirectoryEx(mem_icon_dir, True, iconSize, iconSize, 0x00000000);
        hResourceInfo = FindResourceW(hLib, iconName, win32con.RT_ICON)
        size = KERNEL32.SizeofResource(hLib, hResourceInfo)
        rec = win32api.LoadResource(hLib, win32con.RT_ICON, iconName)
        mem_icon = KERNEL32.LockResource(rec)

        # Get and return the icon data.
        binary_data = (ctypes.c_ubyte * size)()
        libc.memcpy(binary_data, mem_icon, size)
        hIcon = USER32.CreateIconFromResourceEx(binary_data, size, True, 0x00030000, 0, 0, 0x00000000)
        print('hIcon', hIcon)
        ## info = win32gui.GetIconInfo(hIcon)
        ## print('info', info)
        ## bminfo = win32gui.GetObject(info[4])
        ## print('bminfo', bminfo)

        return hIcon

if __name__ == '__main__':
    import sys
    import wx

    class MyFrame(wx.Frame):
        def __init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString,
                     pos=wx.DefaultPosition, size=wx.DefaultSize,
                     style=wx.DEFAULT_FRAME_STYLE, name='frame'):
            wx.Frame.__init__(self, parent, id, title, pos, size, style, name)
            global gMainWin
            gMainWin = self
            wxVER = 'wxPython %s' % wx.version()
            pyVER = 'python %d.%d.%d.%s' % sys.version_info[0:4]
            versionInfos = '%s %s' % (wxVER, pyVER)
            self.CreateStatusBar().SetStatusText(versionInfos)

            # path = r'C:\Program Files\Internet Explorer\iexplore.exe'
            path = r'C:\Program Files\Mozilla Firefox\firefox.exe'
            hIcon = GetWindowsExeIcon(path)
            icon = wx.Icon()
            icon.CreateFromHICON(hIcon)
            print(icon.Width)

            bmp = wx.Bitmap()
            bmp.CopyFromIcon(icon)
            print(bmp.GetSize())
            bmp = bmp.ConvertToImage().Rescale(128, 128).ConvertToBitmap()
            print(bmp.GetSize())

            sb = wx.StaticBitmap(self, -1, bmp)

            self.SetIcon(icon)
            self.Bind(wx.EVT_CLOSE, self.OnDestroy)

        def OnDestroy(self, event):
            self.Destroy()

    app = wx.App(0)
    frame = MyFrame(None)
    frame.Show()
    app.MainLoop()