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:
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:
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:
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:
pyinstaller --windowed --icon=myicon.ico --add-data "myicon.ico;." myapp.py
On Linux or macOS, use : instead of ; as the separator:
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:
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:
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:
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.
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:
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.
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.