Fixing Missing Icons in PyInstaller-Packaged PyQt6 Applications on Windows

Why your app icon disappears after packaging and how to fix it
Heads up! You've already completed this tutorial.

I've packaged my PyQt application with PyInstaller, but the icon isn't showing up — both the executable icon and the running application icon are just the default Python/Windows icon. What's going on?

This is a surprisingly common issue when packaging PyQt6 apps with PyInstaller on Windows. The good news is that it usually comes down to one or two straightforward causes: Windows icon caching, and missing resource files in your packaged output. Let's walk through how to get your icons working properly.

Setting the executable icon with PyInstaller

When you run PyInstaller, you can set the icon for the .exe file itself using the --icon flag:

bash
pyinstaller --windowed --icon=myicon.ico myapp.py

This embeds the icon into the executable, so it shows up in File Explorer and on the desktop. The icon file needs to be in .ico format — .png or .svg won't work here.

After building, check the dist/ folder. Your .exe should display the custom icon. But sometimes... it doesn't.

Windows icon caching

Windows caches icons aggressively. If you've previously built your app (or even just run it) without a custom icon, Windows may continue to show the old default icon even after you've rebuilt with the correct one.

There are a few ways to deal with this:

  • Rename the executable. Even a small change to the filename forces Windows to look up the icon fresh. This is the quickest way to confirm that your icon is actually embedded correctly.
  • Clear the Windows icon cache. You can do this by restarting Windows Explorer or by deleting the icon cache files manually. Open a Command Prompt and run:
python
ie4uinit.exe -show

Or restart your computer. After clearing the cache, the correct icon should appear.

If your icon shows up after renaming the .exe, the icon was embedded correctly all along — it was just a caching issue.

Missing icon file at runtime

Setting the executable icon with --icon only affects what shows up in File Explorer. If your application also sets a window icon in code (using setWindowIcon), that icon file needs to be available at runtime too.

For example, if your code does this:

python
from PyQt6.QtWidgets import QApplication, QMainWindow
from PyQt6.QtGui import QIcon
import sys


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("My Application")


app = QApplication(sys.argv)
app.setWindowIcon(QIcon("myicon.ico"))

window = MainWindow()
window.show()

app.exec()

Then myicon.ico needs to exist in the working directory when the packaged app runs. By default, PyInstaller doesn't include data files like .ico images unless you tell it to.

You can add the icon file to your build using the --add-data flag:

bash
pyinstaller --windowed --icon=myicon.ico --add-data "myicon.ico;." myapp.py

On Linux or macOS, use : instead of ; as the separator:

bash
pyinstaller --windowed --icon=myicon.ico --add-data "myicon.ico:." myapp.py

This copies myicon.ico into the output directory alongside your executable (or into the temporary directory if you're using --onefile).

An alternative approach is to use the Qt Resource System to embed your icon directly into your application, which avoids the need to bundle separate icon files entirely.

Handling --onefile builds

When you use --onefile, PyInstaller extracts everything to a temporary folder at runtime. Your code needs to know how to find files relative to that temporary folder. You can handle this by detecting the base path:

python
import sys
import os

if getattr(sys, 'frozen', False):
    # Running as a PyInstaller bundle
    basedir = sys._MEIPASS
else:
    # Running as a normal script
    basedir = os.path.dirname(__file__)

Then use basedir when constructing file paths:

python
app.setWindowIcon(QIcon(os.path.join(basedir, "myicon.ico")))

Taskbar grouping with an Application User Model ID

On Windows, the taskbar groups windows by their application identity. Without an explicit identity, Windows guesses — and sometimes guesses wrong. This can cause your app to show the Python icon in the taskbar, or to group instances inconsistently depending on where they were launched from.

You can fix this by setting an Application User Model ID before creating your QApplication. This tells Windows exactly which application this is:

python
import ctypes

myappid = "com.mycompany.myapp.1.0"
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)

The string can be anything, but it's conventional to use a reverse-domain format. The value just needs to be unique to your application.

With an explicit app ID set, all instances of your app will group together in the taskbar regardless of where they were launched from — whether that's your IDE, the dist/ folder, or a --onefile build.

Complete working example

Here's a complete example that handles all of the above — the runtime base path, the window icon, and the application user model ID. If you're new to building PyQt6 applications, you may want to start with creating your first window before tackling packaging.

python
import sys
import os
import ctypes

from PyQt6.QtWidgets import QApplication, QMainWindow, QLabel
from PyQt6.QtGui import QIcon
from PyQt6.QtCore import Qt


# Set the app user model ID before creating QApplication (Windows only)
if sys.platform == "win32":
    myappid = "com.mycompany.myapp.1.0"
    ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)

# Determine the base directory for resource files
if getattr(sys, "frozen", False):
    basedir = sys._MEIPASS
else:
    basedir = os.path.dirname(__file__)


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("My Application")
        label = QLabel("Hello, world!")
        label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.setCentralWidget(label)


app = QApplication(sys.argv)
app.setWindowIcon(QIcon(os.path.join(basedir, "myicon.ico")))

window = MainWindow()
window.show()

app.exec()

To package this with PyInstaller:

bash
pyinstaller --windowed --icon=myicon.ico --add-data "myicon.ico;." myapp.py

If you're also looking to add images and icons throughout the rest of your application, see our guide on adding images to PyQt6 applications.

Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak

Packaging Python Applications with PyInstaller by Martin Fitzpatrick

This step-by-step guide walks you through packaging your own Python applications from simple examples to complete installers and signed executables.

More info Get the book

Martin Fitzpatrick

Fixing Missing Icons in PyInstaller-Packaged PyQt6 Applications on Windows was written by Martin Fitzpatrick.

Martin Fitzpatrick has been developing Python/Qt apps for 8 years. Building desktop applications to make data-analysis tools more user-friendly, Python was the obvious choice. Starting with Tk, later moving to wxWidgets and finally adopting PyQt. Martin founded PythonGUIs to provide easy to follow GUI programming tutorials to the Python community. He has written a number of popular Python books on the subject.