首页 > 编程问答 >如何在 Pyqt5 Python 中实现 QTableWidget 列过滤器中的搜索栏?

如何在 Pyqt5 Python 中实现 QTableWidget 列过滤器中的搜索栏?

时间:2024-07-31 09:30:52浏览次数:14  
标签:python search pyqt5 filtering qtablewidget

我使用 pyqt5 创建了一个 QTableWidget,并成功地在表格小部件的每一列中添加了过滤选项,并使用堆栈溢出上可用的答案之一。过滤器按预期工作,但我想在顶部的过滤器中添加搜索栏来搜索值。

在我的 python 脚本中,过滤器中可能有大约 50 多个唯一值。因此,如果有一种方法可以在过滤器中搜索值,那就很容易了。

另外,有没有一种方法可以在其中添加滚动条,因为目前如果我向表中添加更多值,我的过滤区域会变得更大,并且更大以累积所有值。

这是我完整的 python 代码:

import csv
import sys
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QTableWidget, QCheckBox, QWidgetAction, QMenu, QDialogButtonBox, QTableWidgetItem
from PyQt5.QtGui import QFont


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent=parent)
        self.verticalLayout = QVBoxLayout(self)
        self.filterall = QTableWidget(self)
        self.filterall.setColumnCount(0)
        self.filterall.setRowCount(0)
        self.verticalLayout.addWidget(self.filterall)

        self.loadAll()
        self.horizontalHeader = self.filterall.horizontalHeader()
        self.horizontalHeader.sectionClicked.connect(self.on_view_horizontalHeader_sectionClicked)
        self.keywords = dict([(i, []) for i in range(self.filterall.columnCount())])
        self.checkBoxs = []
        self.col = None

    def slotSelect(self, state):

        for checkbox in self.checkBoxs:
            checkbox.setChecked(QtCore.Qt.Checked == state)

    def on_view_horizontalHeader_sectionClicked(self, index):
        #self.clearFilter()
        self.menu = QMenu(self)
        #self.menu.setStyleSheet('QMenu { menu-scrollable: true; }')
        self.col = index

        data_unique = []

        self.checkBoxs = []

        checkBox = QCheckBox("Select all", self.menu)
        checkableAction = QWidgetAction(self.menu)
        checkableAction.setDefaultWidget(checkBox)
        self.menu.addAction(checkableAction)
        checkBox.setChecked(True)
        checkBox.stateChanged.connect(self.slotSelect)

        for i in range(self.filterall.rowCount()):
            if not self.filterall.isRowHidden(i):
                item = self.filterall.item(i, index)
                font = QFont()
                font.setBold(False)
                self.filterall.horizontalHeaderItem(index).setFont(font)
                if item.text() not in data_unique:
                    data_unique.append(item.text())
                    checkBox = QCheckBox(item.text(), self.menu)
                    checkBox.setChecked(True)
                    checkableAction = QWidgetAction(self.menu)
                    checkableAction.setDefaultWidget(checkBox)
                    self.menu.addAction(checkableAction)
                    self.checkBoxs.append(checkBox)

        btn = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel,
                                     QtCore.Qt.Horizontal, self.menu)
        btn.accepted.connect(self.menuClose)
        btn.rejected.connect(self.menu.close)
        checkableAction = QWidgetAction(self.menu)
        checkableAction.setDefaultWidget(btn)
        self.menu.addAction(checkableAction)

        headerPos = self.filterall.mapToGlobal(self.horizontalHeader.pos())

        posY = headerPos.y() + self.horizontalHeader.height()
        posX = headerPos.x() + self.horizontalHeader.sectionPosition(index)
        self.menu.exec_(QtCore.QPoint(posX, posY))

    def menuClose(self):
        self.keywords[self.col] = []
        for element in self.checkBoxs:
            if element.isChecked():
                self.keywords[self.col].append(element.text())
        self.filterdata()
        self.menu.close()

    def loadAll(self):
        with open("sample.csv", "r") as inpfil:
            reader = csv.reader(inpfil, delimiter=',')
            csheader = next(reader)
            ncol = len(csheader)
            data = list(reader)
            row_count = len(data)

            self.filterall.setRowCount(row_count)
            self.filterall.setColumnCount(ncol)
            self.filterall.setHorizontalHeaderLabels(str('%s' % ', '.join(map(str, csheader))).split(","))

            for ii in range(0, row_count):
                mainins = data[ii]
                for var in range(0, ncol):
                    self.filterall.setItem(ii, var, QTableWidgetItem(mainins[var]))

    def clearFilter(self):
        for i in range(self.filterall.rowCount()):
            self.filterall.setRowHidden(i, False)

    def filterdata(self):

        columnsShow = dict([(i, True) for i in range(self.filterall.rowCount())])
        
        for i in range(self.filterall.rowCount()):
            for j in range(self.filterall.columnCount()):
                item = self.filterall.item(i, j)
                if self.keywords[j]:
                    if item.text() not in self.keywords[j]:
                        columnsShow[i] = False
                        font = QFont()
                        font.setBold(True)
                        self.filterall.horizontalHeaderItem(j).setFont(font)
                        
        for key, value in columnsShow.items():
            self.filterall.setRowHidden(key, not value)

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

