如何通过右键单击 QTableView 获取行号?

How to get the row number from a rigth-click on a QTableView?

我正在使用 PyQt5 开发应用程序,但无法找到如何将右键单击 QTableView 的位置转换为行号。我实现它的方式是在 QPoint 光标位置使用 rowAt 方法,我得到的行号看起来与被单击的实际行号有偏移。我一直在环顾四周,但找不到任何明确的解决方案。听起来好像与滚动区域有关。下面是我的代码示例。

用Qt Designer生成的UI代码:

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.setWindowModality(QtCore.Qt.NonModal)
        MainWindow.resize(941, 767)
        MainWindow.setMinimumSize(QtCore.QSize(922, 767))
        MainWindow.setMaximumSize(QtCore.QSize(1900, 1200))
        MainWindow.setAutoFillBackground(False)
        MainWindow.setAnimated(False)
        self.MyWindows = QtWidgets.QWidget(MainWindow)
        self.MyWindows.setEnabled(True)
        self.MyWindows.setMinimumSize(QtCore.QSize(921, 726))
        self.MyWindows.setMaximumSize(QtCore.QSize(1900, 1200))
        self.MyWindows.setObjectName("MyWindows")
        self.layoutWidget = QtWidgets.QWidget(self.MyWindows)
        self.layoutWidget.setGeometry(QtCore.QRect(10, 0, 911, 441))
        self.layoutWidget.setObjectName("layoutWidget")
        self.gridLayout = QtWidgets.QGridLayout(self.layoutWidget)
        self.gridLayout.setContentsMargins(0, 0, 0, 0)
        self.gridLayout.setObjectName("gridLayout")
        self.tableView = QtWidgets.QTableView(self.layoutWidget)
        self.tableView.setEnabled(True)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(1)
        sizePolicy.setVerticalStretch(1)
        sizePolicy.setHeightForWidth(self.tableView.sizePolicy().hasHeightForWidth())
        self.tableView.setSizePolicy(sizePolicy)
        self.tableView.setMinimumSize(QtCore.QSize(900, 400))
        font = QtGui.QFont()
        font.setPointSize(10)
        self.tableView.setFont(font)
        self.tableView.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
        self.tableView.setAlternatingRowColors(True)
        self.tableView.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
        self.tableView.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
        self.tableView.setGridStyle(QtCore.Qt.NoPen)
        self.tableView.setSortingEnabled(False)
        self.tableView.setObjectName("tableView")
        self.tableView.horizontalHeader().setCascadingSectionResizes(True)
        self.tableView.horizontalHeader().setSortIndicatorShown(False)
        self.tableView.horizontalHeader().setStretchLastSection(True)
        self.tableView.verticalHeader().setVisible(True)
        self.tableView.verticalHeader().setCascadingSectionResizes(True)
        self.tableView.verticalHeader().setHighlightSections(True)
        self.tableView.verticalHeader().setSortIndicatorShown(False)
        self.tableView.verticalHeader().setStretchLastSection(True)
        self.gridLayout.addWidget(self.tableView, 1, 1, 1, 1)
        MainWindow.setCentralWidget(self.MyWindows)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Thib Imave Smoother V0.2"))

主要代码:

from PyQt5.QtCore import *
from PyQt5.QtWidgets import QMainWindow, QSizePolicy, QHeaderView, QFileSystemModel, QMenu, QAction
# QtDesigner generated code imports
from UI import *

class ShowTableView:

    def __init__(self, guiObject):
        self._gui = guiObject  # store QWidget from Gui class
        #initialize fake data for testing
        self.my_data = [["test" for x in range(7)] for x in range(5000)]
        self.tm = MyTableModel(self.my_data)
        self._gui.tableView.setModel(self.tm)
        header = self._gui.tableView.horizontalHeader()
        header.setSectionResizeMode(QHeaderView.ResizeToContents)  # have columns width adjusted to content

class MyTableModel(QAbstractTableModel):
    def __init__(self, my_data, parent=None, *args):
        QAbstractTableModel.__init__(self, parent)
        self.my_data = my_data
        self.title_list = ["on","two","three","four","five","six","seven"]

    def rowCount(self, parent):
        return len(self.my_data)

    def columnCount(self, parent):
        return len(self.my_data[0])

    def data(self, index, role):
        if not index.isValid():
            return QVariant()
        elif role != Qt.DisplayRole:
            return QVariant()
        else: #
            return QVariant(self.my_data[index.row()][index.column()])

    def headerData(self, col, orientation, role):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return QVariant(self.title_list[col])
        elif orientation == Qt.Vertical and role == Qt.DisplayRole:
            return QVariant(col + 1)

class Gui(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(Gui, self).__init__(parent)
        self.setupUi(self)
        self.myTable = ShowTableView(self)

    def contextMenuEvent(self, QContextMenuEvent):
        self.menu = QMenu(self)
        removeAction = QAction('Remove', self)
        self.menu.addAction(removeAction)
        removeAction.triggered.connect(lambda: self.remove_row_from_rigth_click(QtGui.QCursor.pos()))
        self.menu.popup(QtGui.QCursor.pos())

    def remove_row_from_rigth_click(self, event,q_point):
        row = self.tableView.rowAt(q_point.y())
        print("row =" + str(row))  # HERE I GET WRONG VALUE WITH AN "RANDOM" OFFSET - IF I CLICK ROW 10 I GET 16 !

if __name__ == "__main__":

    app = QtWidgets.QApplication(sys.argv)
    ui = Gui()
    ui.show()
    sys.exit(app.exec_())

rowAt method uses local coordinates, whereas you are using global coordinates. So you must use mapFromGlobal得到正确的y值:

class Gui(QMainWindow, Ui_MainWindow):
    ...
    def contextMenuEvent(self, event):
        ...
        pos = event.globalPos()
        removeAction.triggered.connect(
            lambda: self.remove_row_from_rigth_click(pos))
        self.menu.popup(pos)

    def remove_row_from_rigth_click(self, q_point):
        row = self.tableView.rowAt(
            self.tableView.viewport().mapFromGlobal(q_point).y())
        print("row =" + str(row))

请注意,返回的行是 zero-based,这与垂直 header 标签不同。