如何在Qtablewidget Python中设置自定义键盘按键事件?

如何在Qtablewidget Python中设置自定义键盘按键事件?,python,python-3.x,pyqt,pyqt5,qtablewidget,Python,Python 3.x,Pyqt,Pyqt5,Qtablewidget,我有一个由QTDesigner生成的QTableWidget,我想在其中设置一些自定义按键事件 当当前单元格处于编辑模式时,若用户按Tab键,它将在编辑模式下移动到下一个单元格,但我只希望它移动到选定的下一个单元格,而不是在编辑模式下 当前单元格处于编辑模式时,如果用户按左、右、上、下键,则应再次在选择模式下移动到相应单元格,而不是在编辑模式下 如果当前单元格刚刚被选中,而不是处于编辑模式,这两种方法现在都可以很好地工作。但如何设置此自定义事件 谢谢 UI设计器生成的代码: from PyQt5

我有一个由QTDesigner生成的QTableWidget,我想在其中设置一些自定义按键事件

  • 当当前单元格处于编辑模式时,若用户按Tab键,它将在编辑模式下移动到下一个单元格,但我只希望它移动到选定的下一个单元格,而不是在编辑模式下

  • 当前单元格处于编辑模式时,如果用户按左、右、上、下键,则应再次在选择模式下移动到相应单元格,而不是在编辑模式下

  • 如果当前单元格刚刚被选中,而不是处于编辑模式,这两种方法现在都可以很好地工作。但如何设置此自定义事件

    谢谢

    UI设计器生成的代码:

    from PyQt5 import QtCore, QtGui, QtWidgets
    class Ui_MainWindow(object):
        def setupUi(self, MainWindow):
            MainWindow.setObjectName("MainWindow")
            MainWindow.resize(428, 285)
            self.centralwidget = QtWidgets.QWidget(MainWindow)
            self.centralwidget.setObjectName("centralwidget")
            self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)
            self.tableWidget.setGeometry(QtCore.QRect(20, 20, 391, 231))
            self.tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.AnyKeyPressed|QtWidgets.QAbstractItemView.DoubleClicked|QtWidgets.QAbstractItemView.EditKeyPressed|QtWidgets.QAbstractItemView.SelectedClicked)
            self.tableWidget.setRowCount(5)
            self.tableWidget.setColumnCount(3)
            self.tableWidget.setObjectName("tableWidget")
            item = QtWidgets.QTableWidgetItem()
            self.tableWidget.setHorizontalHeaderItem(0, item)
            item = QtWidgets.QTableWidgetItem()
            self.tableWidget.setHorizontalHeaderItem(1, item)
            item = QtWidgets.QTableWidgetItem()
            self.tableWidget.setHorizontalHeaderItem(2, item)
            self.tableWidget.horizontalHeader().setVisible(True)
            self.tableWidget.verticalHeader().setVisible(False)
            MainWindow.setCentralWidget(self.centralwidget)
            self.statusbar = QtWidgets.QStatusBar(MainWindow)
            self.statusbar.setObjectName("statusbar")
            MainWindow.setStatusBar(self.statusbar)
    
            self.retranslateUi(MainWindow)
            QtCore.QMetaObject.connectSlotsByName(MainWindow)
    
        def retranslateUi(self, MainWindow):
            _translate = QtCore.QCoreApplication.translate
            MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
            item = self.tableWidget.horizontalHeaderItem(0)
            item.setText(_translate("MainWindow", "Name"))
            item = self.tableWidget.horizontalHeaderItem(1)
            item.setText(_translate("MainWindow", "Age"))
            item = self.tableWidget.horizontalHeaderItem(2)
            item.setText(_translate("MainWindow", "City"))
    
    
    if __name__ == "__main__":
        import sys
        app = QtWidgets.QApplication(sys.argv)
        MainWindow = QtWidgets.QMainWindow()
        ui = Ui_MainWindow()
        ui.setupUi(MainWindow)
        MainWindow.show()
        sys.exit(app.exec_())
    
    from PyQt5 import QtWidgets, QtCore
    from demo import Ui_MainWindow
    
    class DemoTable(QtWidgets.QMainWindow, Ui_MainWindow):                 
        def __init__(self):
            super(DemoTable, self).__init__()
            self.setupUi(self) 
    
            #KeyPressEvent
            self.tableWidget.keyPressEvent = self.KeyPressed
    
        def KeyPressed(self,event):
            if event.key() == QtCore.Qt.Key_Left:
                print('Left Key Pressed')
            elif event.key() == QtCore.Qt.Key_Right:
                print('Right Key Pressed')
            elif event.key() == QtCore.Qt.Key_Tab:
                print('Tab Key Pressed')
            return QtWidgets.QTableWidget.keyPressEvent(self.tableWidget, event)
    
    if __name__ == "__main__":
        import sys
        app = QtWidgets.QApplication(sys.argv)
        demowindow = DemoTable()   
        demowindow.show()
        sys.exit(app.exec_())
    
    我的脚本:

    from PyQt5 import QtCore, QtGui, QtWidgets
    class Ui_MainWindow(object):
        def setupUi(self, MainWindow):
            MainWindow.setObjectName("MainWindow")
            MainWindow.resize(428, 285)
            self.centralwidget = QtWidgets.QWidget(MainWindow)
            self.centralwidget.setObjectName("centralwidget")
            self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)
            self.tableWidget.setGeometry(QtCore.QRect(20, 20, 391, 231))
            self.tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.AnyKeyPressed|QtWidgets.QAbstractItemView.DoubleClicked|QtWidgets.QAbstractItemView.EditKeyPressed|QtWidgets.QAbstractItemView.SelectedClicked)
            self.tableWidget.setRowCount(5)
            self.tableWidget.setColumnCount(3)
            self.tableWidget.setObjectName("tableWidget")
            item = QtWidgets.QTableWidgetItem()
            self.tableWidget.setHorizontalHeaderItem(0, item)
            item = QtWidgets.QTableWidgetItem()
            self.tableWidget.setHorizontalHeaderItem(1, item)
            item = QtWidgets.QTableWidgetItem()
            self.tableWidget.setHorizontalHeaderItem(2, item)
            self.tableWidget.horizontalHeader().setVisible(True)
            self.tableWidget.verticalHeader().setVisible(False)
            MainWindow.setCentralWidget(self.centralwidget)
            self.statusbar = QtWidgets.QStatusBar(MainWindow)
            self.statusbar.setObjectName("statusbar")
            MainWindow.setStatusBar(self.statusbar)
    
            self.retranslateUi(MainWindow)
            QtCore.QMetaObject.connectSlotsByName(MainWindow)
    
        def retranslateUi(self, MainWindow):
            _translate = QtCore.QCoreApplication.translate
            MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
            item = self.tableWidget.horizontalHeaderItem(0)
            item.setText(_translate("MainWindow", "Name"))
            item = self.tableWidget.horizontalHeaderItem(1)
            item.setText(_translate("MainWindow", "Age"))
            item = self.tableWidget.horizontalHeaderItem(2)
            item.setText(_translate("MainWindow", "City"))
    
    
    if __name__ == "__main__":
        import sys
        app = QtWidgets.QApplication(sys.argv)
        MainWindow = QtWidgets.QMainWindow()
        ui = Ui_MainWindow()
        ui.setupUi(MainWindow)
        MainWindow.show()
        sys.exit(app.exec_())
    
    from PyQt5 import QtWidgets, QtCore
    from demo import Ui_MainWindow
    
    class DemoTable(QtWidgets.QMainWindow, Ui_MainWindow):                 
        def __init__(self):
            super(DemoTable, self).__init__()
            self.setupUi(self) 
    
            #KeyPressEvent
            self.tableWidget.keyPressEvent = self.KeyPressed
    
        def KeyPressed(self,event):
            if event.key() == QtCore.Qt.Key_Left:
                print('Left Key Pressed')
            elif event.key() == QtCore.Qt.Key_Right:
                print('Right Key Pressed')
            elif event.key() == QtCore.Qt.Key_Tab:
                print('Tab Key Pressed')
            return QtWidgets.QTableWidget.keyPressEvent(self.tableWidget, event)
    
    if __name__ == "__main__":
        import sys
        app = QtWidgets.QApplication(sys.argv)
        demowindow = DemoTable()   
        demowindow.show()
        sys.exit(app.exec_())
    
    您可以重新实现该方法,该方法负责完成项目编辑后项目视图将采取的操作

    在本例中,我将像处理keypress事件一样覆盖该方法,但我强烈建议您使用子类并在其中实现覆盖,因为代码将更简洁,可以避免现有函数和/或覆盖方法之间的混淆

    class DemoTable(QtWidgets.QMainWindow, Ui_MainWindow):                 
        def __init__(self):
            super(DemoTable, self).__init__()
            self.setupUi(self) 
    
            #KeyPressEvent
            self.tableWidget.keyPressEvent = self.KeyPressed
            self.tableWidget.closeEditor = self.closeEditor
    
        def closeEditor(self, editor, hint):
            if hint in (QtWidgets.QItemDelegate.EditNextItem, 
                QtWidgets.QItemDelegate.EditPreviousItem):
                    # if the hint is to edit the next or previous item, ignore it
                    newHint = QtWidgets.QItemDelegate.NoHint
            else:
                newHint = hint
    
            # call the base implementation with the new hint
            QtWidgets.QTableWidget.closeEditor(self.tableWidget, editor, newHint)
    
            if hint == QtWidgets.QItemDelegate.EditNextItem:
                # find the next item to focus on
                index = self.tableWidget.moveCursor(self.tableWidget.MoveNext, 
                    QtCore.Qt.NoModifier)
            elif hint  == QtWidgets.QItemDelegate.EditPreviousItem:
                # find the previous item to focus on
                index = self.tableWidget.moveCursor(self.tableWidget.MovePrevious, 
                    QtCore.Qt.NoModifier)
            else:
                return
            # set the new current item
            self.tableWidget.setCurrentIndex(index)
    
        # ...
    
    由于您甚至在编辑状态下也希望使用箭头键移动到项目,因此只能通过安装自定义项目委托来执行此操作,该委托检查编辑器中的键盘事件

    不过,我必须警告你:不要这样做
    编辑文本时,左右箭头键始终用于光标导航,绝对不鼓励更改此行为。
    它不直观、不自然、不舒服,不符合任何常见和预期的行为,会让习惯于键盘导航的用户非常、非常、非常恼火。我在过去的一个节目中看到过类似的行为,我可以告诉你,这不好,而且非常令人恼火

    class DelegateYouShouldNotUse(QtWidgets.QStyledItemDelegate):
        def eventFilter(self, source, event):
            if isinstance(source, QtWidgets.QLineEdit) and event.type() == QtCore.QEvent.KeyPress:
                if event.key() == QtCore.Qt.Key_Right:
                    # tell the view to store the current data
                    self.commitData.emit(source)
                    # and to move to the next item (since we've changed the
                    # behavior of the closeEditor *slot* of the table, it
                    # will only move to the item, without starting editing
                    self.closeEditor.emit(source, self.EditNextItem)
                    return True
                elif event.key() == QtCore.Qt.Key_Left:
                    self.commitData.emit(source)
                    self.closeEditor.emit(source, self.EditPreviousItem)
                    return True
            return super().eventFilter(source, event)
    
    
    class DemoTable(QtWidgets.QMainWindow, Ui_MainWindow):                 
        def __init__(self):
            super(DemoTable, self).__init__()
            # ...
            self.veryBadDelegate = DelegateYouShouldNotUse(self.tableWidget)
            self.tableWidget.setItemDelegate(self.veryBadDelegate)
    

    如果不够清晰,您应该不要使用此:-)

    在第二点上,我已经测试并观察到以下情况:如果当前项目处于选择模式,并且我按下向上或向下键,则分别选择顶部或底部项目,而不启用编辑,但在向左或向右按键的情况下,什么都不会发生。据我所知,您希望上下键的行为与左右键相同,对吗?我想知道当单元格处于编辑模式时左右键的上下键的工作方式。谢谢@音乐大师。当您按tab键时,此操作有效。它将移动到选定的下一个单元格。但它似乎不适用于let和right键。@pyvk你是什么意思?在编辑模式下按箭头时是否要移动到单元格?我真的不会这么做,因为这违背了可编辑文本小部件的预期行为(左箭头键和右箭头键应始终移动文本光标)。如果您真的想这样做,您需要创建一个自定义委托,并在请求创建时在编辑器上安装一个事件过滤器,但我强烈建议您不要这样做,这会使文本编辑行为变得不直观,而且很多。是的,我完全理解您在这里试图传达的内容。但对于这个特定的需求,如果可能的话,我至少需要这样做?@pyvk查看更新。但是,说真的,不要这样做:-)