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:
pip install pyqt6 numpy
Here's our starting point — a model that shows both icons and text for boolean values:
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.

You'll need
tick.pngandcross.pngicon 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:
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:
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:
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:
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:
- Return your icon from
Qt.DecorationRole. - Return
NonefromQt.DisplayRolefor 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:
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.
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.