Icons Not Showing in PyQt6 or PySide6 Toolbar

Why your toolbar icons might be missing and how to fix the file path
Heads up! You've already completed this tutorial.

I'm adding an icon to a QAction on my toolbar using QIcon("bug.png"), and the image file is sitting right next to my Python script. But the icon doesn't show up in the GUI. What went wrong?

If you've ever added an icon to a toolbar action and been greeted by a blank space where your icon should be, you're not alone. This is one of the most common stumbling blocks when working with icons in PyQt6 or PySide6, and the fix is straightforward once you understand the cause.

The Problem: Relative File Paths

When you write something like this:

python
button_action = QAction(QIcon("bug.png"), "Your button", self)

Python looks for bug.png relative to the current working directory — the folder your script was launched from, not the folder where your script lives.

If you run your script from the terminal while sitting in the same directory as the script, everything works fine. But if you run it from an IDE like VS Code, PyCharm, or any other tool that may launch your script from a different directory, the working directory could be somewhere else entirely. Python can't find bug.png, so the icon silently fails to load and you see nothing.

There's no error message. Qt simply shows an empty icon when the image file can't be found. This makes the issue tricky to diagnose at first.

The Fix: Use an Absolute Path

The solution is to build an absolute path to the icon file based on the location of your Python script. Python provides the special variable __file__, which always holds the path to the currently running script. You can use that to construct a reliable path to any file sitting alongside your code.

There are two good ways to do this.

Using pathlib (recommended)

The pathlib module provides a clean, modern way to work with file paths:

python
from pathlib import Path

# Get the folder where this script lives
basedir = Path(__file__).resolve().parent

# Build the full path to the icon
icon_path = str(basedir / "bug.png")

Path(__file__).resolve().parent gives you the directory containing your script, regardless of where the script was launched from. The / operator joins path components together.

Using os.path

The os.path module is the older approach, but it works just as well:

python
import os

basedir = os.path.dirname(os.path.abspath(__file__))
icon_path = os.path.join(basedir, "bug.png")

os.path.abspath(__file__) converts the script's path to an absolute path, and os.path.dirname() strips off the filename to give you the containing directory.

Alternative: Change the Working Directory

Another option is to change the working directory to match your script's location at the start of your program:

python
import os

os.chdir(os.path.dirname(os.path.abspath(__file__)))

After this line, all relative paths in your script will resolve relative to the script's own directory. This can be convenient, but it's a global change that affects your entire program, so building absolute paths explicitly is generally a safer habit.

Complete Working Example

Here's a full example using PySide6 with the pathlib approach. If you're using PyQt6, the only changes are the import lines (shown in the comments). This example builds on concepts from the actions, toolbars and menus tutorial.

python
import sys
from pathlib import Path

from PyQt6.QtCore import QSize, Qt
from PyQt6.QtGui import QAction, QIcon
from PyQt6.QtWidgets import (
    QApplication,
    QLabel,
    QMainWindow,
    QStatusBar,
    QToolBar,
)

basedir = Path(__file__).resolve().parent


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

        label = QLabel("Hello!")
        label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.setCentralWidget(label)

        toolbar = QToolBar("My main toolbar")
        toolbar.setIconSize(QSize(16, 16))
        self.addToolBar(toolbar)

        # Use an absolute path for the icon
        icon_path = str(basedir / "bug.png")
        button_action = QAction(QIcon(icon_path), "Your button", self)
        button_action.setStatusTip("This is your button")
        button_action.triggered.connect(self.toolbar_button_clicked)
        button_action.setCheckable(True)
        toolbar.addAction(button_action)

        self.setStatusBar(QStatusBar(self))

    def toolbar_button_clicked(self, s):
        print("click", s)


app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
python
import sys
from pathlib import Path

from PySide6.QtCore import QSize, Qt
from PySide6.QtGui import QAction, QIcon
from PySide6.QtWidgets import (
    QApplication,
    QLabel,
    QMainWindow,
    QStatusBar,
    QToolBar,
)


basedir = Path(__file__).resolve().parent


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

        label = QLabel("Hello!")
        label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.setCentralWidget(label)

        toolbar = QToolBar("My main toolbar")
        toolbar.setIconSize(QSize(16, 16))
        self.addToolBar(toolbar)

        # Use an absolute path for the icon
        icon_path = str(basedir / "bug.png")
        button_action = QAction(QIcon(icon_path), "Your button", self)
        button_action.setStatusTip("This is your button")
        button_action.triggered.connect(self.toolbar_button_clicked)
        button_action.setCheckable(True)
        toolbar.addAction(button_action)

        self.setStatusBar(QStatusBar(self))

    def toolbar_button_clicked(self, s):
        print("click", s)


app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()

Make sure you have a file called bug.png in the same folder as this script. You can use any small PNG image to test.

Why This Happens in IDEs

Most IDEs set the working directory to the project root folder, not the folder containing the specific file you're running. For example, if your project looks like this:

python
my_project/
├── icons_example/
│   ├── toolbars_and_menus.py
│   └── bug.png
└── other_stuff/

And your IDE sets the working directory to my_project/, then QIcon("bug.png") will look for my_project/bug.png — which doesn't exist. The file is actually at my_project/icons_example/bug.png.

Using __file__ to build an absolute path avoids this entirely. Your code will work the same way whether you run it from the terminal, from an IDE, or from a shortcut.

A Good Habit for All Your Projects

Any time you load a file in your Qt application — icons, images, stylesheets, data files — use an absolute path built from __file__. Defining a basedir variable at the top of your script (or in a shared module) makes this easy:

python
from pathlib import Path

basedir = Path(__file__).resolve().parent

Then throughout your code, reference files like this:

python
QIcon(str(basedir / "icons" / "bug.png"))

This pattern keeps your paths reliable and your icons visible, no matter how or where your script gets launched. For more complex applications, you may also want to consider using the Qt Resource System to bundle images and other assets directly into your application.

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

Bring Your PyQt/PySide Application to Market

Stuck in development hell? I'll help you get your project focused, finished and released. Benefit from years of practical experience releasing software with Python.

Find out More

Martin Fitzpatrick

Icons Not Showing in PyQt6 or PySide6 Toolbar 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.