明らかに、このコードにはいくつかの問題があります。気付いたものをすべてリストしてから、説明を確認します。
- paintEventで処理が多すぎます
- そのpaintEvent内でスリープを行う(悪い)
- paintEvent内でself.update()を呼び出す
大丈夫。ペイントイベントは、ウィジェットが再描画する場所であり、可能な限り高速である必要があります。このイベントで再帰的なことをしたり、ドローが遅くなるので時間がかかりすぎたりしないでください。また、イベント内でupdate()を呼び出すと、再帰的になる可能性があります。ペイントイベントの目標は、ウィジェットの現在の状態に応答し、ペイントして、抜け出すことです。
これが機能するコードの修正バージョンです。これは最も理想的なアプローチではありませんが、以下で詳しく説明します...
from PySide.QtCore import *
from PySide.QtGui import *
import sys, time
class Test(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.pts = [[80, 490], [180, 0], [280, 0], [430, 0], [580, 0], [680, 0], [780, 0]]
def poly(self, pts):
return QPolygonF(map(lambda p: QPointF(*p), pts))
def paintEvent(self, event):
painter = QPainter(self)
pts = self.pts[:]
painter.setPen(QPen(QColor(Qt.darkGreen), 3))
painter.drawPolyline(self.poly(pts))
painter.setBrush(QBrush(QColor(255, 0, 0)))
painter.setPen(QPen(QColor(Qt.black), 1))
for x, y in pts:
painter.drawEllipse(QRectF(x - 4, y - 4, 8, 8))
# print pts
def wave(self):
for point in self.pts:
while point[1] < 600:
point[1] += 1
self.update()
QApplication.processEvents()
time.sleep(0.0025)
if __name__ == '__main__':
example = QApplication(sys.argv)
test2 = Test()
test2.resize(800, 600)
test2.show()
test2.raise_()
test2.wave()
sys.exit(example.exec_())
ポイントがメンバー属性に移動され、ポイントの現在の状態のみがペイントされるようになったことに注意してself.ptsくださいpaintEvent()。次に、アニメーションロジックは。と呼ばれる別のメソッドに移動されwave()ます。このメソッドでは、各ポイントをループして変更しupdate()、再描画をトリガーするために呼び出します。update()paintEventの外部で呼び出していることに注意してください。これは重要です。アプリケーションでウィンドウの再描画(サイズ変更など)を引き起こす他のイベントが発生した場合、paintEventが永久にループする可能性があるためです。
したがって、このポイントリスト、スリープ、およびQApplication.processEvents()を呼び出すための重要な追加を変更します。通常、イベントは、アプリケーションがアイドル状態になる(現在の呼び出しを離れる)ときに処理されます。常に再描画を呼び出し、これらのイベントを積み重ねているため、イベントループに先に進んで、すべてをフラッシュするように指示する必要があります。processEvents()そのコマンドをコメントアウトして、何が起こるかを確認してください。ループが完了するまでアプリは何もせずにスピンするだけで、結果の行が所定の位置に表示されます。
さて、私が提案していた部分では、これは例として機能しますが、実際には最も理想的なアプローチではありません。この現在の例では、ウェーブの実行中にメインスレッドをブロックします。純粋にGUIイベントに応答することを目的としているため、メインスレッドをブロックすることは常に避けてください。だからここにいくつかの可能な提案があります:
アニメーション速度をタイムアウトとして使用してQTimerを作成できます。timeout()シグナルを、シングルステップを実行してupdateを呼び出すバージョンのメソッドに0.0025接続します。ここではもう寝る必要はありません。波の計算が終了したら、それをチェックインし、タイマーでstop()を呼び出します。wave()wave()
ループ全体wave()と初期データセットを上記の例からQThreadに移動します。このQThreadは、のようなカスタムシグナルをwaveUpdate(QPolygonF)発行します。このスレッドを開始すると、ループが実行され、QPolygonFの作成が処理され、各ステップでシグナルが送信されてスリープ状態になります。このシグナルをメインウィンドウのメソッドに接続して、新しいポリゴンを受け取り、に割り当ててself._polygon、update()を呼び出すと、self._polygonを取得してペイントすることができます。ここでの考え方は、手間のかかる作業を可能な限りスレッドに移し、メインGUIスレッドに新しい値で再描画するように指示することだけです。