How to Show Only Icons in QTableView Cells (Hide Text)

Use custom model roles to display icons without text in your PyQt6 tables
Heads up! You've already completed this tutorial.

When you're building a table-based interface with QTableView in PyQt6, you'll often want to show icons in certain cells — for example, a checkmark for True and a cross for False. But by default, Qt will also display the underlying data value as text alongside the icon. That means you end up with something like a green tick next to the word "True", which looks cluttered and unprofessional.

In this tutorial, we'll walk through how to display only icons in your QTableView cells, suppressing the text entirely. We'll build a complete working example so you can see exactly how it all fits together.

How Qt Models Serve Data

Before we jump into the solution, it helps to understand a little about how QTableView gets its data.

When a QTableView renders a cell, it asks the underlying model for data by calling the model's data() method. But it doesn't just ask once — it asks multiple times, each time with a different role. Roles tell the model what kind of data the view is looking for. The most common roles are:

  • Qt.DisplayRole — the text to show in the cell.
  • Qt.DecorationRole — an icon or color swatch to show in the cell.

When you return a value for Qt.DisplayRole, that value gets converted to a string and displayed as text. When you return a QIcon or QColor for Qt.DecorationRole, it appears as a small icon to the left of the text.

So if you're returning both a display value and a decoration for the same cell, you'll see both. The trick to showing only the icon is to return None for Qt.DisplayRole on the cells where you want to suppress the text.

A Basic Table Model with Icons

Let's start with a simple custom model that displays a numpy array of mixed data — some strings, some numbers, and some boolean values. We'll add icons for the boolean columns.

First, make sure you have PyQt6 and numpy installed:

bash
pip install pyqt6 numpy

Here's our starting point — a model that shows both icons and text for boolean values:

python
import sys
import numpy as np
from PyQt6.QtCore import Qt, QAbstractTableModel
from PyQt6.QtGui import QIcon
from PyQt6.QtWidgets import QApplication, QMainWindow, QTableView


class TableModel(QAbstractTableModel):
    def __init__(self, data):
        super().__init__()
        self._data = data

    def rowCount(self, parent=None):
        return self._data.shape[0]

    def columnCount(self, parent=None):
        return self._data.shape[1]

    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid():
            return None

        value = self._data[index.row(), index.column()]

        if role == Qt.DisplayRole:
            return str(value)

        if role == Qt.DecorationRole:
            if isinstance(value, bool):
                if value:
                    return QIcon("tick.png")
                return QIcon("cross.png")

        return None


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QTableView Icons Example")

        data = np.array([
            [True, "Alice", 32, False],
            [False, "Bob", 28, True],
            [True, "Charlie", 45, True],
            [False, "Diana", 36, False],
        ], dtype=object)

        self.table = QTableView()
        model = TableModel(data)
        self.table.setModel(model)
        self.setCentralWidget(self.table)
        self.resize(500, 300)


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

If you run this, you'll see that the boolean cells show an icon and the text "True" or "False". That's because Qt.DisplayRole is returning str(value) for every cell, including the boolean ones.

QTableView showing icons alongside boolean text values

You'll need tick.png and cross.png icon files in your project folder for the icons to appear. You can use any small icon images you like, or grab free ones from a site like icons8.com.

Suppressing the Text for Boolean Cells

The fix is straightforward. In the data() method, when the role is Qt.DisplayRole, we check whether the cell's value is a boolean. If it is, we return None instead of the string representation. Returning None tells Qt "there's nothing to display as text for this cell."

Update the Qt.DisplayRole section of your data() method like this:

python
def data(self, index, role=Qt.DisplayRole):
    if not index.isValid():
        return None

    value = self._data[index.row(), index.column()]

    if role == Qt.DisplayRole:
        if isinstance(value, bool):
            return None  # Don't show text for boolean cells
        return str(value)

    if role == Qt.DecorationRole:
        if isinstance(value, bool):
            if value:
                return QIcon("tick.png")
            return QIcon("cross.png")

    return None

That's it! The two lines that make the difference are:

python
if isinstance(value, bool):
    return None

By returning None for Qt.DisplayRole when the value is a boolean, we tell the view there's no text to render. The Qt.DecorationRole still returns the icon as before, so the icon appears on its own — clean and uncluttered.

The Complete Working Example

Here's the full code with the fix applied:

python
import sys
import numpy as np
from PyQt6.QtCore import Qt, QAbstractTableModel
from PyQt6.QtGui import QIcon
from PyQt6.QtWidgets import QApplication, QMainWindow, QTableView


class TableModel(QAbstractTableModel):
    def __init__(self, data):
        super().__init__()
        self._data = data

    def rowCount(self, parent=None):
        return self._data.shape[0]

    def columnCount(self, parent=None):
        return self._data.shape[1]

    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid():
            return None

        value = self._data[index.row(), index.column()]

        if role == Qt.DisplayRole:
            if isinstance(value, bool):
                return None  # Suppress text for boolean values
            return str(value)

        if role == Qt.DecorationRole:
            if isinstance(value, bool):
                if value:
                    return QIcon("tick.png")
                return QIcon("cross.png")

        return None


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QTableView Icons Example")

        data = np.array([
            [True, "Alice", 32, False],
            [False, "Bob", 28, True],
            [True, "Charlie", 45, True],
            [False, "Diana", 36, False],
        ], dtype=object)

        self.table = QTableView()
        model = TableModel(data)
        self.table.setModel(model)
        self.setCentralWidget(self.table)
        self.resize(500, 300)


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

When you run this, the boolean columns will show only the tick or cross icons, with no text beside them. The other columns (names and ages) continue to display their text values as normal.

Centering the Icons

You might notice that the icons sit to the left of the cell by default. If you'd like them centered, you can handle Qt.TextAlignmentRole in your data() method:

python
if role == Qt.TextAlignmentRole:
    if isinstance(value, bool):
        return Qt.AlignCenter

Add this block alongside the other role checks inside your data() method. This tells the view to center the content of boolean cells, which looks much tidier when the icon is the only thing in the cell.

Applying This Pattern to Other Data Types

The same approach works for any situation where you want to show an icon without text. The pattern is always the same:

  1. Return your icon from Qt.DecorationRole.
  2. Return None from Qt.DisplayRole for that cell.

For example, if you had a column showing status values like "active", "inactive", or "pending", you could map each to a colored icon and suppress the text:

python
if role == Qt.DisplayRole:
    if index.column() == 2:  # Status column
        return None
    return str(value)

if role == Qt.DecorationRole:
    if index.column() == 2:  # Status column
        status_icons = {
            "active": QIcon("green.png"),
            "inactive": QIcon("red.png"),
            "pending": QIcon("yellow.png"),
        }
        return status_icons.get(value)

You can use column index checks, value type checks, or any other logic to decide which cells should have their text suppressed.

Summary

Displaying only icons in QTableView cells comes down to understanding how Qt's model roles work. The view asks for display text and decoration separately, so you have full control over what appears. By returning None for Qt.DisplayRole on cells where you only want an icon, you get a clean, icon-only display while keeping the rest of your table's text intact.

This is a small technique, but it makes a real difference in how polished your table interfaces look — and it's a great example of why understanding the role system in Qt's model/view architecture is so valuable.

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

How to Show Only Icons in QTableView Cells (Hide Text) 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.