2012-02-27 30 views
5

Tôi đang cố gắng tạo hiệu ứng động cho một polyline (nó phải hoạt động như một sóng). Tôi đã thử cách này:Tạo một wave động với drawPolyline trong PySide/PyQt

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

Nhưng, nó không hoạt động! Có một mớ hỗn độn trên màn hình, khi chương trình chạy. Dường như, self.update() không cập nhật cửa sổ. Xin vui lòng, giúp đỡ.

Trả lời

6

Có một số vấn đề với mã này, rõ ràng. Tôi sẽ liệt kê tất cả những gì tôi nhận thấy, và sau đó đi qua các giải thích:

  1. làm quá nhiều xử lý trong một paintEvent
  2. làm một giấc ngủ bên trong paintEvent đó (xấu)
  3. gọi self.update() trong khi bên trong một paintEvent

Được rồi. Một sự kiện sơn là nơi mà tiện ích muốn vẽ lại và phải càng nhanh càng tốt. Bạn không nên làm bất cứ điều gì đệ quy trong sự kiện này, hoặc dành quá nhiều thời gian vì nó sẽ làm chậm quá trình vẽ của bạn. Ngoài ra, gọi cập nhật() trong khi bên trong sự kiện của bạn có khả năng đệ quy. Mục tiêu của sự kiện sơn phải là phản hồi trạng thái hiện tại của tiện ích con, tô màu và thoát ra.

Đây là phiên bản đã sửa đổi của mã hoạt động. không phải là phương pháp lý tưởng nhất của nó, nhưng tôi sẽ giải thích rằng hơn dưới đây ...

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

Chú ý rằng những điểm đã được chuyển đến một thuộc tính thành viên, self.ptspaintEvent() bây giờ chỉ vẽ hiện trạng của các điểm . Sau đó, logic hoạt ảnh được chuyển sang phương thức khác có tên là wave(). Trong phương thức này, nó lặp và sửa đổi từng điểm và gọi update() để kích hoạt vẽ lại. Lưu ý rằng chúng tôi đang gọi update() bên ngoài paintEvent. Điều này rất quan trọng vì bất kỳ sự kiện nào khác xảy ra trong ứng dụng của bạn làm cho cửa sổ vẽ lại (thay đổi kích thước, vv), bạn paintEvent có thể lặp lại mãi mãi.

Vì vậy, chúng tôi sửa đổi danh sách điểm này, ngủ và bổ sung quan trọng để gọi QApplication.processEvents(). Thông thường, các sự kiện được xử lý khi ứng dụng trở nên nhàn rỗi (rời khỏi cuộc gọi hiện tại). Bởi vì bạn đang gọi một repaint liên tục, và xếp chồng các sự kiện này lên, bạn cần phải nói cho vòng lặp sự kiện để đi trước và tuôn ra tất cả mọi thứ thông qua. Hãy thử nhận xét rằng lệnh processEvents() và xem điều gì xảy ra. Ứng dụng của bạn sẽ đơn giản quay không làm gì cho đến khi vòng lặp hoàn tất và dòng kết quả sẽ xuất hiện.

Bây giờ cho phần mà tôi đã đề xuất đây không thực sự là cách tiếp cận lý tưởng nhất, mặc dù nó hoạt động như một ví dụ. Ví dụ hiện tại này chặn luồng chính trong khi nó đang thực hiện một wave. Bạn nên tránh việc chặn luồng chính vì nó có nghĩa là hoàn toàn phản hồi các sự kiện GUI. Vì vậy, đây là một số gợi ý có thể:

  1. Bạn có thể tạo một QTimer sử dụng tốc độ 0.0025 hoạt hình như một thời gian chờ. Kết nối tín hiệu timeout() với phiên bản của phương thức wave() thực hiện một bước và gọi cập nhật. Không cần ngủ ở đây nữa. Khi các tính toán sóng của bạn đã kết thúc, bạn sẽ kiểm tra điều đó trong wave() và gọi stop() trên bộ hẹn giờ.

  2. Di chuyển toàn bộ vòng lặp wave() và tập dữ liệu ban đầu từ ví dụ trên vào QThread. QThread này sẽ emit a custom signal như waveUpdate(QPolygonF). Khi bạn bắt đầu thread này nó sẽ làm vòng lặp, và xử lý việc tạo ra QPolygonF và trên mỗi bước nó sẽ phát ra tín hiệu và ngủ. Bạn có thể kết nối tín hiệu này với một phương thức trên cửa sổ chính của bạn sẽ nhận được đa giác mới, gán nó cho self._polygon và gọi update(), sau đó sẽ chỉ lấy self._polygon và tô nó. Ý tưởng ở đây là để di chuyển càng nhiều càng nhiều càng tốt vào sợi chỉ, và chỉ nói cho thread GUI chính của bạn để sơn lại với các giá trị mới.

Các vấn đề liên quan