You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PyQt5的QTableWidget列过滤器中添加搜索栏与滚动条

给QTableWidget过滤器添加搜索栏与滚动条优化

直接上修改后的完整代码,实现类似Excel的筛选搜索效果,同时解决选项过多导致菜单过大的问题:

import sys
from PyQt5.QtWidgets import (QApplication, QMainWindow, QTableWidget, QTableWidgetItem,
                             QMenu, QAction, QLineEdit, QWidget, QVBoxLayout, QScrollArea)
from PyQt5.QtCore import Qt, QRegExp
from PyQt5.QtGui import QRegExpValidator

class FilterableTableWidget(QTableWidget):
    def __init__(self, rows, columns, parent=None):
        super().__init__(rows, columns, parent)
        self.filter_menus = {}  # 存储各列过滤器菜单
        self.filter_values = {}  # 存储各列过滤值

    def setHorizontalHeaderLabels(self, labels):
        super().setHorizontalHeaderLabels(labels)
        # 为每个表头绑定过滤器菜单触发事件
        for col in range(self.columnCount()):
            header_item = self.horizontalHeaderItem(col)
            header_item.setToolTip("点击筛选")
        self.horizontalHeader().setContextMenuPolicy(Qt.CustomContextMenu)
        self.horizontalHeader().customContextMenuRequested.connect(self.show_filter_menu)

    def show_filter_menu(self, pos):
        # 获取点击的列索引
        col = self.horizontalHeader().logicalIndexAt(pos)
        if col < 0:
            return

        # 提取当前列的所有唯一值并排序
        unique_values = []
        for row in range(self.rowCount()):
            item = self.item(row, col)
            if item and item.text() not in unique_values:
                unique_values.append(item.text())
        unique_values.sort()

        # 创建主菜单
        menu = QMenu(self)

        # 构建搜索栏组件
        search_widget = QWidget()
        search_layout = QVBoxLayout(search_widget)
        search_layout.setContentsMargins(5, 5, 5, 5)
        search_edit = QLineEdit()
        search_edit.setPlaceholderText("搜索选项...")
        search_layout.addWidget(search_edit)

        # 构建带滚动条的选项区域
        scroll_area = QScrollArea()
        scroll_area.setWidgetResizable(True)
        scroll_area.setMaximumHeight(300)  # 限制菜单最大高度,超出显示滚动条
        content_widget = QWidget()
        content_layout = QVBoxLayout(content_widget)
        content_layout.setAlignment(Qt.AlignTop)
        scroll_area.setWidget(content_widget)
        search_layout.addWidget(scroll_area)

        # 将搜索组件加入菜单
        search_action = QAction(menu)
        search_action.setDefaultWidget(search_widget)
        menu.addAction(search_action)
        menu.addSeparator()

        # 添加全选选项
        select_all_action = QAction("全选", menu)
        select_all_action.setCheckable(True)
        select_all_action.setChecked(True)
        menu.addAction(select_all_action)
        menu.addSeparator()

        # 生成选项动作列表
        action_list = []
        for value in unique_values:
            action = QAction(value, menu)
            action.setCheckable(True)
            action.setChecked(True)
            action.triggered.connect(lambda checked, val=value: self.update_filter(col, val, checked))
            content_layout.addWidget(action)
            action_list.append(action)

        # 绑定搜索栏文本变化事件,过滤选项
        def filter_options(text):
            regex = QRegExp(text, Qt.CaseInsensitive, QRegExp.FixedString)
            for action in action_list:
                action.setVisible(regex.exactMatch(action.text()))
        search_edit.textChanged.connect(filter_options)

        # 绑定全选事件
        def toggle_all(checked):
            for action in action_list:
                action.setChecked(checked)
                self.update_filter(col, action.text(), checked)
        select_all_action.triggered.connect(toggle_all)

        # 显示菜单
        menu.exec_(self.horizontalHeader().mapToGlobal(pos))

    def update_filter(self, col, value, checked):
        # 更新过滤值集合
        if col not in self.filter_values:
            self.filter_values[col] = set()
        if checked:
            self.filter_values[col].add(value)
        else:
            self.filter_values[col].discard(value)
        # 应用过滤规则
        self.apply_filters()

    def apply_filters(self):
        # 根据过滤值控制行的显示/隐藏
        for row in range(self.rowCount()):
            show_row = True
            for col, allowed_values in self.filter_values.items():
                item = self.item(row, col)
                if item and item.text() not in allowed_values:
                    show_row = False
                    break
            self.setRowHidden(row, not show_row)

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("带搜索过滤器的表格")
        self.setGeometry(100, 100, 800, 600)

        # 创建表格并设置表头
        self.table = FilterableTableWidget(100, 3)
        headers = ["姓名", "部门", "职级"]
        self.table.setHorizontalHeaderLabels(headers)

        # 填充测试数据(模拟50+唯一值场景)
        departments = ["研发部", "市场部", "人事部", "财务部", "运营部", "产品部", "客服部", "行政部"] * 7
        positions = ["实习生", "专员", "主管", "经理", "总监", "副总裁", "总裁"] * 8
        for row in range(100):
            self.table.setItem(row, 0, QTableWidgetItem(f"员工{row+1}"))
            self.table.setItem(row, 1, QTableWidgetItem(departments[row]))
            self.table.setItem(row, 2, QTableWidgetItem(positions[row]))

        self.setCentralWidget(self.table)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

关键修改说明

  • 搜索栏功能:在过滤器菜单顶部添加QLineEdit,通过正则匹配实时过滤选项,支持大小写不敏感搜索
  • 滚动条优化:将选项列表放入QScrollArea并限制最大高度,避免选项过多导致菜单超出屏幕
  • 全选同步:点击全选按钮时自动同步所有选项的选中状态,同时更新过滤规则
  • 过滤逻辑:通过集合存储允许显示的值,遍历所有行并控制显示状态,保证过滤效率

内容的提问来源于stack exchange,提问作者Jenit Patel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 08:40:11