<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Python GUIs - filtering</title><link href="https://www.pythonguis.com/" rel="alternate"/><link href="https://www.pythonguis.com/feeds/filtering.tag.atom.xml" rel="self"/><id>https://www.pythonguis.com/</id><updated>2021-02-11T09:00:00+00:00</updated><subtitle>Create GUI applications with Python and Qt</subtitle><entry><title>How to Create a Filter/Search Bar for a QTableWidget in PyQt5 — Use QSortFilterProxyModel to add live filtering to your tables</title><link href="https://www.pythonguis.com/faq/how-to-create-a-filter-search-bar-for-a-qtablewidget/" rel="alternate"/><published>2021-02-11T09:00:00+00:00</published><updated>2021-02-11T09:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.pythonguis.com,2021-02-11:/faq/how-to-create-a-filter-search-bar-for-a-qtablewidget/</id><summary type="html">If you're displaying tabular data in PyQt5, you'll probably want to let users search or filter that data at some point. Maybe you're pulling records from an SQLite database and want to add a search bar above the table so users can quickly narrow things down.</summary><content type="html">
            &lt;p&gt;If you're displaying tabular data in PyQt5, you'll probably want to let users search or filter that data at some point. Maybe you're pulling records from an SQLite database and want to add a search bar above the table so users can quickly narrow things down.&lt;/p&gt;
&lt;p&gt;The good news is that Qt provides a built-in tool for exactly this: &lt;code&gt;QSortFilterProxyModel&lt;/code&gt;. Despite the long name, it's straightforward to use &amp;mdash; and yes, it works perfectly in PyQt5, not just C++.&lt;/p&gt;
&lt;p&gt;In this tutorial, we'll build a simple table with a search bar that filters rows in real time as the user types. We'll use a custom table model backed by a Python list, but the same approach works with any Qt model, including models connected to a database.&lt;/p&gt;
&lt;h2 id="what-is-qsortfilterproxymodel"&gt;What is QSortFilterProxyModel?&lt;/h2&gt;
&lt;p&gt;When you display data in a &lt;code&gt;QTableView&lt;/code&gt;, you connect it to a &lt;em&gt;model&lt;/em&gt; &amp;mdash; an object that provides the data. A &lt;code&gt;QSortFilterProxyModel&lt;/code&gt; sits between your data model and the view. It acts as a middleman that can sort and filter the data before it reaches the table.&lt;/p&gt;
&lt;p&gt;The flow looks like this:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;Data Model  &amp;rarr;  QSortFilterProxyModel  &amp;rarr;  QTableView
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The proxy model doesn't change your original data. It just controls which rows (and in what order) are shown in the view. This makes it a clean and efficient way to add filtering. If you're not yet familiar with how Qt's Model/View architecture works, take a look at our &lt;a href="https://www.pythonguis.com/tutorials/modelview-architecture/"&gt;Model View Architecture tutorial&lt;/a&gt; for a thorough introduction.&lt;/p&gt;
&lt;h2 id="setting-up-the-table-model"&gt;Setting Up the Table Model&lt;/h2&gt;
&lt;p&gt;Before we add filtering, we need a table with some data. We'll create a simple custom model by subclassing &lt;code&gt;QAbstractTableModel&lt;/code&gt;. This model stores data as a nested Python list, where each inner list represents a row.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt5.QtCore import Qt, QAbstractTableModel


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

    def data(self, index, role):
        if role == Qt.DisplayRole:
            return self._data[index.row()][index.column()]

    def rowCount(self, index):
        return len(self._data)

    def columnCount(self, index):
        return len(self._data[0])
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This is about as minimal as a table model gets. The &lt;code&gt;data()&lt;/code&gt; method returns the value for each cell, and &lt;code&gt;rowCount()&lt;/code&gt; and &lt;code&gt;columnCount()&lt;/code&gt; tell the view how big the table is.&lt;/p&gt;
&lt;p&gt;If you're loading data from SQLite, you could populate the &lt;code&gt;data&lt;/code&gt; list from your query results and pass it into this model the same way.&lt;/p&gt;
&lt;h2 id="adding-the-proxy-model-and-search-bar"&gt;Adding the Proxy Model and Search Bar&lt;/h2&gt;
&lt;p&gt;Now let's put it all together. We'll create a &lt;code&gt;QMainWindow&lt;/code&gt; with a &lt;code&gt;QLineEdit&lt;/code&gt; (the search bar) at the top, and a &lt;code&gt;QTableView&lt;/code&gt; below it. The proxy model connects the two &amp;mdash; whenever the text in the search bar changes, the proxy model updates its filter and the table shows only matching rows.&lt;/p&gt;
&lt;p&gt;Here's the complete working example:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys
from PyQt5.QtWidgets import (
    QApplication, QWidget, QTableView,
    QMainWindow, QVBoxLayout, QLineEdit,
)
from PyQt5.QtCore import Qt, QSortFilterProxyModel, QAbstractTableModel


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

    def data(self, index, role):
        if role == Qt.DisplayRole:
            return self._data[index.row()][index.column()]

    def rowCount(self, index):
        return len(self._data)

    def columnCount(self, index):
        return len(self._data[0])


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.table = QTableView()

        data = [
            [4, 9, 2],
            [1, "hello", 0],
            [3, 5, 0],
            [3, 3, "what"],
            ["this", 8, 9],
        ]

        self.model = TableModel(data)
        self.proxy_model = QSortFilterProxyModel()
        self.proxy_model.setFilterKeyColumn(-1)  # Search all columns.
        self.proxy_model.setSourceModel(self.model)

        self.proxy_model.sort(0, Qt.AscendingOrder)

        self.table.setModel(self.proxy_model)

        self.searchbar = QLineEdit()
        self.searchbar.setPlaceholderText("Type to filter...")

        self.searchbar.textChanged.connect(
            self.proxy_model.setFilterFixedString
        )

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

        container = QWidget()
        container.setLayout(layout)

        self.setCentralWidget(container)


