【问题标题】:Animated QRect using Delegate paint() method使用 Delegate paint() 方法的动画 QRect
【发布时间】:2021-10-19 16:01:20
【问题描述】:

目前我正在尝试使用 QStyledItemDelegate 绘制一个 QRect,该 QRect 根据项目值在 10 秒内逐渐消失

def paint(self, painter, option, index):
    painter.save()
    painter.setFont(self.font)
    painter.setPen(self.normalPen)

    r = option.rect.adjusted(5, 0, -5, 0)

    item = index.model().sourceModel().rowData(index.model().mapToSource(index))

    if item.attr_X is True:
        # animate qrect to fade away
        painter.fillRect(option.rect, QtGui.QBrush(QtGui.QColor(100, 100, 100)))

不知道从哪里开始,我研究了 QPropertyAnimation,但似乎并不容易在绘画功能中实现。

【问题讨论】:

  • 我有一个问题:动画应该什么时候开始?
  • 考虑在绘画事件中发生的事情(也称为代表的paint)应该从不除了实际绘画之外做任何其他事情:这意味着你不应在绘图函数中实现任何动画,但“动画”应在其他地方实现并负责请求更新。
  • @eyllanesc 如果项目值 attr_X 为 True,则动画应该开始,否则它什么也不做。 attr_X 自动设置在不同的位置
  • @musicamante 你有什么建议在哪里实现它而不是那里?
  • @JohnD。那么请提供minimal reproducible example

标签: python animation pyqt qpainter


【解决方案1】:

一种可能的解决方案不是覆盖委托,而是使用动画中的信息更新Qt::BackgroundRole

import random
from dataclasses import dataclass
from functools import cached_property

from PyQt5.QtCore import (
    pyqtSignal,
    QAbstractAnimation,
    QModelIndex,
    QPersistentModelIndex,
    QObject,
    Qt,
    QVariantAnimation,
)
from PyQt5.QtGui import QColor, QPalette, QStandardItemModel
from PyQt5.QtWidgets import QPushButton, QTableView, QVBoxLayout, QWidget


@dataclass
class AnimationItem(QObject):
    index: QPersistentModelIndex
    role: int
    animation: QVariantAnimation

    value_changed = pyqtSignal(QModelIndex, object, int)
    finished = pyqtSignal()

    def __post_init__(self):
        super().__init__()
        self.animation.valueChanged.connect(self.handle_value_changed)
        self.animation.finished.connect(self.finished)

    def is_valid(self):
        return self.index.isValid()

    def start(self):
        self.animation.start(QAbstractAnimation.DeleteWhenStopped)

    def stop(self):
        self.animation.stop()

    def handle_value_changed(self, value):
        self.value_changed.emit(QModelIndex(self.index), value, self.role)


class AnimationManager(QObject):
    def __init__(self, view, parent=None):
        super().__init__(parent)
        self._view = view

    @property
    def view(self):
        return self._view

    @cached_property
    def items(self):
        return list()

    def start(self, item):
        for it in self.items:
            if it.index == item.index:
                self.remove(it)
                break
        self.items.append(item)
        item.value_changed.connect(self.handle_value_changed)
        item.finished.connect(self.handle_finished)
        item.start()

    def handle_finished(self):
        item = self.sender()
        if isinstance(item, AnimationItem):
            self.remove(item)

    def remove(self, item):
        item.stop()
        self.items.remove(item)

    def handle_value_changed(self, index, value, role):
        self.view.model().setData(index, value, role)
        self.view.update(index)


class Widget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.view.setModel(self.model)
        self.model.setRowCount(2)
        self.model.setColumnCount(2)

        lay = QVBoxLayout(self)
        lay.addWidget(self.button)
        lay.addWidget(self.view)

        self.button.clicked.connect(self.handle_clicked)

    @cached_property
    def view(self):
        return QTableView()

    @cached_property
    def model(self):
        return QStandardItemModel()

    @cached_property
    def button(self):
        return QPushButton("Start Animation")

    @cached_property
    def animation_manager(self):
        return AnimationManager(self.view)

    def handle_clicked(self):
        end_color = self.view.palette().brush(QPalette.Base).color()
        for i in range(self.model.rowCount()):
            for j in range(self.model.columnCount()):
                index = self.model.index(i, j)
                start_color = QColor(*random.sample(range(255), 3))
                animation = QVariantAnimation()
                animation.setStartValue(start_color)
                animation.setEndValue(end_color)
                animation.setDuration(random.randint(1000, 6 * 1000))
                item = AnimationItem(
                    QPersistentModelIndex(index), Qt.BackgroundRole, animation
                )
                self.animation_manager.start(item)


def main():
    import sys
    from PyQt5.QtWidgets import QApplication

    app = QApplication(sys.argv)
    app.setStyle("fusion")
    w = Widget()
    w.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

【讨论】:

  • 感谢您写出这篇文章,非常详细,非常感谢。 python 的使用有点过头了,所以我会在接下来的几天里仔细研究一下。再次感谢!
猜你喜欢
  • 2012-05-21
  • 1970-01-01
  • 1970-01-01
  • 2019-09-01
  • 2012-03-30
  • 1970-01-01
  • 1970-01-01
  • 2023-01-16
  • 2011-05-07
相关资源
最近更新 更多