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()