Причина тормозов, как правильно отметил
agalen, - это попиксельная отрисовка. Она даже на С++ скорее всего бы тормозила.
По поводу организации анимации.
Все-таки, можно обойтись без потоков.
В Qt можно рисовать прямо в виджетах (без промежуточной картинки), но делать это можно только в paintEvent().
Поэтому обычно создают таймер, по сигналу которого обновляется анимация (точнее ее параметры, в данном случае длина закраски phi), и вызывается метод виджета update(), который вызовет событие перерисовки.
Почему нельзя обновлять параметры (модель, phi в данном случае) в paintEvent? Потому что paintEvent вызывается при разных событиях, например, изменение размера виджета.
В виде кода (на базе
agalen и
Loki):
# -*- coding: utf-8 -*-
import sys
import math
from PyQt4 import QtCore, QtGui
def phi_generator(width=720, step=10):
phi = 0.0
while True:
if (math.degrees(phi) + 0.5) * width / 360 < width:
phi += math.radians(step)
else:
phi = 0
yield phi
class MyLabel(QtGui.QLabel):
def __init__(self, title="", parent=None):
super(MyLabel, self).__init__(title, parent)
self.setMinimumSize(280, 50)
self.setText(u"<font color='red'>Закрашивание полосы</font>")
self.phi = 0.0
self.phi_gen = phi_generator()
def paintEvent(self, event):
width, height = self.width(), self.height()
phi = width / 6.2 * self.phi
p = QtGui.QPainter(self)
p.fillRect(0, 0, phi, height, QtGui.QColor('red'))
p.fillRect(phi, 0, width - phi, height, QtGui.QColor('gray'))
def animation_step(self):
self.phi = self.phi_gen.next()
self.update()
class MyWindow(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.label = MyLabel()
self.label.setAlignment(QtCore.Qt.AlignCenter)
self.btnStart = QtGui.QPushButton(u"Запустить зарисовку")
self.btnStop = QtGui.QPushButton(u"Остановить зарисовку")
self.vbox = QtGui.QVBoxLayout()
self.vbox.addWidget(self.label)
self.vbox.addWidget(self.btnStart)
self.vbox.addWidget(self.btnStop)
self.setLayout(self.vbox)
self.paint_timer = QtCore.QTimer(
interval=50,
timeout=self.label.animation_step,
)
self.btnStart.clicked.connect(self.paint_timer.start)
self.btnStop.clicked.connect(self.paint_timer.stop)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
window = MyWindow()
window.setWindowTitle(u"Запуск и остановка закрашивания")
window.resize(300, 100)
window.show()
sys.exit(app.exec_())
Здесь phi_generator() упрощен, но это может быть и класс, который будет содержать различные параметры и состояние, который знает как себя обновить. Также это можно было написать внутри MyLabel.animation_step. Тут уже все зависит от логики.
Например, можно делегировать widget.paintEvent() классу анимации, а widget.update() и animation.one_step() присоединять к timer.timeout по отдельности.
P.S.
Где-то я находил хорошие примеры, но чуть поискал и сейчас не попадаются.