Search code examples
pythonpyqtpyqt5qgraphicssceneqgraphicsitem

Hover event while clicking in PyQt


In my application I have a QGraphicsScene where the user should be able to change the color of items by having the mouse button clicked and hover over the items.

Below is an example code which I borrowed from another question:

PyQt: hover and click events for graphicscene ellipse

from PyQt5 import QtGui, QtCore, QtWidgets

class MyFrame(QtWidgets.QGraphicsView):
    def __init__( self, parent = None ):
        super(MyFrame, self).__init__(parent)

        self.setScene(QtWidgets.QGraphicsScene())

        # add some items
        x = 0
        y = 0
        w = 15
        h = 15
        pen = QtGui.QPen(QtGui.QColor(QtCore.Qt.green))
        brush = QtGui.QBrush(pen.color().darker(150))

        # i want a mouse over and mouse click event for this ellipse
        for xi in range(3):
            for yi in range(3):
                item = callbackRect(x+xi*30, y+yi*30, w, h)
                item.setAcceptHoverEvents(True)
                item.setPen(pen)
                item.setBrush(brush)
                self.scene().addItem(item)
                item.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable)

class callbackRect(QtWidgets.QGraphicsRectItem):
    '''
    Rectangle call-back class.
    '''

    def mouseReleaseEvent(self, event):
        # recolor on click
        color = QtGui.QColor(180, 174, 185)
        brush = QtGui.QBrush(color)
        QtWidgets.QGraphicsRectItem.setBrush(self, brush)

        return QtWidgets.QGraphicsRectItem.mouseReleaseEvent(self, event)

    def hoverMoveEvent(self, event):
        # Do your stuff here.
        pass

    def hoverEnterEvent(self, event):
        color = QtGui.QColor(0, 174, 185)
        brush = QtGui.QBrush(color)
        QtWidgets.QGraphicsRectItem.setBrush(self, brush)

    def hoverLeaveEvent(self, event):
        color = QtGui.QColor(QtCore.Qt.green)
        brush = QtGui.QBrush(color.darker(150))
        QtWidgets.QGraphicsRectItem.setBrush(self, brush)

if ( __name__ == '__main__' ):
    app = QtWidgets.QApplication([])
    f = MyFrame()
    f.show()
    app.exec_()

So, in this code the hovering methods are only called when there is no mouse button pressed. As stated in the documentation (for PySide) the mousePressEvent "decides which graphics item it is that receives mouse events" which in some way blocks mouse events for other items.

https://deptinfo-ensip.univ-poitiers.fr/ENS/pyside-docs/PySide/QtGui/QGraphicsItem.html?highlight=graphicsitem#PySide.QtGui.PySide.QtGui.QGraphicsItem.mouseMoveEvent

However, is there a way to simultaneously hold the mouse button pressed and call hover events of different items?


Solution

  • The problem is the combination of events that makes the task complicated, you can propagate the mouseMoveEvent event but you can not do the same with hover events. A simple solution is to implement the logic in the method mouseMoveEvent of QGraphicsView as shown below:

    class MyFrame(QtWidgets.QGraphicsView):
        def __init__( self, parent = None ):
            super(MyFrame, self).__init__(parent)    
            self.setScene(QtWidgets.QGraphicsScene())
            [...]
    
        itemsSelected = []
        def mouseMoveEvent(self, event):
            QtWidgets.QGraphicsView.mouseMoveEvent(self, event)
            items = self.items(event.pos())#, QtGui.QTransform())
            for item in self.itemsSelected:
                if item in items:
                    item.enterColor()
                else:
                    item.leaveColor()
            self.itemsSelected = items
    
    class callbackRect(QtWidgets.QGraphicsRectItem):
        '''
        Rectangle call-back class.
        '''
        def enterColor(self):
            color = QtGui.QColor(0, 174, 185)
            brush = QtGui.QBrush(color)
            QtWidgets.QGraphicsRectItem.setBrush(self, brush)
    
        def leaveColor(self):
            color = QtGui.QColor(QtCore.Qt.green)
            brush = QtGui.QBrush(color.darker(150))
            QtWidgets.QGraphicsRectItem.setBrush(self, brush)
    
        def hoverEnterEvent(self, event):
            self.enterColor()
    
        def hoverLeaveEvent(self, event):
            self.leaveColor()