Search code examples
pythonpysidepyside6

Pyside6 Animated Rectangle trouble


I'm trying to make fading looped rectangle area. I used base code from here Just decided expand it. Its just blinking rectangle, but I need smooth fade-in and fade-out effects. So I decided to make method which will calulate new opacity percent and set it to painter. But it doesnt work in cycle.

This is my class now

class HighlightRect(QFrame):
board_width = 400  # width of frame
board_height = 400  #height of frame

def __init__(self, parent, x, y, width=50, height=50, blink_speed=1000):
    super().__init__(parent)
    self.blink_speed = blink_speed
    self.opacity_timer = self.blink_speed
    self.board_height = self.parent().height()
    self.board_width = self.parent().width()
    self.square_height = height
    self.square_width = width
    self.highlight_x = x
    self.highlight_y = y


    #self.setFocusPolicy(QtCore.Qt.StrongFocus)
    self.timer_draw = QtCore.QTimer(self)
    self.timer_draw.timeout.connect(self.draw)
    self.timer_draw.start(self.blink_speed)

    self.color = QtCore.Qt.red
    self.is_draw = False
    self.x_apple = 0
    self.y_apple = 0
    self.draw()

def blink(self, painter):
    self.color = QtCore.Qt.red
    while self.opacity_timer >= 0:
        self.opacity_timer -= 1 / 10  # просто подбор
        percents = round(int(self.opacity_timer / self.blink_speed * 100)/100, 1)
        print(percents)
        painter.setOpacity(percents)



def paintEvent(self, event):
    painter = QtGui.QPainter(self)

    print ("Paint Event?")
    if self.is_draw == True:
        print ("Draw")
        #self.color = QtCore.Qt.red
        self.blink_thread = threading.Thread(name='background', target=lambda: self.blink(painter))
        self.blink_thread.start()
    else:

        self.opacity_timer = self.blink_speed
        print ("Do not draw")
        self.color = QtCore.Qt.transparent
        threading.SystemExit = SystemExit
    painter.setPen(self.color)
    painter.drawRect(self.rect)

def draw(self):
    self.is_draw = not self.is_draw
    self.rect = QRect(self.highlight_x, self.highlight_y, self.square_width, self.square_height)
    self.update()

Changind of opacity inside blink function but outside while loop works as well, but its static. No changes. Changing opacity in loop isn't work. Whats wrong? Maybe somewhere here is another more correct way to get what I want?


Solution

  • One possible solution is to create a QProperty that handles opacity and then use QPropertyAnimation to make the change smooth.

    import random
    import sys
    
    from PySide6.QtCore import Property, Signal, QPropertyAnimation, QRect, Qt
    from PySide6.QtGui import QPainter
    from PySide6.QtWidgets import QFrame, QApplication
    
    
    class Board(QFrame):
        rect_opacity_changed = Signal(name="rectOpacityChanged")
    
        def __init__(self, parent=None):
            super(Board, self).__init__(parent)
    
            self._rect_opacity = 1.0
            self._rect = QRect(0, 0, 50, 50)
    
            self._opacity_animation = QPropertyAnimation(
                targetObject=self, propertyName=b"rect_opacity", duration=3000
            )
            for p, v in ((0.0, 0.0), (0.3, 1.0), (0.7, 1.0), (1.0, 0.0)):
                self._opacity_animation.setKeyValueAt(p, v)
    
            self._opacity_animation.finished.connect(self.change)
    
            self.change()
    
        @Property(float, notify=rect_opacity_changed)
        def rect_opacity(self):
            return self._rect_opacity
    
        @rect_opacity.setter
        def rect_opacity(self, opacity):
            self._rect_opacity = opacity
            self.rect_opacity_changed.emit()
            self.update()
    
        def change(self):
            x = random.randint(0, self.width() - self._rect.width())
            y = random.randint(0, self.height() - self._rect.height())
            self._rect.moveTo(x, y)
            self._opacity_animation.start()
    
        def paintEvent(self, event):
            painter = QPainter(self)
            painter.setOpacity(self.rect_opacity)
            painter.setPen(Qt.red)
            painter.drawRect(self._rect)
    
    
    def main():
    
        app = QApplication([])
        board = Board()
        board.show()
        sys.exit(app.exec())
    
    
    if __name__ == "__main__":
        main()