Search code examples
pythonqtpyqtqgraphicssceneselectionchanged

Using selectionChanged signal from QGraphicsScene to addRect around selected items


I am trying to draw a rect around the items that are selected in the scene (either via RubberBandDrag or ctrl+click for each item).

In order to do this I've subclassed QGraphicsScene and reimplemented the selectionChanged method to add a QGraphicsRectItem around the selected area, but for some reason, this method is not being called when items are selected or unselected in the scene. I've made sure that the items are in fact selectable.

Here is a minimal example of what I'm trying to do:

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

class DiagramScene(QGraphicsScene):

    def __init__(self, parent=None):
        super().__init__(parent)

        self.selRect = None

    def selectionChanged(self):
        area = self.selectionArea().boundingRect()
        pen = QPen()
        pen.setColor(Qt.black)
        pen.setStyle(Qt.DashLine)

        self.selRect = self.addRect(area, pen)


if __name__ == "__main__":

    app = QApplication(sys.argv)

    view = QGraphicsView()
    view.setDragMode(QGraphicsView.RubberBandDrag)

    scene = DiagramScene()

    scene.setSceneRect(0, 0, 500, 500)

    rect1 = scene.addRect(20, 20, 100, 50)
    rect2 = scene.addRect(80, 80, 100, 50)
    rect3 = scene.addRect(140, 140, 100, 50)

    rect1.setFlag(QGraphicsItem.ItemIsSelectable, True)
    rect2.setFlag(QGraphicsItem.ItemIsSelectable, True)
    rect3.setFlag(QGraphicsItem.ItemIsSelectable, True)

    view.setScene(scene)
    view.show()

    sys.exit(app.exec_())

Solution

  • selectionChanged is a signal, not a method that you have to implement. What you need to do is to connect this signal to slot and your the implementation in the slot, so whenever the signal is emitted, your code gets executed:

    from PyQt4.QtGui import *
    from PyQt4.QtCore import *
    import sys  
    
    class DiagramScene(QGraphicsScene):
    
        def __init__(self, parent=None):
            super().__init__(parent)
            self.selRect = None
            self.selectionChanged.connect(self.onSelectionChanged)
    
        @pyqtSlot()
        def onSelectionChanged(self):
            area = self.selectionArea().boundingRect()
            pen = QPen()
            pen.setColor(Qt.black)
            pen.setStyle(Qt.DashLine)
            self.selRect = self.addRect(area, pen)
    
    
    if __name__ == "__main__":
    
        app = QApplication(sys.argv)
    
        view = QGraphicsView()
        view.setDragMode(QGraphicsView.RubberBandDrag)
    
        scene = DiagramScene()
    
        scene.setSceneRect(0, 0, 500, 500)
    
        rect1 = scene.addRect(20, 20, 100, 50)
        rect2 = scene.addRect(80, 80, 100, 50)
        rect3 = scene.addRect(140, 140, 100, 50)
    
        rect1.setFlag(QGraphicsItem.ItemIsSelectable, True)
        rect2.setFlag(QGraphicsItem.ItemIsSelectable, True)
        rect3.setFlag(QGraphicsItem.ItemIsSelectable, True)
    
        view.setScene(scene)
        view.show()
    
        sys.exit(app.exec_())