pyqt -Clear selection - no work

Hello, I have managed to make the circles change color when selecting them using ‘QRubberBand’, but when I try to clear the selection it does not work

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class Boton(QGraphicsItem):
    def __init__(self,parent,size,X,Y,name):
        super(Boton,self).__init__()
        self.setFlag(QGraphicsItem.ItemIsSelectable, True)        
        self.parent=parent
        self.globalRec=QRectF(0,0,size,size)
        self.setPos(X,Y)
        self.parent._scene.addItem(self)
    def cambi(self,val):
        if val ==0:
            self.parent.select=0
        else:
            self.parent.select=1
    def paint(self,painter,option,widget):
        if self.parent.select==0:
            painter.setBrush(self.parent.brush2)
        if self.parent.select==1:
            painter.setBrush(self.parent.brush1)       
        painter.setPen(self.parent.pen)
        painter.drawEllipse(self.globalRec)
    def boundingRect(self):
        return self.globalRec
class Viewer(QGraphicsView):
    rectChanged = pyqtSignal(QRect)
    def __init__(self, parent):
        super(Viewer, self).__init__(parent)
        self.select=0   
        self.misItems=None
        color2 = QColor(5,100,70)
        color3 = QColor(155,100,70)
        color1 = QColor(225,100,70)
        self.brush1=QBrush(color1)
        self.brush2=QBrush(color2)
        self.origin = QPoint()
        self.poi=QPoint()
        self.pen=QPen()        
        self.pen.setWidth(2)
        self.pen.setColor(color3)
        self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
        self.setMouseTracking(True)
        self.changeRubberBand = False
        self._scene = QGraphicsScene(self)
        self.setScene(self._scene)
        Boton(self,20,0,0,'btn1')
        Boton(self,20,0,80,'btn2')
        Boton(self,20,0,-80,'btn3')

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.origin = event.pos()
            self.rubberBand.setGeometry(QRect(self.origin, QSize()))
            self.rectChanged.emit(self.rubberBand.geometry())
            self.rubberBand.show()
            self.changeRubberBand = True
            return
        super(Viewer, self).mousePressEvent(event)
    def mouseMoveEvent(self, event):
        if self.changeRubberBand:
            self.rubberBand.setGeometry(
                QRect(self.origin, event.pos()).normalized())
            self.rectChanged.emit(self.rubberBand.geometry())
        super(Viewer, self).mouseMoveEvent(event)
    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.changeRubberBand = False
            if self.rubberBand.isVisible():
                self.rubberBand.hide()
                rect = self.rubberBand.geometry()
                rect_scene = self.mapToScene(rect).boundingRect()
                selected = self.scene().items(rect_scene)
#event SELECT items Buttons:
                if selected:
                    self.misItems=selected
                    for item in selected:
                        item.cambi(1)               
                else:
                    for item in self.misItems:
                        item.cambi(0)
        super(Viewer, self).mouseReleaseEvent(event)


class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.viewer = Viewer(self)
        VBlayout = QVBoxLayout(self)
        VBlayout.addWidget(self.viewer)
        VBlayout.setContentsMargins(0,0,0,0)
        self.setFixedSize(400,400)
if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

https://i.imgur.com/UxbOZg5.gif

Hey @srMnauel

The issue here is that you don’t notify the graphics item that something has changed & so it doesn’t know to redraw itself. You can fix this by adding a call to .update() in the cambi method.

    def cambi(self,val):
        if val ==0:
            self.parent.select=0
        else:
            self.parent.select=1
        self.update()

Also, note that when you create your Boton objects you’re passing in self which is stored as the .parent. This means that self.parent refers to the same objects in all the Boton items – you can’t select them individually(!) as they all share the selected state.

Currently if you try and select a single object it it’ll look like you can because only the rubber-band selected object calls .update() to refresh. But all the others are “selected” as well.

What you probably want to do is store the select state on the object itself, so self.select instead of self.parent.select. That said, you may also want to use Qt’s build in selected() state, which you can change using setSelected(). Below is an example (note, we no longer need the .update() because our call to setSelected() tells Qt the object has changed.

Finally, using self.misItems to store the selected items is fine, however you need to deselect all items always before applying a new selection. Otherwise you can drag over one item & select it, then drag over another item & select it. But then there is no way to deselect the first, as it is no longer in misItems.

QGraphicsScene provides a list of selected items via selectedItems() which makes this simpler. All we need to do is check if there are currently selected items, deselect them if so & then apply our new selection (if any).

                if self._scene.selectedItems():
                    self._scene.clearSelection()
                
#event SELECT items Buttons:
                # Deselect previously selected items.
                if selected:
                    self.misItems=selected
                    for item in selected:
                        item.cambi(True)      

The complete code is

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class Boton(QGraphicsItem):
    def __init__(self,parent,size,X,Y,name):
        super(Boton,self).__init__()
        self.setFlag(QGraphicsItem.ItemIsSelectable, True)        
        self.parent=parent
        self.globalRec=QRectF(0,0,size,size)
        self.setPos(X,Y)
        self.parent._scene.addItem(self)
    def cambi(self,val):
        self.setSelected(val)
    def paint(self,painter,option,widget):
        if not self.isSelected():
            painter.setBrush(self.parent.brush2)
        if self.isSelected():
            painter.setBrush(self.parent.brush1)       
        painter.setPen(self.parent.pen)
        painter.drawEllipse(self.globalRec)
    def boundingRect(self):
        return self.globalRec
class Viewer(QGraphicsView):
    rectChanged = pyqtSignal(QRect)
    def __init__(self, parent):
        super(Viewer, self).__init__(parent)
        self.select=0   
        self.misItems=None
        color2 = QColor(5,100,70)
        color3 = QColor(155,100,70)
        color1 = QColor(225,100,70)
        self.brush1=QBrush(color1)
        self.brush2=QBrush(color2)
        self.origin = QPoint()
        self.poi=QPoint()
        self.pen=QPen()        
        self.pen.setWidth(2)
        self.pen.setColor(color3)
        self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
        self.setMouseTracking(True)
        self.changeRubberBand = False
        self._scene = QGraphicsScene(self)
        self.setScene(self._scene)
        Boton(self,20,0,0,'btn1')
        Boton(self,20,0,80,'btn2')
        Boton(self,20,0,-80,'btn3')

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.origin = event.pos()
            self.rubberBand.setGeometry(QRect(self.origin, QSize()))
            self.rectChanged.emit(self.rubberBand.geometry())
            self.rubberBand.show()
            self.changeRubberBand = True
            return
        super(Viewer, self).mousePressEvent(event)
    def mouseMoveEvent(self, event):
        if self.changeRubberBand:
            self.rubberBand.setGeometry(
                QRect(self.origin, event.pos()).normalized())
            self.rectChanged.emit(self.rubberBand.geometry())
        super(Viewer, self).mouseMoveEvent(event)
    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.changeRubberBand = False
            if self.rubberBand.isVisible():
                self.rubberBand.hide()
                rect = self.rubberBand.geometry()
                rect_scene = self.mapToScene(rect).boundingRect()
                selected = self.scene().items(rect_scene)
                
                if self._scene.selectedItems():
                    self._scene.clearSelection()
                
#event SELECT items Buttons:
                # Deselect previously selected items.
                if selected:
                    self.misItems=selected
                    for item in selected:
                        item.cambi(True)               
        super(Viewer, self).mouseReleaseEvent(event)


class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.viewer = Viewer(self)
        VBlayout = QVBoxLayout(self)
        VBlayout.addWidget(self.viewer)
        VBlayout.setContentsMargins(0,0,0,0)
        self.setFixedSize(400,400)
if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())