pythonpyqt5qstyleditemdelegate

PyQt5 : State_MouseOver in QStyledItemDelegate just inside QtableView


Sorry, I couldn't think of a better title.

I have a QStyledItemDelegate that sets the MousePointer to QtCore.Qt.PointingHandCursor when I hover over column 8. The problem is that when I exit column 8 above the table header, the cursor remains QtCore.Qt.PointingHandCursor, but my thought was that the cursor should be restored by QtWidgets.QApplication.restoreOverrideCursor().

My delegate:

from PyQt5 import QtWidgets
from PyQt5 import QtGui, QtCore

class reportDelegate(QtWidgets.QStyledItemDelegate):
    def initStyleOption(self, option, index):
        super(reportDelegate, self).initStyleOption(option, index)

        if option.state & QtWidgets.QStyle.State_MouseOver:
            if option.index.column() == 8 and option.index.row() >=0:
                if option.text:
                    QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
                else:
                    QtWidgets.QApplication.restoreOverrideCursor()
            else:
                QtWidgets.QApplication.restoreOverrideCursor()

Solution

  • You cannot do that, and it won't work for three reasons:

    1. in your implementation, the restoreOverrideCursor() only happens when the option state is "under mouse";
    2. delegates are only "triggered" for existing items, if the mouse has to change cursor, that has to happen no matter if the mouse is over a valid index (or, better, a valid index with a delegate) or not;
    3. setOverrideCursor() is application-wide, but for a case such as this you should only set the cursor for the widget, especially because setOverrideCursor() use an internal stack of cursors, so if you call setOverrideCursor() twice and restoreOverrideCursor() only once, the "original" cursor won't be restored;

    This clearly means one thing: the implementation cannot happen in the delegate, but in the view, and it should set the cursor for the view (or, to be precise, its viewport), not for the application.

    Since Qt item views normally accept hover events, the solution is quite simple: override the view's event() and react to HoverMove events:

    class TableWidget(QTableWidget):
        def event(self, event):
            if event.type() == event.HoverMove:
                index = self.indexAt(self.viewport().mapFromParent(event.pos()))
                if index.column() == 7:
                    self.viewport().setCursor(Qt.PointingHandCursor)
                else:
                    self.viewport().unsetCursor()
            return super().event(event)
    

    This can also be achieved for views created within Designer, either by installing an event filter or by using a promoted widget. Please do some research on those subjects to understand how to achieve that.