pyside/pyqt 如何简单地制作弧形动画?
pyside/pyqt how to animate an arc simply?
我正在寻找一个解决方案,从 0 - 360° 为这个圆弧设置动画。我是 Pyside/Pyqt 的新手,我没有找到如此简单的解决方案(只有初学者“不熟悉”)。我也用 while 循环试过,但它不起作用。目前我不明白这个动画系统,但我想研究它。
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()
QPropertyAnimation 用于为任何 QObject 的 Qt 属性 设置动画。如果您引用 self
(QMainWindow 的当前实例),那么您也可以为所有 properties of a QMainWindow and all inherited properties (QMainWindow inherits from QWidget, so you can animate all the QWidget properties 设置动画。
在您的情况下,您正在尝试为 window 的 width
属性 制作动画,而这肯定不是您想要做的。
由于您要更改的值不是 window 的 属性,因此您不能使用 QPropertyAnimation(除非您使用 [= 创建 Qt 属性 13=] 装饰器),你应该改用 QVariantAnimation。
然后,Qt 每次 将要绘制小部件时都会调用 paintEvent(这可能经常发生),因此您不能在那里创建动画,否则您可能会以递归结束:由于动画需要重新绘制,因此每次前一个动画需要更新时,您都会创建一个新动画。
最后,考虑到通常不鼓励在 QMainWindow 上绘制,因为 Qt main window 是一种特殊的 QWidget,用于高级功能(菜单、状态栏等)并使用 中央小部件 显示实际内容。
正确的做法是创建并设置一个中央小部件,然后在该小部件上实现绘画。
这是您的代码的修订版和工作版本:
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)
valueChanged
连接确保每次值更改时小部件都会安排更新(因此一旦事件队列允许就调用 paintEvent
),然后您可以使用当前值绘制实际弧线的动画。
感谢 @musicamante
关于使用 QAnimationProperty
动画弧的解决方案
已修改 @musicmante
代码以创建加载效果我猜这将有助于开发人员,并可能会节省他们尝试使用 Qt
制作 加载效果 的时间
来源
#!/usr/bin/env python3.10
import sys
import string
import random
from PySide6.QtWidgets import (QMainWindow, QPushButton, QVBoxLayout,
QApplication, QWidget)
from PySide6.QtCore import (Qt, QVariantAnimation)
from PySide6.QtGui import (QPen, QPainter, QColor)
class Arc:
colors = list(string.ascii_lowercase[0:6]+string.digits)
shades_of_blue = ["#7CB9E8","#00308F","#72A0C1", "#F0F8FF",
"#007FFF", "#6CB4EE", "#002D62", "#5072A7",
"#002244", "#B2FFFF", "#6F00FF", "#7DF9FF","#007791",
"#ADD8E6", "#E0FFFF", "#005f69", "#76ABDF",
"#6A5ACD", "#008080", "#1da1f2", "#1a1f71", "#0C2340"]
shades_of_green = ['#32CD32', '#CAE00D', '#9EFD38', '#568203', '#93C572',
'#8DB600', '#708238', '#556B2F', '#014421', '#98FB98', '#7CFC00',
'#4F7942', '#009E60', '#00FF7F', '#00FA9A', '#177245', '#2E8B57',
'#3CB371', '#A7F432', '#123524', '#5E8C31', '#90EE90', '#03C03C',
'#66FF00', '#006600', '#D9E650']
def __init__(self):
self.diameter = random.randint(100, 600)
#cols = list(Arc.colors)
#random.shuffle(cols)
#_col = "#"+''.join(cols[:6])
#print(f"{_col=}")
#self.color = QColor(_col)
#self.color = QColor(Arc.shades_of_blue[random.randint(0, len(Arc.shades_of_blue)-1)])
self.color = QColor(Arc.shades_of_green[random.randint(0, len(Arc.shades_of_green)-1)])
#print(f"{self.color=}")
self.span = random.randint(40, 150)
self.direction = 1 if random.randint(10, 15)%2 == 0 else -1
self.startAngle = random.randint(40, 200)
self.step = random.randint(100, 300)
class ArcWidget(QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.arcs = [Arc() for i in range(random.randint(10, 20))]
self.startAnime()
def initUI(self):
#self.setAutoFillBackground(True)
self.setAttribute(Qt.WA_StyledBackground, True)
self.setStyleSheet("background-color:black;")
def startAnime(self):
self.anim = QVariantAnimation(self, duration = 2000)
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.setRenderHint(QPainter.Antialiasing)
#painter.setPen(QPen(QColor("#b6faec"), 5, Qt.SolidLine))
#painter.drawArc(
# 100, 100, 400, 400, 90*16,
# self.anim.currentValue() * 16)
#width = 400
#height = 400
#painter.drawArc(self.width()/2 -width/2, self.height()/2 - height/2, 400, 400, self.anim.currentValue()*16, 45*16)
for arc in self.arcs:
painter.setPen(QPen(arc.color, 6, Qt.SolidLine))
painter.drawArc(self.width()/2 - arc.diameter/2,
self.height()/2 - arc.diameter/2, arc.diameter,
arc.diameter, self.anim.currentValue()*16*arc.direction+arc.startAngle*100, arc.span*16)
#print(f"currentValue : {self.anim.currentValue()}")
#arc.startAngle = random.randint(50, 200)
if self.anim.currentValue() == 360:
#print("360")
self.startAnime()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Animate Arc")
self.setGeometry(100, 100, 600, 600)
self.arcWidget = ArcWidget()
self.setCentralWidget(self.arcWidget)
if __name__ == "__main__":
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
app.exec()
输出:
$ ./arc_widget.py
我正在寻找一个解决方案,从 0 - 360° 为这个圆弧设置动画。我是 Pyside/Pyqt 的新手,我没有找到如此简单的解决方案(只有初学者“不熟悉”)。我也用 while 循环试过,但它不起作用。目前我不明白这个动画系统,但我想研究它。
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()
QPropertyAnimation 用于为任何 QObject 的 Qt 属性 设置动画。如果您引用 self
(QMainWindow 的当前实例),那么您也可以为所有 properties of a QMainWindow and all inherited properties (QMainWindow inherits from QWidget, so you can animate all the QWidget properties 设置动画。
在您的情况下,您正在尝试为 window 的 width
属性 制作动画,而这肯定不是您想要做的。
由于您要更改的值不是 window 的 属性,因此您不能使用 QPropertyAnimation(除非您使用 [= 创建 Qt 属性 13=] 装饰器),你应该改用 QVariantAnimation。
然后,Qt 每次 将要绘制小部件时都会调用 paintEvent(这可能经常发生),因此您不能在那里创建动画,否则您可能会以递归结束:由于动画需要重新绘制,因此每次前一个动画需要更新时,您都会创建一个新动画。
最后,考虑到通常不鼓励在 QMainWindow 上绘制,因为 Qt main window 是一种特殊的 QWidget,用于高级功能(菜单、状态栏等)并使用 中央小部件 显示实际内容。
正确的做法是创建并设置一个中央小部件,然后在该小部件上实现绘画。
这是您的代码的修订版和工作版本:
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)
valueChanged
连接确保每次值更改时小部件都会安排更新(因此一旦事件队列允许就调用 paintEvent
),然后您可以使用当前值绘制实际弧线的动画。
感谢 @musicamante
关于使用 QAnimationProperty
已修改 @musicmante
代码以创建加载效果我猜这将有助于开发人员,并可能会节省他们尝试使用 Qt
制作 加载效果 的时间
来源
#!/usr/bin/env python3.10
import sys
import string
import random
from PySide6.QtWidgets import (QMainWindow, QPushButton, QVBoxLayout,
QApplication, QWidget)
from PySide6.QtCore import (Qt, QVariantAnimation)
from PySide6.QtGui import (QPen, QPainter, QColor)
class Arc:
colors = list(string.ascii_lowercase[0:6]+string.digits)
shades_of_blue = ["#7CB9E8","#00308F","#72A0C1", "#F0F8FF",
"#007FFF", "#6CB4EE", "#002D62", "#5072A7",
"#002244", "#B2FFFF", "#6F00FF", "#7DF9FF","#007791",
"#ADD8E6", "#E0FFFF", "#005f69", "#76ABDF",
"#6A5ACD", "#008080", "#1da1f2", "#1a1f71", "#0C2340"]
shades_of_green = ['#32CD32', '#CAE00D', '#9EFD38', '#568203', '#93C572',
'#8DB600', '#708238', '#556B2F', '#014421', '#98FB98', '#7CFC00',
'#4F7942', '#009E60', '#00FF7F', '#00FA9A', '#177245', '#2E8B57',
'#3CB371', '#A7F432', '#123524', '#5E8C31', '#90EE90', '#03C03C',
'#66FF00', '#006600', '#D9E650']
def __init__(self):
self.diameter = random.randint(100, 600)
#cols = list(Arc.colors)
#random.shuffle(cols)
#_col = "#"+''.join(cols[:6])
#print(f"{_col=}")
#self.color = QColor(_col)
#self.color = QColor(Arc.shades_of_blue[random.randint(0, len(Arc.shades_of_blue)-1)])
self.color = QColor(Arc.shades_of_green[random.randint(0, len(Arc.shades_of_green)-1)])
#print(f"{self.color=}")
self.span = random.randint(40, 150)
self.direction = 1 if random.randint(10, 15)%2 == 0 else -1
self.startAngle = random.randint(40, 200)
self.step = random.randint(100, 300)
class ArcWidget(QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.arcs = [Arc() for i in range(random.randint(10, 20))]
self.startAnime()
def initUI(self):
#self.setAutoFillBackground(True)
self.setAttribute(Qt.WA_StyledBackground, True)
self.setStyleSheet("background-color:black;")
def startAnime(self):
self.anim = QVariantAnimation(self, duration = 2000)
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.setRenderHint(QPainter.Antialiasing)
#painter.setPen(QPen(QColor("#b6faec"), 5, Qt.SolidLine))
#painter.drawArc(
# 100, 100, 400, 400, 90*16,
# self.anim.currentValue() * 16)
#width = 400
#height = 400
#painter.drawArc(self.width()/2 -width/2, self.height()/2 - height/2, 400, 400, self.anim.currentValue()*16, 45*16)
for arc in self.arcs:
painter.setPen(QPen(arc.color, 6, Qt.SolidLine))
painter.drawArc(self.width()/2 - arc.diameter/2,
self.height()/2 - arc.diameter/2, arc.diameter,
arc.diameter, self.anim.currentValue()*16*arc.direction+arc.startAngle*100, arc.span*16)
#print(f"currentValue : {self.anim.currentValue()}")
#arc.startAngle = random.randint(50, 200)
if self.anim.currentValue() == 360:
#print("360")
self.startAnime()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Animate Arc")
self.setGeometry(100, 100, 600, 600)
self.arcWidget = ArcWidget()
self.setCentralWidget(self.arcWidget)
if __name__ == "__main__":
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
app.exec()
输出:
$ ./arc_widget.py