Skip to content
Advertisement

pyside/pyqt how to animate an arc simply?

I’m looking for a solution, to animate this arc from 0 – 360°. I’m relative new to Pyside/Pyqt and I don’t find such a simple solution (only beginner “unfriedly”). I tried it with while loops aswell, but it doesn’t works. At the moment I don’t understand this animation system, but I want to work on it.

import sys

from PySide6 import QtCore
from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtCore import Qt
from PySide6.QtGui import QBrush, QPen, QPainter


class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("AnimateArc")
        self.setGeometry(100, 100, 600, 600)

    def paintEvent(self, event):
        self.anim = QtCore.QPropertyAnimation(self, b"width", duration=1000) #<- is there a documentation for b"width", b"geometry"?
        self.anim.setStartValue(0)
        start = 0
        painter = QPainter(self)
        painter.setPen(QPen(Qt.black, 5, Qt.SolidLine))
        painter.drawArc(100, 100, 400, 400, 90 * 16, start * 16)    # I want to make the change dynamicly

        self.anim.setEndValue(360)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    app.exec()

Advertisement

Answer

QPropertyAnimation is used to animate Qt properties of any QObject. If you refer to self (the current instance of QMainWindow), then you can animate all properties of a QMainWindow and all inherited properties (QMainWindow inherits from QWidget, so you can animate all the QWidget properties as well).
In your case, you’re trying to animate the width property of the window, and that’s certainly not what you want to do.

Since what you want to change is a value that is not a property of the window, you cannot use QPropertyAnimation (unless you create a Qt property using the @Property decorator), and you should use a QVariantAnimation instead.

Then, a paintEvent is called by Qt every time the widget is going to be drawn (which can happen very often), so you cannot create the animation there, otherwise you could end up with a recursion: since the animation would require a repaint, you would create a new animation everytime the previous requires an update.

Finally, consider that painting on a QMainWindow is normally discouraged, as a Qt main window is a special kind of QWidget intended for advanced features (menus, status bar, etc) and uses a central widget to show the actual contents.
The correct approach is to create and set a central widget, and implement the painting on that widget instead.

Here is a revised and working version of your code:

class ArcWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.anim = QtCore.QVariantAnimation(self, duration=1000)
        self.anim.setStartValue(0)
        self.anim.setEndValue(360)
        self.anim.valueChanged.connect(self.update)
        self.anim.start()

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setPen(QPen(Qt.black, 5, Qt.SolidLine))
        painter.drawArc(
            100, 100, 400, 400, 90 * 16, self.anim.currentValue() * 16)


class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("AnimateArc")
        self.setGeometry(100, 100, 600, 600)
        self.arcWidget = ArcWidget()
        self.setCentralWidget(self.arcWidget)

The valueChanged connection ensures that everytime the value changes the widget schedules an update (thus calling a paintEvent as soon as the event queue allows it), then you can use the current value of the animation to draw the actual arc.

User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement