PySide 1.2中QTableView带水平表头时indexAt返回索引异常问询
嘿,这个情况绝对不属于预期行为,本质是你直接用visualRect处理坐标时,没分清视图坐标和模型坐标的转换逻辑——当水平表头显示时,visualRect会把表头区域也算进视图的坐标体系里,才导致索引错位、最后一行失效的问题。
问题出在哪?
在PySide 1.2里,QTableView.visualRect()返回的是项在**整个视图控件(包含表头)**里的绘制矩形。如果你自己通过点击坐标去反向匹配这个矩形,表头的高度会让第一行数据的y坐标整体下移,导致你误以为点击的是row=1的项,但它实际对应的模型索引是row=0;最后一行的索引失效也是因为这个偏移,让你判断坐标时没覆盖到最后一行的区域。
比如你可能是这么写的(补全你没写完的代码):
class CustomTableView(QTableView): def __init__(self, parent=None): super(CustomTableView, self).__init__(parent) self.model = QStandardItemModel(5, 3) self.setModel(self.model) # 填充测试数据 for row in range(5): for col in range(3): self.model.setItem(row, col, QStandardItem(f"Item {row}-{col}")) self.horizontalHeader().show() def mousePressEvent(self, event): pos = event.pos() # 错误的做法:自己遍历匹配visualRect for row in range(self.model.rowCount()): for col in range(self.model.columnCount()): rect = self.visualRect(self.model.index(row, col)) if rect.contains(pos): print(f"错误索引:({row}, {col})") break
这种写法在显示表头时,第一行数据的visualRect的y坐标是表头高度,点击第一行时会匹配到row=1,完全错位。
正确的解决姿势
别自己折腾坐标匹配了,直接用QTableView.indexAt(QPoint)方法!这个方法内部已经处理了视图和模型的坐标映射,会自动忽略表头区域,返回的索引直接对应模型里的正确位置,不管表头显示与否都没问题。
修正后的代码:
class CustomTableView(QTableView): def __init__(self, parent=None): super(CustomTableView, self).__init__(parent) self.model = QStandardItemModel(5, 3) self.setModel(self.model) for row in range(5): for col in range(3): self.model.setItem(row, col, QStandardItem(f"Row {row}, Col {col}")) # 随便切换显示/隐藏表头,结果都正确 self.horizontalHeader().show() def mousePressEvent(self, event): super(CustomTableView, self).mousePressEvent(event) # 获取正确的模型索引 target_index = self.indexAt(event.pos()) if target_index.isValid(): print(f"正确索引:行{target_index.row()},列{target_index.column()}") else: print("点击到了表头或者空白区域哦")
额外提一句
如果确实需要用visualRect来做一些绘制相关的操作,记得它返回的是视图坐标系下的矩形,而模型索引对应的是数据本身的位置,两者之间的转换可以用viewport()来获取视图的可视区域坐标——比如self.viewport().mapFromGlobal(event.globalPos()),这样就能排除表头的影响,不过一般来说,获取点击索引用indexAt()就足够了。
内容的提问来源于stack exchange,提问作者paul c

