一种可能的解决方案不是覆盖委托,而是使用动画中的信息更新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()