如何在 PyQt5 中过滤 SQLite3 表

问题描述

我正在尝试制作一个搜索栏来过滤表中的数据,但看起来模型没有链接到行编辑表有效,但我无法使用它过滤数据库数据(它适用于列表)怎么了?

class Ui_MainWindow(object):
    def setupUi(self,MainWindow):
        mainLayout = QVBoxLayout()
        conn = sqlite3.connect("banco_cadastro.db.db")
        cursor = conn.cursor()
        list = (cursor.fetchall())
        model = QStandardItemmodel(len(list),5)
        model.setHorizontalHeaderLabels(['Clients'])
        for row,company in enumerate(list):
            item = QStandardItem(company)
            model.setItem(row,item)
        filter_proxy_model = QSortFilterProxyModel()
        filter_proxy_model.setSourceModel(model)
        filter_proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive)
        filter_proxy_model.setFilterKeyColumn(0)
        MainWindow.setobjectName("MainWindow")
        MainWindow.resize(779,693)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setobjectName("centralwidget")
        self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit.textChanged.connect(filter_proxy_model.setFilterRegExp)
        mainLayout.addWidget(self.lineEdit)
        self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)
        self.tableWidget.setobjectName("tableWidget")
        MainWindow.setCentralWidget(self.centralwidget)
        connection = sqlite3.connect('banco_cadastro.db')
        query = "SELECT * FROM cadastro"
        result = connection.execute(query)
        list = (cursor.fetchall())

        for row_number,row_data in enumerate(result):
            self.tableWidget.insertRow(row_number)

            for column_number,data in enumerate(row_data):
                self.tableWidget.setItem(row_number,column_number,QtWidgets.QTableWidgetItem(str(data)))

        connection.close()

    def retranslateUi(self,MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setwindowTitle(_translate("MainWindow","MainWindow"))

if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

解决方法

免责声明:在这篇文章中,我将只关注背景目标,而不关注 OP 的错误(例如,将 QStandardItemModel 与 QTableWidget 结合,或者使用未连接到的 QSortFilterProxyModel 的无用尝试任何东西)。

我将借此机会展示如何使用 PyQt5(它也适用于 PySide2)过滤 sqlite 表(相同的概念可以应用于其他数据库)的不同方法。

- QTableWidget + sqlite3 模块

一种可能的解决方案是遍历行并验证它是否满足所需条件,如果满足则该行将可见,否则该行将被隐藏。

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)
        grid.addWidget(self.combo,1)
        grid.addWidget(self.table,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()

另一种可能的解决方案是删除所有数据并使用过滤器发出新请求。

import sqlite3

from PyQt5 import QtCore,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 模块

同上逻辑:

import sqlite3

from PyQt5 import QtCore,QtGui,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,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,text):
        if text:
            filter_column = self.combo.currentIndex()

            for i in range(self.model.rowCount()):
                item = self.model.item(i,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,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,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,)
        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()

还可以选择使用 QSortFilterProxyModel:

import sqlite3

from PyQt5 import QtCore,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,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,item)


def main():
    import sys

    app = QtWidgets.QApplication(sys.argv)
    ex = Widget()
    ex.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

- QTableView + QSqlQueryModel

在这种情况下,最好的选择是使用 sql 更改查询来制作过滤器:

from PyQt5 import QtCore,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,2)

        self.populate_table("SELECT * FROM foo_table")
        self.edit.textChanged.connect(self.filter_table)

    def populate_table(self,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,)
        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()

另一种选择是应用隐藏行或使用 QSortFilterProxyModel 的技术。

- QTableView + QSqlTableModel

在这种情况下,应该使用 setFilter 方法:

from PyQt5 import QtCore,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,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()

另一种选择是应用隐藏行或使用 QSortFilterProxyModel 的技术。