我希望看到我的代码工作,以便当用户在搜索栏中输入时,它会从给定的唯一值中过滤选项。这就像 Excel 过滤器中的搜索栏,您可以在其中键入关键字并过滤可用的唯一值。

预期示例输出:

类似 Excel 的过滤

我的代码过滤如下所示:

python 代码过滤| ||我的sample.csv 文件有数据(19 条记录),如下面的屏幕截图所示:

sample.csv 文件

sample.csv file


好的,我可以帮助你在 Pyqt5 QTableWidget 列过滤器中实现搜索栏。以下是实现此目标的方法,以及添加滚动条以更好地处理大量唯一值的说明:

import csv
import sys
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QTableWidget, QCheckBox, QWidgetAction, QMenu, QDialogButtonBox, QTableWidgetItem, QLineEdit
from PyQt5.QtGui import QFont

class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent=parent)
        self.verticalLayout = QVBoxLayout(self)
        self.filterall = QTableWidget(self)
        self.filterall.setColumnCount(0)
        self.filterall.setRowCount(0)
        self.verticalLayout.addWidget(self.filterall)

        self.loadAll()
        self.horizontalHeader = self.filterall.horizontalHeader()
        self.horizontalHeader.sectionClicked.connect(self.on_view_horizontalHeader_sectionClicked)
        self.keywords = dict([(i, []) for i in range(self.filterall.columnCount())])
        self.checkBoxs = []
        self.col = None

    def slotSelect(self, state):
        for checkbox in self.checkBoxs:
            checkbox.setChecked(QtCore.Qt.Checked == state)

    def on_view_horizontalHeader_sectionClicked(self, index):
        self.col = index

        data_unique = []
        self.checkBoxs = []

        menu = QMenu(self)
        menu.setStyleSheet('QMenu { menu-scrollable: true; }')  # 启用滚动

        # 添加搜索栏
        searchEdit = QLineEdit(menu)
        searchEdit.setPlaceholderText("搜索...")
        searchEdit.textChanged.connect(lambda text: self.searchFilter(text, menu))
        checkableAction = QWidgetAction(menu)
        checkableAction.setDefaultWidget(searchEdit)
        menu.addAction(checkableAction)

        # 添加“全选”复选框
        checkBox = QCheckBox("全选", menu)
        checkableAction = QWidgetAction(menu)
        checkableAction.setDefaultWidget(checkBox)
        menu.addAction(checkableAction)
        checkBox.setChecked(True)
        checkBox.stateChanged.connect(self.slotSelect)

        for i in range(self.filterall.rowCount()):
            if not self.filterall.isRowHidden(i):
                item = self.filterall.item(i, index)
                if item.text() not in data_unique:
                    data_unique.append(item.text())
                    checkBox = QCheckBox(item.text(), menu)
                    checkBox.setChecked(True)
                    checkableAction = QWidgetAction(menu)
                    checkableAction.setDefaultWidget(checkBox)
                    menu.addAction(checkableAction)
                    self.checkBoxs.append(checkBox)

        btn = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel,
                                     QtCore.Qt.Horizontal, menu)
        btn.accepted.connect(lambda: self.menuClose(menu))  # 传递菜单
        btn.rejected.connect(menu.close)
        checkableAction = QWidgetAction(menu)
        checkableAction.setDefaultWidget(btn)
        menu.addAction(checkableAction)

        headerPos = self.filterall.mapToGlobal(self.horizontalHeader.pos())
        posY = headerPos.y() + self.horizontalHeader.height()
        posX = headerPos.x() + self.horizontalHeader.sectionPosition(index)
        menu.exec_(QtCore.QPoint(posX, posY))

    def searchFilter(self, text, menu):
        # 在菜单中过滤操作
        for action in menu.actions():
            if isinstance(action.defaultWidget(), QCheckBox):
                checkbox = action.defaultWidget()
                checkbox.setVisible(text.lower() in checkbox.text().lower())

    def menuClose(self, menu):  # 接收菜单作为参数
        self.keywords[self.col] = []
        for action in menu.actions():
            if isinstance(action.defaultWidget(), QCheckBox):
                checkbox = action.defaultWidget()
                if checkbox.isChecked():
                    self.keywords[self.col].append(checkbox.text())
        self.filterdata()
        menu.close()  # 关闭传递的菜单

    def loadAll(self):
        with open("sample.csv", "r") as inpfil:
            reader = csv.reader(inpfil, delimiter=',')
            csheader = next(reader)
            ncol = len(csheader)
            data = list(reader)
            row_count = len(data)

            self.filterall.setRowCount(row_count)
            self.filterall.setColumnCount(ncol)
            self.filterall.setHorizontalHeaderLabels(str('%s' % ', '.join(map(str, csheader))).split(","))

            for ii in range(0, row_count):
                mainins = data[ii]
                for var in range(0, ncol):
                    self.filterall.setItem(ii, var, QTableWidgetItem(mainins[var]))

    def clearFilter(self):
        for i in range(self.filterall.rowCount()):
            self.filterall.setRowHidden(i, False)

    def filterdata(self):
        columnsShow = dict([(i, True) for i in range(self.filterall.rowCount())])
        for i in range(self.filterall.rowCount()):
            for j in range(self.filterall.columnCount()):
                item = self.filterall.item(i, j)
                if self.keywords[j]:
                    if item.text() not in self.keywords[j]:
                        columnsShow[i] = False
                        font = QFont()
                        font.setBold(True)
                        self.filterall.horizontalHeaderItem(j).setFont(font)
        for key, value in columnsShow.items():
            self.filterall.setRowHidden(key, not value)

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

