如何使用 QPropertyAnimation 和 QPainter 绘制弧线

Posted

技术标签:

【中文标题】如何使用 QPropertyAnimation 和 QPainter 绘制弧线【英文标题】:How to use QPropertyAnimation with QPainter to draw an arc 【发布时间】:2019-08-26 14:14:40 【问题描述】:

我正在编写一个执行系统监视器功能的桌面小部件。使用QPainter 我画了一条弧线,它表示cpu 使用水平的图形表示。每隔一秒,paintevent 就会根据cpu_percent() 函数值使用跨度角重绘此弧。

结果是新级别表示和上一个级别表示之间的跳跃过渡。我想使用QPropertyAnimation 创建一个平滑的缓动弧动画。不幸的是,我不知道我应该使用的属性。如果你告诉我如何以正确的方式做到这一点,我会很高兴。

这是我使用的一个小部件类:

from PySide2 import QtWidgets, QtCore, QtGui
from psutil import cpu_percent


class cpu_diagram(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(cpu_diagram, self).__init__()
        self.resize(600, 600)   # todo

        # color constants
        self.dark = "#3B3A44"
        self.light = "#4A4953"
        self.color = "#75ECB5"

        # text constants
        self.module_name = "CPU"
        self.postfix = "average"

        # timer with an interval of 1 sec
        self.timer = QtCore.QTimer()
        self.timer.setInterval(1000)
        self.timer.timeout.connect(self.update)
        self.timer.start()

    def paintEvent(self, event:QtGui.QPaintEvent):
        # get cpu usage
        self.percent = cpu_percent()

        painter = QtGui.QPainter(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)

        # draw base
        basic_rect = self.rect().adjusted(20, 20, -20, -20)
        painter.setBrush(QtGui.QBrush(QtGui.QColor(self.dark)))
        painter.drawEllipse(basic_rect)

        # draw arc
        pen = QtGui.QPen(QtGui.QColor(self.light))
        pen.setWidth(40)
        painter.setPen(pen)
        arc_rect = basic_rect.adjusted(40, 40, -40, -40)
        painter.drawEllipse(arc_rect)

        # draw active arc
        pen.setColor(QtGui.QColor(self.color))
        start_angle = 90
        span_angle = self.percent_to_angle(self.percent)
        painter.setPen(pen)
        painter.drawArc(arc_rect, start_angle * 16, span_angle * 16)

        # draw text

        # draw module name
        painter.setPen(QtGui.QPen(QtGui.QColor(QtCore.Qt.white)))
        font = QtGui.QFont()
        font.setPixelSize(110)
        painter.setFont(font)
        arc_rect.moveTop(-25)
        painter.drawText(arc_rect, QtCore.Qt.AlignCenter, self.module_name)

        # draw postfix
        font = QtGui.QFont()
        font.setPixelSize(60)
        painter.setFont(font)
        arc_rect.moveTop(-125)
        painter.drawText(arc_rect, QtCore.Qt.AlignCenter | QtCore.Qt.AlignBottom, self.postfix)

        # draw percents
        arc_rect.moveBottom(460)
        painter.setPen(QtGui.QPen(self.color))
        painter.drawText(arc_rect, QtCore.Qt.AlignCenter | QtCore.Qt.AlignBottom, f"str(self.percent) %")

    def percent_to_angle(self, percent):
        return -percent / 100 * 360

【问题讨论】:

【参考方案1】:

您必须创建一个表示百分比的 QProperty 并在 QPropertyAnimation 中使用它。

from PySide2 import QtWidgets, QtCore, QtGui
from psutil import cpu_percent


class CpuDiagram(QtWidgets.QWidget):
    percentChanged = QtCore.Signal(float)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.resize(600, 600)  # todo

        # color constants
        self.dark = "#3B3A44"
        self.light = "#4A4953"
        self.color = "#75ECB5"

        # text constants
        self.module_name = "CPU"
        self.postfix = "average"

        # timer with an interval of 1 sec
        self.timer = QtCore.QTimer()
        self.timer.setInterval(1000)
        self.timer.timeout.connect(self.onTimeout)
        self.timer.start()

        self._percent = 0
        self._animation = QtCore.QPropertyAnimation(self, b"percent", duration=400)

        self.percentChanged.connect(self.update)

    @QtCore.Slot()
    def onTimeout(self):
        start_value = self.percent
        end_value = cpu_percent()

        self._animation.setStartValue(start_value)
        self._animation.setEndValue(end_value)
        self._animation.start()

    def get_percent(self):
        return self._percent

    def set_percent(self, p):
        if self._percent != p:
            self._percent = p
            self.percentChanged.emit(p)

    percent = QtCore.Property(
        float, fget=get_percent, fset=set_percent, notify=percentChanged
    )

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

        # draw base
        basic_rect = self.rect().adjusted(20, 20, -20, -20)
        painter.setBrush(QtGui.QBrush(QtGui.QColor(self.dark)))
        painter.drawEllipse(basic_rect)

        # draw arc
        pen = QtGui.QPen(QtGui.QColor(self.light))
        pen.setWidth(40)
        painter.setPen(pen)
        arc_rect = basic_rect.adjusted(40, 40, -40, -40)
        painter.drawEllipse(arc_rect)

        # draw active arc
        pen.setColor(QtGui.QColor(self.color))
        start_angle = 90
        span_angle = self.percent_to_angle(self.percent)
        painter.setPen(pen)
        painter.drawArc(arc_rect, start_angle * 16, span_angle * 16)

        # draw text

        # draw module name
        painter.setPen(QtGui.QPen(QtGui.QColor(QtCore.Qt.white)))
        font = QtGui.QFont()
        font.setPixelSize(110)
        painter.setFont(font)
        arc_rect.moveTop(-25)
        painter.drawText(arc_rect, QtCore.Qt.AlignCenter, self.module_name)

        # draw postfix
        font = QtGui.QFont()
        font.setPixelSize(60)
        painter.setFont(font)
        arc_rect.moveTop(-125)
        painter.drawText(
            arc_rect, QtCore.Qt.AlignCenter | QtCore.Qt.AlignBottom, self.postfix
        )

        # draw percents
        arc_rect.moveBottom(460)
        painter.setPen(QtGui.QPen(self.color))
        painter.drawText(
            arc_rect,
            QtCore.Qt.AlignCenter | QtCore.Qt.AlignBottom,
            f"self.percent:.2f %",
        )

    def percent_to_angle(self, percent):
        return -percent / 100 * 360


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = CpuDiagram()
    w.show()
    sys.exit(app.exec_())

【讨论】:

以上是关于如何使用 QPropertyAnimation 和 QPainter 绘制弧线的主要内容,如果未能解决你的问题,请参考以下文章

PyQt5 QPropertyAnimation finished() 如何连接

QPropertyAnimation 支持哪些属性进行动画处理?

QPainter 在使用 QPropertyAnimation 时崩溃

Qt炫酷动画5.QVariantAnimation可变属性类QPropertyAnimation动画属性类

Qt炫酷动画5.QVariantAnimation可变属性类QPropertyAnimation动画属性类

Qt炫酷动画5.QVariantAnimation可变属性类QPropertyAnimation动画属性类