2012-02-27 7 views
5

폴리 라인을 애니메이션으로 만들려고합니다 (웨이브처럼 행동해야합니다). 이 방법을 시도했습니다 :PySide/PyQt에서 drawPolyline을 사용하여 애니메이션 웨이브 만들기

from PySide.QtCore import * 
from PySide.QtGui import * 
import sys, time 

class Test(QMainWindow): 
    def __init__(self, parent=None): 
     QMainWindow.__init__(self, parent) 

    def poly(self, pts): 
     return QPolygonF(map(lambda p: QPointF(*p), pts)) 

    def paintEvent(self, event): 
     painter = QPainter(self) 

     pts = [[80, 490], [180, 0], [280, 0], [430, 0], [580, 0], [680, 0], [780, 0]] 

     for i in pts: 
      while i[1] < 600: 

       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)) 

       i[1] += 1 
       print pts 
       time.sleep(0.0025) 
       self.update() 

if __name__ == '__main__': 
    example = QApplication(sys.argv) 
    test2 = Test() 
    test2.resize(800, 600) 
    test2.show() 
    sys.exit(example.exec_()) 

하지만 작동하지 않습니다! 프로그램이 실행될 때 화면에 혼란이 있습니다. 그것은 self.update() 창이 업데이트되지 않는 것 같습니다. 도와주세요.

답변

6

이 코드에는 몇 가지 문제가 분명히 있습니다.) (

  1. (나쁜)
  2. 호출 self.update 그 paintEvent의 내부에 잠을 일을 paintEvent
  3. 에 너무 많은 처리를 수행 : 내가주의 모든 것을 나열하고 설명을 통해 이동합니다 paintEvent의 내부에있는 동안

좋아. 페인트 이벤트는 위젯이 다시 그리기를 원하고 가능한 빨리해야하는 곳입니다. 이 이벤트에서 반복적 인 일을해서는 안되며, 너무 오래 걸릴 때마다 추첨이 늦어집니다. 또한 이벤트 내부에서 update()를 호출하면 잠재적으로 재귀 적입니다. 페인트 이벤트의 목표는 위젯의 현재 상태에 응답하고, 페인트 칠하고 꺼내는 것입니다.

다음은 작동하는 코드의 수정 된 버전입니다. 그것없는 가장 이상적인 방법은,하지만 난 점은 멤버 속성, self.pts로 이동 된 것을

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_()) 

공지 사항 ... 아래 그 이상을 설명 할 것, 그리고 paintEvent()는 이제 지점의 현재 상태를 페인트 . 그런 다음 애니메이션 논리가 wave()이라는 다른 메서드로 이동합니다. 이 메서드에서는 각 지점을 반복 및 수정하고 update()을 호출하여 다시 그리기를 트리거합니다. paintEvent 바깥에서 update()이라고 부릅니다. 응용 프로그램에서 윈도우가 다시 그리기 (크기 조정 등)하는 다른 이벤트가 발생하면 paintEvent가 반복적으로 반복 될 수 있으므로 중요합니다.

그래서 우리는이 포인트 목록을 수정하고 잠을 자고 중요한 추가로 QApplication.processEvents()을 호출합니다. 일반적으로 응용 프로그램이 유휴 상태가 될 때 (현재 통화 종료) 이벤트가 처리됩니다. 재 페인트를 계속 호출하고 이러한 이벤트를 겹쳐서 표시하기 때문에 이벤트 루프에 모든 것을 플러시하도록 지시해야합니다. 그 processEvents() 명령을 주석 처리하고 어떤 일이 발생하는지보십시오. 루프가 완료 될 때까지 앱에서 아무 일도하지 않고 간단히 돌리면 결과로 나타나는 선이 제자리에 나타납니다.

이제 내가 제안한 부분에 대해 이것이 실제로 가장 이상적인 방법은 아니지만 예제로 작동합니다. 이 현재 예제는 웨이브를 수행하는 동안 주 스레드를 차단합니다. 주 스레드는 GUI 이벤트에 순전히 반응하는 것을 차단해야합니다. 그래서 여기에 몇 가지 가능한 제안은 다음과 같습니다

  1. 당신은 타임 아웃으로 0.0025 애니메이션 속도를 사용하여 QTimer를 만들 수 있습니다. timeout() 신호를 단일 단계를 수행하고 업데이트를 호출하는 wave() 메서드 버전에 연결하십시오. 더 이상 수면이 필요 없습니다. 웨이브 계산이 끝나면 wave()에서 확인하고 타이머에서 stop()으로 전화하십시오.

  2. 위 예제의 wave() 루프 및 초기 데이터 세트 전체를 QThread으로 이동하십시오. 이 QThread는 emit a custom signal이고 waveUpdate(QPolygonF)과 같습니다. 이 스레드를 시작하면 루프를 수행하고 QPolygonF를 작성하고 각 단계에서 신호를 내고 절전 모드로 전환합니다. 이 신호를 새 다각형을 수신하는 메쏘드에 연결하고, 그것을 self._polygon에 할당하고, update()를 호출하면 self._polygon을 잡고 페인트 할 수 있습니다. 여기에 가능한 한 많은 작업을 가능한 한 스레드로 옮기고 기본 GUI 스레드에 새로운 값으로 다시 칠하도록 지시하는 것입니다.