app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Run this and you'll see a table with five rows. Start typing in the search bar and the table updates instantly, showing only the rows that contain matching text.&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyQt5 QTableView with a search bar filtering rows using QSortFilterProxyModel" src="images/pyqt5-qsortfilterproxymodel-search-bar.png"/&gt;&lt;/p&gt;
&lt;h2 id="how-it-works"&gt;How It Works&lt;/h2&gt;
&lt;p&gt;Let's walk through the important parts.&lt;/p&gt;
&lt;h3&gt;Creating the proxy model&lt;/h3&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.proxy_model = QSortFilterProxyModel()
self.proxy_model.setFilterKeyColumn(-1)
self.proxy_model.setSourceModel(self.model)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We create a &lt;code&gt;QSortFilterProxyModel&lt;/code&gt; and tell it which model to wrap using &lt;code&gt;setSourceModel()&lt;/code&gt;. The call to &lt;code&gt;setFilterKeyColumn(-1)&lt;/code&gt; tells the proxy to search &lt;strong&gt;all columns&lt;/strong&gt; when filtering. If you only wanted to filter based on a specific column, you could pass that column's index instead (e.g., &lt;code&gt;0&lt;/code&gt; for the first column, &lt;code&gt;1&lt;/code&gt; for the second).&lt;/p&gt;
&lt;h3&gt;Connecting the view to the proxy&lt;/h3&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.table.setModel(self.proxy_model)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Notice that we set the table's model to the &lt;em&gt;proxy model&lt;/em&gt;, not our original &lt;code&gt;TableModel&lt;/code&gt;. The view talks to the proxy, and the proxy talks to your real data model behind the scenes.&lt;/p&gt;
&lt;h3&gt;Wiring up the search bar&lt;/h3&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.searchbar.textChanged.connect(
    self.proxy_model.setFilterFixedString
)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This single line does all the work. Every time the text in the &lt;code&gt;QLineEdit&lt;/code&gt; changes, the &lt;code&gt;textChanged&lt;/code&gt; signal fires and passes the current text to &lt;code&gt;setFilterFixedString()&lt;/code&gt; on the proxy model. The proxy then hides any rows that don't contain that text, and the table view updates automatically. For more on how signals and slots work in PyQt5, see our &lt;a href="https://www.pythonguis.com/tutorials/pyqt-signals-slots-events/"&gt;Signals, Slots &amp;amp; Events guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="choosing-a-filter-mode"&gt;Choosing a Filter Mode&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;QSortFilterProxyModel&lt;/code&gt; supports several different ways to filter. We used &lt;code&gt;setFilterFixedString&lt;/code&gt; above, which does a simple substring match. But you have other options depending on what you need:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Slot&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;setFilterFixedString&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Matches rows containing the exact string (case-insensitive by default)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;setFilterWildcard&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Supports &lt;code&gt;*&lt;/code&gt; and &lt;code&gt;?&lt;/code&gt; wildcard characters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;setFilterRegExp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full regular expression matching&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;To switch modes, just connect the &lt;code&gt;textChanged&lt;/code&gt; signal to a different slot:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Use wildcard matching instead
self.searchbar.textChanged.connect(
    self.proxy_model.setFilterWildcard
)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;For most search bar use cases, &lt;code&gt;setFilterFixedString&lt;/code&gt; is exactly what you want. Wildcard and regex matching are useful if you need to give users more control over their searches.&lt;/p&gt;
&lt;h2 id="controlling-case-sensitivity"&gt;Controlling Case Sensitivity&lt;/h2&gt;
&lt;p&gt;By default, the filter is case-insensitive &amp;mdash; typing "hello" will match "Hello", "HELLO", and so on. If you want case-sensitive matching, you can set it on the proxy model:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.proxy_model.setFilterCaseSensitivity(Qt.CaseSensitive)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;For a user-facing search bar, case-insensitive matching (the default) is usually the better experience.&lt;/p&gt;
&lt;h2 id="using-this-with-sqlite-data"&gt;Using This with SQLite Data&lt;/h2&gt;
&lt;p&gt;If you're working with data from an SQLite database, the approach is the same. You just need to get your data into a format your model understands. For example, you could fetch your rows and pass them in as a list of lists:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sqlite3

