Disclaimer: In this post I am going to focus only on the background objective and not on the errors of the OP (for example combining a QStandardItemModel with QTableWidget, or the useless attempt to use QSortFilterProxyModel that does not connect to anything).
I'm going to take the opportunity to show different ways of how to filter the sqlite tables (the same concepts can be applied to other DBs) using PyQt5 (it also applies to PySide2).
- QTableWidget + sqlite3 module
A possible solution is to iterate over the rows and verify that it meets the desired condition, if it meets then the row will be visible and otherwise the row will be hidden.
import sqlite3
from PyQt5 import QtCore, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.edit = QtWidgets.QLineEdit()
self.combo = QtWidgets.QComboBox()
self.table = QtWidgets.QTableWidget()
grid = QtWidgets.QGridLayout(self)
grid.addWidget(self.edit, 0, 0)
grid.addWidget(self.combo, 0, 1)
grid.addWidget(self.table, 1, 0, 1, 2)
self.connection = sqlite3.connect("database.db")
self.populate_table("SELECT * FROM foo_table")
self.edit.textChanged.connect(self.filter_table)
def populate_table(self, query, values=None):
cursor = self.connection.cursor()
if values is None:
cursor.execute(query)
else:
cursor.execute(query, values)
name_of_columns = [e[0] for e in cursor.description]
self.table.setColumnCount(len(name_of_columns))
self.table.setRowCount(0)
self.table.setHorizontalHeaderLabels(name_of_columns)
self.combo.clear()
self.combo.addItems(name_of_columns)
for i, row_data in enumerate(cursor.fetchall()):
self.table.insertRow(self.table.rowCount())
for j, value in enumerate(row_data):
item = QtWidgets.QTableWidgetItem()
item.setData(QtCore.Qt.DisplayRole, value)
self.table.setItem(i, j, item)
def filter_table(self, text):
if text:
filter_column = self.combo.currentIndex()
for i in range(self.table.rowCount()):
item = self.table.item(i, filter_column)
if self.filter_row(item, text):
self.table.showRow(i)
else:
self.table.hideRow(i)
else:
for i in range(self.table.rowCount()):
self.table.showRow(i)
def filter_row(self, item, text):
return text in item.text()
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
ex = Widget()
ex.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Another possible solution is to delete all the data and make a new request with the filter.
import sqlite3
from PyQt5 import QtCore, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.edit = QtWidgets.QLineEdit()
self.combo = QtWidgets.QComboBox()
self.table = QtWidgets.QTableWidget()
grid = QtWidgets.QGridLayout(self)
grid.addWidget(self.edit, 0, 0)
grid.addWidget(self.combo, 0, 1)
grid.addWidget(self.table, 1, 0, 1, 2)
self.connection = sqlite3.connect("database.db")
self.populate_table("SELECT * FROM foo_table")
self.edit.textChanged.connect(self.filter_table)
def populate_table(self, query, values=None):
cursor = self.connection.cursor()
if values is None:
cursor.execute(query)
else:
cursor.execute(query, values)
name_of_columns = [e[0] for e in cursor.description]
self.table.setColumnCount(len(name_of_columns))
self.table.setRowCount(0)
self.table.setHorizontalHeaderLabels(name_of_columns)
self.combo.clear()
self.combo.addItems(name_of_columns)
for i, row_data in enumerate(cursor.fetchall()):
self.table.insertRow(self.table.rowCount())
for j, value in enumerate(row_data):
item = QtWidgets.QTableWidgetItem()
item.setData(QtCore.Qt.DisplayRole, value)
self.table.setItem(i, j, item)
def filter_table(self, text):
if text:
self.populate_table(
"SELECT * FROM foo_table WHERE {} LIKE ?".format(
self.combo.currentText()
),
["%{}%".format(text)],
)
else:
self.populate_table("SELECT * FROM foo_table")
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
ex = Widget()
ex.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
- QTableView + QStandardItemModel + sqlite3 module
The same logic as above:
import sqlite3
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.edit = QtWidgets.QLineEdit()
self.combo = QtWidgets.QComboBox()
self.table = QtWidgets.QTableView()
self.model = QtGui.QStandardItemModel()
self.table.setModel(self.model)
grid = QtWidgets.QGridLayout(self)
grid.addWidget(self.edit, 0, 0)
grid.addWidget(self.combo, 0, 1)
grid.addWidget(self.table, 1, 0, 1, 2)
self.connection = sqlite3.connect("database.db")
self.populate_table("SELECT * FROM foo_table")
self.edit.textChanged.connect(self.filter_table)
def populate_table(self, query):
cursor = self.connection.cursor()
cursor.execute(query)
name_of_columns = [e[0] for e in cursor.description]
self.model.setColumnCount(len(name_of_columns))
self.model.setRowCount(0)
self.model.setHorizontalHeaderLabels(name_of_columns)
self.combo.clear()
self.combo.addItems(name_of_columns)
for i, row_data in enumerate(cursor.fetchall()):
self.model.insertRow(self.model.rowCount())
for j, value in enumerate(row_data):
item = QtGui.QStandardItem()
item.setData(value, QtCore.Qt.DisplayRole)
self.model.setItem(i, j, item)
def filter_table(self, text):
if text:
filter_column = self.combo.currentIndex()
for i in range(self.model.rowCount()):
item = self.model.item(i, filter_column)
if self.filter_row(item, text):
self.table.showRow(i)
else:
self.table.hideRow(i)
else:
for i in range(self.model.rowCount()):
self.table.showRow(i)
def filter_row(self, item, text):
return text in item.text()
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
ex = Widget()
ex.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
import sqlite3
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.edit = QtWidgets.QLineEdit()
self.combo = QtWidgets.QComboBox()
self.table = QtWidgets.QTableView()
self.model = QtGui.QStandardItemModel()
self.table.setModel(self.model)
grid = QtWidgets.QGridLayout(self)
grid.addWidget(self.edit, 0, 0)
grid.addWidget(self.combo, 0, 1)
grid.addWidget(self.table, 1, 0, 1, 2)
self.connection = sqlite3.connect("database.db")
self.populate_table("SELECT * FROM foo_table")
self.edit.textChanged.connect(self.filter_table)
def populate_table(self, query, values=None):
cursor = self.connection.cursor()
if values is None:
cursor.execute(query)
else:
cursor.execute(query, values)
name_of_columns = [e[0] for e in cursor.description]
self.model.setColumnCount(len(name_of_columns))
self.model.setRowCount(0)
self.model.setHorizontalHeaderLabels(name_of_columns)
self.combo.clear()
self.combo.addItems(name_of_columns)
for i, row_data in enumerate(cursor.fetchall()):
self.model.insertRow(self.model.rowCount())
for j, value in enumerate(row_data):
item = QtGui.QStandardItem()
item.setData(value, QtCore.Qt.DisplayRole)
self.model.setItem(i, j, item)
def filter_table(self, text):
if text:
self.populate_table(
"SELECT * FROM foo_table WHERE {} LIKE ?".format(
self.combo.currentText()
),
["%{}%".format(text)],
)
else:
self.populate_table("SELECT * FROM foo_table")
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
ex = Widget()
ex.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
There is also the option to use a QSortFilterProxyModel:
import sqlite3
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.edit = QtWidgets.QLineEdit()
self.combo = QtWidgets.QComboBox()
self.table = QtWidgets.QTableView()
self.model = QtGui.QStandardItemModel()
self.proxy = QtCore.QSortFilterProxyModel()
self.proxy.setSourceModel(self.model)
self.proxy.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.table.setModel(self.proxy)
grid = QtWidgets.QGridLayout(self)
grid.addWidget(self.edit, 0, 0)
grid.addWidget(self.combo, 0, 1)
grid.addWidget(self.table, 1, 0, 1, 2)
self.connection = sqlite3.connect("database.db")
self.populate_table("SELECT * FROM foo_table")
self.combo.currentIndexChanged.connect(self.proxy.setFilterKeyColumn)
self.edit.textChanged.connect(self.proxy.setFilterRegExp)
def populate_table(self, query, values=None):
cursor = self.connection.cursor()
if values is None:
cursor.execute(query)
else:
cursor.execute(query, values)
name_of_columns = [e[0] for e in cursor.description]
self.model.setColumnCount(len(name_of_columns))
self.model.setRowCount(0)
self.model.setHorizontalHeaderLabels(name_of_columns)
self.combo.clear()
self.combo.addItems(name_of_columns)
for i, row_data in enumerate(cursor.fetchall()):
self.model.insertRow(self.model.rowCount())
for j, value in enumerate(row_data):
item = QtGui.QStandardItem()
item.setData(value, QtCore.Qt.DisplayRole)
self.model.setItem(i, j, item)
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
ex = Widget()
ex.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
- QTableView + QSqlQueryModel
In this case the best option is to make the filter using sql changing the query:
from PyQt5 import QtCore, QtGui, QtWidgets, QtSql
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.edit = QtWidgets.QLineEdit()
self.combo = QtWidgets.QComboBox()
self.table = QtWidgets.QTableView()
self.model = QtSql.QSqlQueryModel()
self.table.setModel(self.model)
grid = QtWidgets.QGridLayout(self)
grid.addWidget(self.edit, 0, 0)
grid.addWidget(self.combo, 0, 1)
grid.addWidget(self.table, 1, 0, 1, 2)
self.populate_table("SELECT * FROM foo_table")
self.edit.textChanged.connect(self.filter_table)
def populate_table(self, query, values=None):
q = QtSql.QSqlQuery(query)
if values is not None:
for value in values:
q.addBindValue(value)
print(value)
if not q.exec_():
print(q.lastError().text())
self.model.setQuery(q)
self.combo.clear()
for i in range(self.model.columnCount()):
self.combo.addItem(self.model.headerData(i, QtCore.Qt.Horizontal))
def filter_table(self, text):
if text:
self.populate_table(
"SELECT * FROM foo_table WHERE {} LIKE ?".format(
self.combo.currentText()
),
["%{}%".format(text)],
)
else:
self.populate_table("SELECT * FROM foo_table")
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName("database.db")
if not db.open():
sys.exit(-1)
ex = Widget()
ex.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Another option is to apply the techniques of hiding rows or using a QSortFilterProxyModel.
- QTableView + QSqlTableModel
In this case the setFilter method should be used:
from PyQt5 import QtCore, QtGui, QtWidgets, QtSql
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.edit = QtWidgets.QLineEdit()
self.combo = QtWidgets.QComboBox()
self.table = QtWidgets.QTableView()
self.model = QtSql.QSqlTableModel()
self.table.setModel(self.model)
grid = QtWidgets.QGridLayout(self)
grid.addWidget(self.edit, 0, 0)
grid.addWidget(self.combo, 0, 1)
grid.addWidget(self.table, 1, 0, 1, 2)
self.model.setTable("foo_table")
self.model.select()
self.combo.clear()
for i in range(self.model.columnCount()):
self.combo.addItem(self.model.headerData(i, QtCore.Qt.Horizontal))
self.edit.textChanged.connect(self.filter_table)
def filter_table(self, text):
f = " {} LIKE '%{}%'".format(self.combo.currentText(), text) if text else text
self.model.setFilter(f)
self.model.select()
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName("database.db")
if not db.open():
sys.exit(-1)
ex = Widget()
ex.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Another option is to apply the techniques of hiding rows or using a QSortFilterProxyModel.