As mentioned above, I didn't manage to encapsulate desired icon files into my executable to later access them with relative paths from my script. However there seems to be a way around as PyInstaller has no issues in attaching icon to the executable file itself. Afterwards I just read and decode icon from the executable file. Thanks to this post: How to extract 32x32 icon bitmap data from EXE and convert it into a PIL Image object?
My final script looks next:
import sys
import win32api
import win32con
import win32gui
import win32ui
from PySide6.QtCore import Qt
from PySide6.QtGui import QImage, QPixmap
from PySide6.QtWidgets import QApplication, QMainWindow, QLabel
def extract_icon_from_exe(exe_path):
"""Extracts the icon from an executable and converts it to a QPixmap with transparency."""
# Get system icon size
ico_x = win32api.GetSystemMetrics(win32con.SM_CXICON)
ico_y = win32api.GetSystemMetrics(win32con.SM_CYICON)
# Extract the large icon from the executable
large, small = win32gui.ExtractIconEx(exe_path, 0)
if not large:
raise RuntimeError("Failed to extract icon.")
hicon = large[0] # Handle to the large icon
# Create a compatible device context (DC) and bitmap
hdc = win32ui.CreateDCFromHandle(win32gui.GetDC(0))
mem_dc = hdc.CreateCompatibleDC()
hbmp = win32ui.CreateBitmap()
hbmp.CreateCompatibleBitmap(hdc, ico_x, ico_y)
mem_dc.SelectObject(hbmp)
# Draw the icon onto the bitmap
mem_dc.DrawIcon((0, 0), hicon)
# Retrieve the bitmap info and bits
bmpinfo = hbmp.GetInfo()
bmpstr = hbmp.GetBitmapBits(True)
# Convert to a QImage with transparency (ARGB format)
image = QImage(bmpstr, bmpinfo["bmWidth"], bmpinfo["bmHeight"], QImage.Format_ARGB32)
# Clean up resources
win32gui.DestroyIcon(hicon)
mem_dc.DeleteDC()
hdc.DeleteDC()
return QPixmap.fromImage(image)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Hello World Application")
label = QLabel("Hello, World!", self)
label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.setWindowIcon(extract_icon_from_exe(sys.executable))
self.setCentralWidget(label)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.resize(400, 300)
window.show()
sys.exit(app.exec())
TestApp.spec:
a = Analysis(
['test.py'],
pathex=[],
binaries=[],
datas=[('my_u2net', 'my_u2net')],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='TestApp',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=['app_icon.ico'],
)