以下是所做更改的说明:

  1. 添加搜索栏:
  2. on_view_horizontalHeader_sectionClicked 函数内部,我们创建了一个 QLineEdit 小部件 ( searchEdit ) 并将其添加到菜单中。
  3. searchEdit textChanged 信号连接到一个新函数 searchFilter

  4. 实现 searchFilter 函数:

  5. 此函数接收搜索文本并迭代菜单中的操作。
  6. 它检查操作是否具有 QCheckBox 作为其默认小部件。
  7. 如果是,它会根据搜索文本显示或隐藏复选框。

  8. 启用菜单滚动:

  9. on_view_horizontalHeader_sectionClicked 函数中,我们使用样式表 QMenu { menu-scrollable: true; } 启用菜单滚动。这为包含大量唯一值的长过滤器列表添加了一个滚动条。

现在,当单击列标题时,将出现一个带有搜索栏的菜单。在搜索栏中键入内容将过滤过滤器选项列表,从而更容易找到要查找的内容,尤其是在有大量唯一值的情况下。

标签:python,search,pyqt5,filtering,qtablewidget
From: 78814144

相关文章

  • Python - Abstract Base classes
    Wehaveseenthatifwehavetodefineagroupofclassesthathavesimilarfeaturesandshowcommonbehavior,wecandefineabaseclassandtheninherittheclassesfromit.Inthederivedclasses,wehavethechoicetoeitherusethebaseclassversion......
  • python3 unittest+BeautifulReport单个进程输出多个测试报告
    最近一个项目中需要由于输出的案例内容非常多(上万条),导致BeautifulReport输出的报告内容非常大(几百兆)。浏览器无法正常处理这么大的测试报告,就算打开了,也不方便阅读和处理,因此需要将报告分成多个输出。经修改代码,发现单个进程内输出多个测试报告出现问题:第一个测试报告能正常数据......
  • 具有自引用的类装饰器的 Python 类型提示
    我的最终目标是编写一个系统来轻松记录函数调用(特别是类方法)。我首先编写一个带有包装方法的类Loggable,该方法允许我装饰子类方法并记录它们的调用|||现在我可以编写子类并记录它们的调用:Param=ParamSpec("Param")RetType=TypeVar("RetType")CountType=......
  • 如何在for循环中使用curve_fit函数在python中一次性创建多个回归?
    简而言之,我有两个矩阵,一个称为t,另一个称为y。每个都有7列。假设它们被称为a、b、c、d、e、f和g。我想要的是从a对a、b对b、...、g对g这两个矩阵进行回归。我已经设法使我的算法使用curve_fit对一列进行回归一次。但我真正希望的是它能够一次性完成7个回归......
  • 激活虚拟环境会让python消失?
    VisualStudioCode终端的屏幕截图如屏幕截图所示,python在Powershell中运行得很好。然后我在E:\DrewFTCAPI\ftcapivenv激活虚拟环境,然后python就消失了。不仅没有消失,它不运行任何东西,也不产生任何输出。我至少预计会出现某种类型的"python"i......
  • Python 3.6 中的相互递归类型,使用命名元组语法
    我正在尝试实现图的节点和边。这是我的代码:fromtypingimportNamedTuple,ListclassNode(NamedTuple):name:stredges:List[Edge]classEdge(NamedTuple):src:Nodedest:Node这会引发错误,因为创建Edge时未定义Node类型。......
  • 使用 keras 模型对函数进行 Python 类型提示
    如果我创建这样的函数:defmdl(input_shape):model=Sequential()model.add(Conv2D(depth=64,kernel_size=(3,3),input_shape=input_shape,activation='relu'))model.add(Dense(32),activation='relu')model.add(Dropout(0.3))m......
  • Python:自动完成可以用于列表中的元素吗?
    Python在函数参数和函数返回类型中具有类型提示。类的元素是否有类似的东西?我希望能够在如下示例中使用自动完成功能:classMyClass:defhello(self):print("Hello")mylist=[]mylist.append(MyClass())foriinmylist:i.hello()#Noautocomplete......
  • python 中 COM 对象的正确类型提示是什么?
    我在python中使用COM对象来向3rd方软件公开可编程接口。这是通过使用Dispatchfromwin32com.client来实现的。我的项目也一直在使用python.3.7中的类型提示,但是我不确定如何为了类型提示的目的定义这些COM对象的类型。这个问题涉及我拥有的所有COM......
  • 如何遍历Python字典同时避免KeyErrors?
    解析大型JSON时,某些键可能仅在某些情况下存在,例如出现错误时。从服务器的API获取200OK的情况并不少见,但是您得到的响应包含应检查的错误。处理此问题的最佳方法是什么?我知道使用类似||之类的东西。|是处理KeyError的一种方法。get()但是如果......