How to catch mouse over event of QTableWidget item

2019-04-16 12:35发布

问题:

what I want to do is to change the color of a QTableWidget item, when I hover with the mouse over the item of my QTableWidget.

回答1:

Firstly, the table widget needs to have mouse-tracking switched on to get the hover events.

Secondly, we need to find some signals that tell us when the mouse enters and leaves the table cells, so that the background colours can be changed at the right times.

The QTableWidget class has the cellEntered / itemEntered signals, but there is nothing for when the mouse leaves a cell. So, we will need to create some custom signals to do that.

The TableWidget class in the demo script below sets up the necessary cellExited / itemExited signals, and then shows how everything can be hooked up to change the item background when hovering with the mouse:

from PyQt4 import QtGui, QtCore

class TableWidget(QtGui.QTableWidget):
    cellExited = QtCore.pyqtSignal(int, int)
    itemExited = QtCore.pyqtSignal(QtGui.QTableWidgetItem)

    def __init__(self, rows, columns, parent=None):
        QtGui.QTableWidget.__init__(self, rows, columns, parent)
        self._last_index = QtCore.QPersistentModelIndex()
        self.viewport().installEventFilter(self)

    def eventFilter(self, widget, event):
        if widget is self.viewport():
            index = self._last_index
            if event.type() == QtCore.QEvent.MouseMove:
                index = self.indexAt(event.pos())
            elif event.type() == QtCore.QEvent.Leave:
                index = QtCore.QModelIndex()
            if index != self._last_index:
                row = self._last_index.row()
                column = self._last_index.column()
                item = self.item(row, column)
                if item is not None:
                    self.itemExited.emit(item)
                self.cellExited.emit(row, column)
                self._last_index = QtCore.QPersistentModelIndex(index)
        return QtGui.QTableWidget.eventFilter(self, widget, event)

class Window(QtGui.QWidget):
    def __init__(self, rows, columns):
        QtGui.QWidget.__init__(self)
        self.table = TableWidget(rows, columns, self)
        for column in range(columns):
            for row in range(rows):
                item = QtGui.QTableWidgetItem('Text%d' % row)
                self.table.setItem(row, column, item)
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.table)
        self.table.setMouseTracking(True)
        self.table.itemEntered.connect(self.handleItemEntered)
        self.table.itemExited.connect(self.handleItemExited)

    def handleItemEntered(self, item):
        item.setBackground(QtGui.QColor('moccasin'))

    def handleItemExited(self, item):
        item.setBackground(QtGui.QTableWidgetItem().background())

if __name__ == '__main__':

    import sys
    app = QtGui.QApplication(sys.argv)
    window = Window(6, 3)
    window.setGeometry(500, 300, 350, 250)
    window.show()
    sys.exit(app.exec_())


回答2:

You can achieve your goal pretty easily using the proper signals as proved by the following simple code:

from PyQt4.QtGui import *
from PyQt4.QtCore import *

class TableViewer(QMainWindow):
    def __init__(self, parent=None):
        super(TableViewer, self).__init__(parent)
        self.table = QTableWidget(3, 3)
        for row in range (0,3):
            for column in range(0,3):
                item = QTableWidgetItem("This is cell {} {}".format(row+1, column+1))
                self.table.setItem(row, column, item)
        self.setCentralWidget(self.table)

        self.table.setMouseTracking(True)

        self.current_hover = [0, 0]
        self.table.cellEntered.connect(self.cellHover)

    def cellHover(self, row, column):
        item = self.table.item(row, column)
        old_item = self.table.item(self.current_hover[0], self.current_hover[1])
        if self.current_hover != [row,column]:
            old_item.setBackground(QBrush(QColor('white')))
            item.setBackground(QBrush(QColor('yellow')))
        self.current_hover = [row, column]


if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    tv = TableViewer()
    tv.show()
    sys.exit(app.exec_())

You may be interested in other signals too, especially itemEntered. However, if you want total control over the editing and display of items then using delegates (via the QTableWidget.setItemDelegate method) is strongly recommended.

UPDATE:

sorry, I had forgotten the second part of the problem i.e. what happens when the mouse exits a cell. Even then the problem can be solved easily without using events. See the updated code, please.



回答3:

There are no events based on QTableWidgetItem, but you can do this:

  • reimplement the mouseMoveEvent() of QTableWidget, you can get the mouse position;
  • use itemAt() method to get the item under your mouse cursor;
  • customize your item;

This may simalute what you want.