How to Search a QTableWidget and Highlight Results in PyQt6

Use findItems() to search through your table data and jump to matching results
Heads up! You've already completed this tutorial.

If you've built a QTableWidget and populated it with data — maybe from a database — you'll probably want to let users search through it. PyQt6 makes this straightforward with the built-in .findItems() method, which searches the table for matching text and returns the results as a list of items you can then highlight or select.

In this tutorial, we'll build a simple searchable table. You'll learn how to wire up a QLineEdit as a search box, use .findItems() to locate matches, and highlight the results in the table.

Let's start with a complete working example. We'll create a QTableWidget filled with random text, and a QLineEdit at the top that searches the table as you type.

python
from PyQt6.QtWidgets import (
    QTableWidget, QLineEdit, QApplication,
    QMainWindow, QVBoxLayout, QWidget, QTableWidgetItem
)
from PyQt6.QtCore import Qt

import random
import string


class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()

        self.query = QLineEdit()
        self.query.setPlaceholderText("Search...")
        self.query.textChanged.connect(self.search)

        n_rows = 50
        n_cols = 4

        self.table = QTableWidget()
        self.table.setRowCount(n_rows)
        self.table.setColumnCount(n_cols)

        for c in range(0, n_cols):
            for r in range(0, n_rows):
                s = ''.join(random.choice(string.ascii_lowercase) for n in range(10))
                i = QTableWidgetItem(s)
                self.table.setItem(c, r, i)

        layout = QVBoxLayout()
        layout.addWidget(self.query)
        layout.addWidget(self.table)

        w = QWidget()
        w.setLayout(layout)
        self.setCentralWidget(w)

    def search(self, s):
        items = self.table.findItems(s, Qt.MatchContains)
        if items:  # we have found something
            item = items[0]  # take the first
            self.table.setCurrentItem(item)


app = QApplication([])
w = MainWindow()
w.show()
app.exec()

Run this, and you'll see a table full of random strings with a search box at the top. Start typing in the search box, and the table will jump to and highlight the first matching cell.

QTableWidget with search results highlighted

Now let's walk through how this works, piece by piece.

How findItems() works

The .findItems() method is the heart of the search. It takes two arguments:

  1. The search text — a plain string to look for.
  2. Match flags — a Qt.MatchFlags value that controls how the search is performed.
python
items = self.table.findItems(s, Qt.MatchContains)

The method returns a Python list of QTableWidgetItem objects — these are the actual data items in the table that matched your search. If nothing matched, you get an empty list.

Match flag options

The match flags determine the behavior of the search. Here are the most useful options:

Flag Description
Qt.MatchContains The search text appears anywhere in the item
Qt.MatchStartsWith The item starts with the search text
Qt.MatchExactly The item matches the search text exactly
Qt.MatchEndsWith The item ends with the search text
Qt.MatchCaseSensitive Makes the search case-sensitive

You can combine flags together using the | (bitwise OR) operator. For example, to search for items that start with a string and are case-sensitive:

python
items = self.table.findItems(s, Qt.MatchStartsWith | Qt.MatchCaseSensitive)

By default, searches are case-insensitive, which is usually what you want for a user-facing search box.

Selecting a matching item

Once you have a list of matching items, you can tell the table to jump to one of them using .setCurrentItem():

python
if items:
    item = items[0]  # take the first match
    self.table.setCurrentItem(item)

This selects and highlights the cell in the table, scrolling to it if necessary. We check if items first to make sure we actually got results — calling .setCurrentItem() with nothing would cause problems.

Stepping through multiple results

In the basic example above, we only ever select the first match. But .findItems() can return many matches. To let users step through all of them, you can track the current position in the results list and move forward each time.

Here's an updated version that adds "Next" and "Previous" buttons:

python
from PyQt6.QtWidgets import (
    QTableWidget, QLineEdit, QPushButton, QApplication,
    QMainWindow, QVBoxLayout, QHBoxLayout, QWidget,
    QTableWidgetItem, QLabel
)
from PyQt6.QtCore import Qt

import random
import string


class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()

        self.search_results = []
        self.current_index = 0

        self.query = QLineEdit()
        self.query.setPlaceholderText("Search...")
        self.query.textChanged.connect(self.search)

        self.prev_button = QPushButton("Previous")
        self.prev_button.clicked.connect(self.previous_result)

        self.next_button = QPushButton("Next")
        self.next_button.clicked.connect(self.next_result)

        self.result_label = QLabel("")

        n_rows = 50
        n_cols = 4

        self.table = QTableWidget()
        self.table.setRowCount(n_rows)
        self.table.setColumnCount(n_cols)

        for c in range(0, n_cols):
            for r in range(0, n_rows):
                s = ''.join(random.choice(string.ascii_lowercase) for n in range(10))
                i = QTableWidgetItem(s)
                self.table.setItem(c, r, i)

        search_layout = QHBoxLayout()
        search_layout.addWidget(self.query)
        search_layout.addWidget(self.prev_button)
        search_layout.addWidget(self.next_button)
        search_layout.addWidget(self.result_label)

        layout = QVBoxLayout()
        layout.addLayout(search_layout)
        layout.addWidget(self.table)

        w = QWidget()
        w.setLayout(layout)
        self.setCentralWidget(w)

    def search(self, s):
        self.search_results = self.table.findItems(s, Qt.MatchContains)
        self.current_index = 0
        self.highlight_current()

    def highlight_current(self):
        if self.search_results:
            item = self.search_results[self.current_index]
            self.table.setCurrentItem(item)
            self.result_label.setText(
                f"{self.current_index + 1} of {len(self.search_results)}"
            )
        else:
            self.result_label.setText("No results")

    def next_result(self):
        if self.search_results:
            self.current_index = (self.current_index + 1) % len(self.search_results)
            self.highlight_current()

    def previous_result(self):
        if self.search_results:
            self.current_index = (self.current_index - 1) % len(self.search_results)
            self.highlight_current()


app = QApplication([])
w = MainWindow()
w.show()
app.exec()

Now when you type a search term, you'll see "1 of N" next to the search box, telling you how many matches were found. Clicking Next and Previous cycles through all the matches, jumping to each one in the table.

The % (modulo) operator in next_result and previous_result makes the navigation wrap around — when you reach the last match and click Next, it loops back to the first one.

A note about QTableWidget vs. QTableView

This approach works specifically with QTableWidget, which stores its data internally using QTableWidgetItem objects. If you're using a QTableView with a custom model (like QSqlTableModel or your own QAbstractTableModel), the .findItems() method isn't available. In that case, you'd need to search through the model's data yourself, or use QSortFilterProxyModel to filter rows based on search criteria.

For most cases where you're manually populating a table with data from a database, QTableWidget with .findItems() is the simplest path to a working search feature.

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

Create GUI Applications with Python & Qt6 by Martin Fitzpatrick

(PyQt6 Edition) The hands-on guide to making apps with Python — Over 15,000 copies sold!

More info Get the book

Martin Fitzpatrick

How to Search a QTableWidget and Highlight Results in PyQt6 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.