PySide6如何实现点击TableWidget列表头在该列右侧显示列表选择框筛选列数据

1. 实现的界面效果

鼠标单击选择需要筛选的表格列标题,在列标题右侧出现列表选择框,选中列表选择框的某个数据,相应的表格上就只显示符合筛选条件的数据。

在这里插入图片描述

2. 关键技术

拆分代码,主要介绍实现功能的关键点。

2.1 设置表格列标题的用户鼠标点击事件

TableWidget控件的表格列标题的用户鼠标点击事件设置如下:

        header = self.tableWidget.horizontalHeader()
        header.sectionClicked.connect(self.showFilterComboBox)

2.2 使用comboBox的activated信号

在 Qt 的 QComboBox 控件中,currentIndexChanged(int index) 和 activated(int index) 是两个常用的信号(signal),但它们在触发时机和用途上有所不同。

QComboBox 的 currentIndexChanged 信号来检测用户何时改变了选中项,但是QComboBox 默认索引是0,导致索引是0的文本被选中不会触发currentIndexChanged 信号。activated 信号在用户选择了一个项并且下拉列表关闭时触发,无论这个项是否是之前选中的。所以,最好不要使用currentIndexChanged 信号,而改为使用activated 信号比较好。

使用QComboBox 的currentIndexChanged 信号的时候还会遇到如下的错误。

原来出错的代码

    self.comboBox.currentIndexChanged.connect(self.commitData)
RuntimeError: Failed to connect signal currentIndexChanged(int).

def commitData(self):
        editor = self.sender()
        commit_index = self.comboBox.currentIndex()
        model = self.comboBox.model()
        
        # 找到当前编辑的单元格的索引
        for view in QApplication.instance().allWidgets():
            if isinstance(view, QTableView):
                for index in view.selectionModel().selectedIndexes():
                    if view.indexWidget(index) == editor:
                        commit_index = index
                        break
        
        # 提交数据到模型
        model.setData(commit_index, editor.currentText(), Qt.EditRole)
currentIndexChanged 信号不直接传递新索引。正确的方式是在槽函数中直接访问 comboBox.currentIndex()def on_current_index_changed(self):
    index = self.comboBox.currentIndex()  # 获取当前索引
    # ... 使用索引执行操作 ...

# 连接信号和槽
comboBox.currentIndexChanged.connect(self.on_current_index_changed)

解决办法:必须在comboBox的currentIndexChanged事件响应函数commitData中传递一个int类型的入参,修改后的代码如下:

self.comboBox.currentIndexChanged.connect(
            lambda: self.commitData(self.comboBox.currentIndex())
        )

def commitData(self, commit_index):
     editor = self.sender()
     model = self.comboBox.model()

     # 找到当前编辑的单元格的索引
     for view in QApplication.instance().allWidgets():
         if isinstance(view, QTableView):
             for index in view.selectionModel().selectedIndexes():
                 if view.indexWidget(index) == editor:
                     commit_index = index
                     break

     # 提交数据到模型
     model.setData(commit_index, editor.currentText(), Qt.EditRole)

2.3 设置点击表格列标题后的列表框显示事件

    @Slot()
     def showFilterComboBox(self, column_index):
        # 先隐藏已经出现的筛选列表框
        self._hide_combox()
        # 在列标题旁边显示一个CustomComboBox
        combo = CustomComboBox(self)
        # 添加筛选选项
        column_data_lst = []
        for row in range(self.tableWidget.rowCount()):
            editor = self.tableWidget.cellWidget(row, column_index)
            if isinstance(editor, QComboBox):
                num_items = editor.count()
                # 迭代并打印每个选项的文本
                for i in range(num_items):
                    item_text = editor.itemText(i)
                    column_data_lst.append(item_text)
            else:
                item = self.tableWidget.item(row, column_index)
                if item is not None:
                    data = item.text()
                else:
                    data = ""
                column_data_lst.append(data)

        column_data_lst = sorted(set(column_data_lst))
        combo.addItems(column_data_lst)
        combo.activated.connect(
            lambda index: self.filterTableByColumn(column_index, combo.currentText())
        )

        # 计算显示位置
        # Get the position and size of the QTableWidget
        table_rect = self.tableWidget.geometry()
        header_height = self.tableWidget.horizontalHeader().height()

        # Calculate the position for the CustomComboBox
        column_width_sum = self._get_column_width_sum(column_index)
        combo_box_x = table_rect.x() + column_width_sum
        combo_box_y = (
            table_rect.y() + header_height
        )  # Position it right below the header

        # Set the geometry for the CustomComboBox
        combo_box_width = self.tableWidget.columnWidth(
            column_index
        )  # Match the column width
        combo_box_height = (
            combo.sizeHint().height()
        )  # Use the combo box's preferred height
        combo.setGeometry(combo_box_x, combo_box_y, combo_box_width, combo_box_height)
        combo.show()

        # 存储这个CustomComboBox以便后续处理(比如隐藏它)
        self.filter_comboboxes[column_index] = combo

2.4 计算ComboBox的显示位置

要想列表选择框显示在选中列的右侧,关键在于计算好ComboBox在界面上显示的位置

    # 计算显示位置
    # Get the position and size of the QTableWidget
    table_rect = self.tableWidget.geometry()
    header_height = self.tableWidget.horizontalHeader().height()

    # Calculate the position for the CustomComboBox
    column_width_sum = self._get_column_width_sum(column_index)
    combo_box_x = table_rect.x() + column_width_sum
    combo_box_y = (
        table_rect.y() + header_height
    )  # Position it right below the header

    # Set the geometry for the CustomComboBox
    combo_box_width = self.tableWidget.columnWidth(
        column_index
    )  # Match the column width
    combo_box_height = (
        combo.sizeHint().height()
    )  # Use the combo box's preferred height
    combo.setGeometry(combo_box_x, combo_box_y, combo_box_width, combo_box_height)
    combo.show()

2.5 根据列表框用户选中的数值显示表格内容

要想做到根据列表框用户选中的数值显示表格内容,关键在于显示和隐藏表格的行数据

@Slot()
def filterTableByColumn(self, column_index, value):
    # 根据列和值来筛选表格
    for row in range(self.tableWidget.rowCount()):
        item = self.tableWidget.item(row, column_index)
        if item is not None:
            data = item.text()
        else:
            data = ""
        if data == value:
            self.tableWidget.showRow(row)
        else:
            self.tableWidget.hideRow(row)
    # 隐藏列表框
    self.filter_comboboxes[column_index].hide()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

草莓仙生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值