PyQt6 QTableView首次点击选中项selectedIndexes()返回空列表问题
问题根因
自定义mousePressEvent时的执行顺序错误:先发射了自定义信号,再调用父类QTableView.mousePressEvent。QTableView的选中状态更新逻辑是在父类的鼠标按下事件中执行的,首次点击时信号触发早于选中逻辑执行,因此selectedIndexes()返回空列表;第一次点击完成后选中状态被保留,后续点击时信号触发时能拿到上一次的选中结果,才会看起来“正常工作”。
解决方案
方案1:修正事件执行顺序
如果要保留自定义信号的实现,只需要调整代码顺序,先执行父类事件处理逻辑,待选中状态更新完成后再发射信号即可,无需提前设置默认选中行。
修改后的自定义表格类代码:
class TableViewClick(qt.QTableView): signal = pyqtSignal() def mousePressEvent(self, event): # 先执行父类默认逻辑,完成点击选中流程 super().mousePressEvent(event) # 选中状态更新后再发射自定义信号 self.signal.emit()
注意:用
super()调用父类方法比直接写qt.QTableView.mousePressEvent(self, event)更符合Python面向对象的写法,后续类继承时不会出现逻辑异常。
方案2:使用原生信号(推荐)
完全不需要重写鼠标事件自定义信号,QTableView自带的clicked信号已经实现了点击表项后的触发逻辑,且触发时机在选中状态更新完成之后,还会自动将当前点击项的QModelIndex作为参数传出,直接就能拿到行号,代码更简洁稳定,也不会出现时序问题。
完整实现代码:
from PyQt6.QtCore import Qt, QModelIndex from PyQt6.QtGui import QStandardItem, QStandardItemModel from PyQt6 import QtWidgets as qt import sys class Main(qt.QMainWindow): def __init__(self): super().__init__() frame = qt.QFrame() self.setCentralWidget(frame) laymb = qt.QVBoxLayout(frame) model = QStandardItemModel() # 无需自定义TableView子类 tabmb = qt.QTableView() tabmb.setModel(model) tabmb.setGridStyle(Qt.PenStyle(0)) tabmb.setSelectionMode(qt.QTableView.SelectionMode(1)) tabmb.setSelectionBehavior(tabmb.SelectionBehavior(1)) laymb.addWidget(tabmb) subj = [] for e in range(10): subj.append(QStandardItem('Testing...')) model.appendColumn(subj) # 直接绑定原生clicked信号 tabmb.clicked.connect(self._click_table) def _click_table(self, index: QModelIndex): # 直接从参数获取点击行号,无需额外调用selectedIndexes click_row = index.row() print(f"当前点击行号:{click_row}") # 需要所有选中索引时再调用selectedIndexes即可 # selected = self.sender().selectedIndexes() app = qt.QApplication(sys.argv) main_gui = Main() main_gui.show() sys.exit(app.exec())
该方案的优势:
- 无需自定义子类,代码量更少,维护成本低
- 原生信号自动处理了点击位置判断,点击表格空白区域不会触发逻辑
- 直接传入点击项索引,不需要额外遍历选中列表,单选场景下更高效
- 不存在事件调用时序问题,行为和Qt默认逻辑完全一致
内容的提问来源于stack exchange,提问作者malonn
相关产品推荐
相关产品推荐

