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

问题描述 投票:0回答:1

我使用 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 like Filtering

我的代码过滤如下所示:

python code filtering

我的sample.csv文件有数据(19条记录),如下图所示:

sample.csv file

python search pyqt5 filtering qtablewidget
1个回答
0
投票

这是代码的修订版,在过滤时实现了搜索栏:

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.clearFilter()
        self.menu = QMenu(self)
        self.col = index
        data_unique = []
        self.checkBoxs = []
        self.search_bar = QLineEdit()
        self.search_bar.setPlaceholderText("Search...")
        self.search_bar.textChanged.connect(self.on_search)
        search_action = QWidgetAction(self.menu)
        search_action.setDefaultWidget(self.search_bar)
        self.menu.addAction(search_action)
        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)
                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.setFixedWidth(self.menu.sizeHint().width())
        self.search_bar.setFixedWidth(self.menu.width() - 20) 
        self.menu.exec_(QtCore.QPoint(posX, posY))

    def on_search(self, text):
        for action in self.menu.actions():
            if isinstance(action, QWidgetAction):
                widget = action.defaultWidget()
                if isinstance(widget, QCheckBox) and widget.text() != "Select all":
                    if text.lower() in widget.text().lower():
                        action.setVisible(True)
                    else:
                        action.setVisible(False)

    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)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())
© www.soinside.com 2019 - 2024. All rights reserved.