Search code examples
qtpyqtselectionqgraphicssceneqgraphicsitem

How to stop a middle mouse click from deselecting items in a QGraphicsScene without blocking it from items in the scene?


I am creating a node graph and I want to be able to click the empty space in a scene and middle-mouse-drag to navigate without deselecting the currently selected items in the scene. Any suggestions?

I can block the middle click in the view's mousePressEvent and get the right behavior but then I no longer have middle-mouse click events working on items in the scene. I don't mind a middle-click resulting in a single selection when clicking on an item in the scene, but if I middle-click the empty space in the scene I don't want the selection altered.

This didn't cover the more complex behavior I am looking for: PyQt. How to block clear selection on mouse right click?

I didn't try using an eventFilter as I assume the issue would be the same

I am using PyQt/PySide, FWIW.

Before I roll my own workaround I thought I'd post here for the correct way or at least other workaround ideas.

Some workaround ideas:

  • Block the mousePressEvent to the scene but iterate over child items to deliver it directly
  • Restore selection while still in the mousePressEvent in the scene. Probably bad for performance at scale but simple I suppose.

Any feedback would be great!

[Edit:] Here is my python version of the answer. Code tested. In my QGraphicsScene derived class:

def mousePressEvent(self, event):
    # Prevent the QGraphicsScene default behavior to deselect-all when clicking on 
    # empty space by blocking the event in this circumstance. 
    item_under_the_mouse = self.itemAt(event.scenePos())
    if event.button() == QtCore.Qt.MidButton and not item_under_the_mouse:
        event.accept()
    else:
        super(GraphScene, self).mousePressEvent(event)

Solution

  • In your QGraphicsScene::mousePressEvent derived implementation, if it's a middle mouse click, check for items under the mouse click. If there aren't any, then accept the event and don't call the base class implementation. If something is under the click, then just call the base implementation; you don't have to try to reimplement that yourself. I think this is the general idea:

    void MyScene::mousePressEvent (QGraphicsSceneMouseEvent *evt)
    {
        if ((evt->buttons () & Qt::MidButton) && items (evt->scenePos ().count ())
        {
            QGraphicsScene::mousePressEvent (evt);
        }
        else
        {
            evt->accept ();
        }
    }
    

    I'm not sure if the accept is required in this case or not. I haven't compiled or tested this, but hopefully, it's helpful to get you going in the right direction.