首页 > 解决方案 > 我的实时搜索实现是否滥用了 PyQt5 方法?

问题描述

我一直在做一个管理大型数据集的项目,利用 PyQt5 的 QAbstractTableModel、QTableView 和 QSortFilterProxyModel 类来显示、过滤和修改数据。我将来需要扩展这个项目,所以我会尽力避免不良做法。我目前犹豫要不要继续前进,因为我刚刚实现了一个实时搜索栏(您键入时的表格过滤器),它似乎在测试数据上运行良好,但我担心每次输入键时运行函数可能会导致更大的问题数据集/更复杂的搜索条件。

我通过一个简单的示例复制了下面的实时搜索功能,该示例通过连接 QLineEdit.textChanged 信号在每次将字符输入到搜索栏中时更新代理模型的过滤字符串来过滤表模型中的“名称”列。

如果这里没有真正的问题,我深表歉意,但我找不到此功能的任何 PyQt 示例。我只想知道使用 textChanged 信号更新每个按键上的过滤条件是否安全,或者是否有更好/更有效的方法来完成此操作。

我已经在更大的数据集和稍微复杂的过滤条件上尝试了这个功能,它似乎运行顺利,但我对 PyQt 的熟练程度不足以猜测以后可能会出现什么类型的问题。

from PyQt5 import QtCore, QtGui, QtWidgets
import pandas
import sys

class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, data, parent=None):
        super().__init__(parent)

        self.headings = ["name", "address", "number"]
        self.dataset = data

    def rowCount(self, parent):
        return self.dataset.shape[0]

    def columnCount(self, parent):
        return self.dataset.shape[0]

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

        return QtCore.QVariant()

    def headerData(self, section, orientation, role):
        if role == QtCore.Qt.DisplayRole and orientation == QtCore.Qt.Horizontal:
            return self.headings[section]

        return QtCore.QVariant()

class ProxyTableModel(QtCore.QSortFilterProxyModel):
    def __init__(self):
        super().__init__()
        self.filter_str = ''

    def setFilterStr(self, val):
        self.filter_str = val.lower()

    def filterAcceptsRow(self, sourceRow, sourceParent):
        index = self.sourceModel().index(sourceRow, 0, sourceParent)
        data = self.sourceModel().data(index, QtCore.Qt.DisplayRole)

        return self.filter_str in data.lower()

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, data):
        super().__init__()
        self.setupUi(data)
        self.show()

    def setupUi(self, data):
        self.setObjectName("MainWindow")
        self.resize(500, 500)
        # create central display widget
        self.centralWidget = QtWidgets.QWidget(self)
        self.centralWidget.setObjectName("central_widget")
        # create table view
        self.tableView = QtWidgets.QTableView(self.centralWidget)
        self.tableView.setGeometry(QtCore.QRect(0, 50, 500, 450))
        self.tableView.setObjectName("email_attributes")
        # create models & set proxy model to view
        self.mainModel = TableModel(data)
        self.proxyModel = ProxyTableModel()
        self.proxyModel.setSourceModel(self.mainModel)
        self.tableView.setModel(self.proxyModel)
        # create column filter search bar
        self.searchBar = QtWidgets.QLineEdit(self.centralWidget)
        self.searchBar.setGeometry(QtCore.QRect(0, 0, 500, 50))
        self.searchBar.textChanged[str].connect(self.filternames)
        # set central widget & connect slots
        self.setCentralWidget(self.centralWidget)
        QtCore.QMetaObject.connectSlotsByName(self)

    def filternames(self, text):
        # set proxy model filter string
        self.proxyModel.setFilterStr(text)
        # set filterFixedString() to '' to refresh view
        self.proxyModel.setFilterFixedString('')

# underlying dataset for table model
df = {
    "name": ["John Smith", "Larry David", "George Washington"],
    "address": ["10 Forest Dr", "15 East St", "12 Miami Ln"],
    "phone_number": ["000-111-2222", "222-000-1111", "111-222-0000"]
}
data = pandas.DataFrame(df)

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    ui = MainWindow(data)
    sys.exit(app.exec_())

标签: pythonpython-3.xpyqt5

解决方案


您应该扩展 QLineEdit 类和 keyPressedEvent 函数来处理各种按键事件,如下所示

from PyQt5.QtWidgets import (QLineEdit)
from PyQt5.QtCore import (Qt, QRegExp)
class LineEdit(QLineEdit):
    def __init__(self, parent=None):
        super(LineEdit, self).__init__(parent)
        self.initialize_component()

    def initialize_component(self):
        self.setClearButtonEnabled(True)

    def keyPressEvent(self, event):
        key = event.key()
        if key == Qt.Key_Tab:
            self.parent().focusNextChild()
        elif key == Qt.Key_Backtab:
            self.parent().focusPrevChild()
        else:
            return QLineEdit.keyPressEvent(self, event)

推荐阅读