Search code examples
pythonpyqtpyqt5qgraphicssceneqgraphicsitem

Update the Opacity of QGraphicsItem


I want to update the opacity of some QGraphicsItem after the mouse clicking. As suggested from other solution, the QGraphicScene manually update the GraphicsItem after the mouser press event. I have tried different setOpacity() and update() in QGraphicsScene and QGraphicsItem. But none works and do not know what is wrong.

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

CUBE_POS = {
    "a":(   8.281,  18.890),
    "b":(   8.668,  23.692),
    "c":(   21.493, 23.423),
    "d":(   21.24,  15.955),
    }        

class CubeItem(QGraphicsItem):

    def __init__(self, x, y, parent=None):
        super(CubeItem,self).__init__(parent)
        self.x = x
        self.y = y
        self.polygon = QPolygonF([
            QPointF(self.x-10, self.y-10), QPointF(self.x-10, self.y+10),
            QPointF(self.x+10, self.y+10), QPointF(self.x+10, self.y-10),
            ])
        self._painter = QPainter()

    ##Estimate the drawing area
    def boundingRect(self):
        return QRectF(self.x-10, self.y-10, 20, 20)

    ##Real Shape of drawing area
    def shape(self):
        path = QPainterPath()
        path.addRect(self.x-10, self.y-10, 20, 20)
        return path

    ##paint function called by graphicview
    def paint(self, painter, option, widget):
        painter.setBrush(Qt.red)
        painter.setOpacity(0.2)
        painter.drawRect(self.x-10, self.y-10, 20, 20)
        self._painter = painter

    def activate(self):
        try:
            #self._painter.setOpacity(1.0)
            self.setOpacity(1.0)
            self.update()
        except ValueError as e:
            print(e)

class TagScene(QGraphicsScene):

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

        self.cubes_items_ref = {}
        self.addCubes()

    def addCubes(self):
        for cube in CUBE_POS:
            newCube = CubeItem(CUBE_POS[cube][0]*15, 
                                   CUBE_POS[cube][1]*15)
            self.addItem(newCube)
            self.cubes_items_ref[cube] = newCube

    def mousePressEvent(self, event):
        print("mouse pressed")

        #for cube in self.cubes_items_ref:
        #    self.cubes_items_ref[cube].setOpacity(1.0)
        #    #self.cubes_items_ref[cube].activate()
        #self.update(QRectF(0,0,500,500))

        for cube in self.items():
            cube.setOpacity(1.0)
        self.update(QRectF(0,0,500,500))

class MainWindow(QMainWindow):

     def __init__(self):
        super(MainWindow, self).__init__()
        layout = QHBoxLayout()
        self.scene = TagScene()
        self.view = QGraphicsView(self.scene)
        self.scene.setSceneRect(QRectF(0,0,500,500))
        layout.addWidget(self.view)
        self.widget = QWidget()
        self.widget.setLayout(layout)
        self.setCentralWidget(self.widget)

if __name__ == "__main__":

    app = QApplication(sys.argv)
    test = MainWindow()
    test.show()
    sys.exit(app.exec_())

Solution

  • The problem is that when you overwrite the paint method of the QGraphicsItem you are setting a constant opacity

    def paint(self, painter, option, widget):
        painter.setBrush(Qt.red)
        painter.setOpacity(0.2) # <-- this line is the problem
        painter.drawRect(self.x-10, self.y-10, 20, 20)
        self._painter = painter
    

    And you will not use the opacity that the QPainter already passes paint() method.

    If you want to set an initial opacity you must do it in the constructor.On the other hand the setOpacity() method already calls update() so it is not necessary to make an explicit call.

    import sys
    from PyQt5 import QtCore, QtGui, QtWidgets
    
    CUBE_POS = {
        "a": (8.281, 18.890),
        "b": (8.668, 23.692),
        "c": (21.493, 23.423),
        "d": (21.24, 15.955),
    }
    
    
    class CubeItem(QtWidgets.QGraphicsItem):
        def __init__(self, x, y, parent=None):
            super(CubeItem, self).__init__(parent)
            self.x = x
            self.y = y
            self.polygon = QtGui.QPolygonF(
                [
                    QtCore.QPointF(self.x - 10, self.y - 10),
                    QtCore.QPointF(self.x - 10, self.y + 10),
                    QtCore.QPointF(self.x + 10, self.y + 10),
                    QtCore.QPointF(self.x + 10, self.y - 10),
                ]
            )
            self.setOpacity(0.2) # initial opacity
    
        ##Estimate the drawing area
        def boundingRect(self):
            return QtCore.QRectF(self.x - 10, self.y - 10, 20, 20)
    
        ##Real Shape of drawing area
        def shape(self):
            path = QtGui.QPainterPath()
            path.addRect(self.boundingRect())
            return path
    
        ##paint function called by graphicview
        def paint(self, painter, option, widget):
            painter.setBrush(QtCore.Qt.red)
            painter.drawRect(self.x - 10, self.y - 10, 20, 20)
    
    
    class TagScene(QtWidgets.QGraphicsScene):
        def __init__(self, parent=None):
            super(TagScene, self).__init__(parent)
    
            self.cubes_items_ref = {}
            self.addCubes()
    
        def addCubes(self):
            for cube in CUBE_POS:
                newCube = CubeItem(CUBE_POS[cube][0] * 15, CUBE_POS[cube][1] * 15)
                self.addItem(newCube)
                self.cubes_items_ref[cube] = newCube
    
        def mousePressEvent(self, event):
            for cube in self.items():
                cube.setOpacity(1.0) # update opacity
    
    
    class MainWindow(QtWidgets.QMainWindow):
        def __init__(self):
            super(MainWindow, self).__init__()
            layout = QtWidgets.QHBoxLayout()
            self.scene = TagScene()
            self.view = QtWidgets.QGraphicsView(self.scene)
            self.scene.setSceneRect(QtCore.QRectF(0, 0, 500, 500))
            layout.addWidget(self.view)
            self.widget = QtWidgets.QWidget()
            self.widget.setLayout(layout)
            self.setCentralWidget(self.widget)
    
    
    if __name__ == "__main__":
    
        app = QtWidgets.QApplication(sys.argv)
        test = MainWindow()
        test.show()
        sys.exit(app.exec_())