如何通过右键单击 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 标签不同。
我正在使用 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 标签不同。