connection = sqlite3.connect("my_database.db")
cursor = connection.cursor()
cursor.execute("SELECT * FROM my_table")
data = cursor.fetchall()  # Returns a list of tuples.

# Convert tuples to lists (optional, but consistent with our model).
data = [list(row) for row in data]

model = TableModel(data)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;From here, the proxy model and search bar work exactly the same way. The filtering all happens at the Qt level, so it doesn't matter where your data originally came from. If you're working with NumPy or Pandas data instead, see our guide on &lt;a href="https://www.pythonguis.com/tutorials/qtableview-modelviews-numpy-pandas/"&gt;displaying tabular data with QTableView&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;Adding a search/filter bar to a table in PyQt5 comes down to three things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Create a &lt;code&gt;QSortFilterProxyModel&lt;/code&gt;&lt;/strong&gt; and set your data model as its source.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set the table view's model&lt;/strong&gt; to the proxy model (not the original).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connect a &lt;code&gt;QLineEdit&lt;/code&gt;'s &lt;code&gt;textChanged&lt;/code&gt; signal&lt;/strong&gt; to one of the proxy model's filter slots.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The &lt;code&gt;QSortFilterProxyModel&lt;/code&gt; handles all the heavy lifting &amp;mdash; filtering rows, updating the view, and keeping your original data untouched. Once you've seen the pattern, you can drop it into any project that uses a &lt;code&gt;QTableView&lt;/code&gt;. You can also use the same proxy model for &lt;a href="https://www.pythonguis.com/tutorials/pyqt5-modelview-sort-filter-tables/"&gt;sorting and filtering with more advanced controls&lt;/a&gt;.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PyQt5 see my book, &lt;a href="https://www.martinfitzpatrick.com/pyqt5-book/"&gt;Create GUI Applications with Python &amp; Qt5.&lt;/a&gt;&lt;/p&gt;
            </content><category term="pyqt5"/><category term="pyqt"/><category term="qtableview"/><category term="filtering"/><category term="search"/><category term="python"/><category term="qt"/><category term="qt5"/></entry></feed>