2012-04-24 3 views
10

나는 포인트 목록을 가지고있다. 예 : points = [[160, 75], [115, 567]]. PyQt4에서 애니메이션이있는 선을 그리는 방법

어떻게 PyQt4에 선을 그립니다, 그래서는 다음과 같이 될 것이다 : 사전에 Line

감사합니다.

편집 : 기록을 위해, 나는 베 지어 곡선을 구현하기 위해 노력하고있어, 그래서이처럼 보였다 :

from PyQt4.QtGui import QWidget, QPolygonF, QPainter, QPen, QBrush, QColor, \ 
    QApplication, QIcon, QVBoxLayout, QSlider, QHBoxLayout, QPushButton, QLCDNumber 
from PyQt4.QtCore import QObject, SIGNAL, SLOT, QPointF, Qt, QRectF, QPointF 
import time, sys 

#================================================================ 
# For the next block I used this post 
# http://stackoverflow.com/a/3220819/736306, 
# but I wish to replace it with self.liniar_bez(), 
# biliniar_bez(), self.cubic_bez() and self.fourG_bez() 
# because I want to control 't' with self.slider 

def avg(a, b): 
    xa, ya = a 
    xb, yb = b 
    return (xa + xb) * 0.5, (ya + yb) * 0.5 

def bez4split(p0, p1, p2, p3, p4): 
    p01 = avg(p0, p1) 
    p12 = avg(p1, p2) 
    p23 = avg(p2, p3) 
    p34 = avg(p3, p4) 
    p012 = avg(p01, p12) 
    p123 = avg(p12, p23) 
    p234 = avg(p23, p34) 
    p= avg(p012, p123) 
    p1234 = avg(p123, p234) 
    p= avg(p0123, p1234) 
    return [(p0, p01, p012, p0123, p), 
     (p, p1234, p234, p34, p4)] 

def bez4(p0, p1, p2, p3, p4, levels=5): 
    if levels <= 0: 
     return [p0, p4] 
    else: 
     (a0, a1, a2, a3, a4), (b0, b1, b2, b3, b4) = bez4split(p0, p1, p2, p3, p4) 
     return (bez4(a0, a1, a2, a3, a4, levels - 1) + 
       bez4(b0, b1, b2, b3, b4, levels - 1)[1:]) 
#================================================================ 

points = [[160, 75], [115, 567], [292, 58], [685, 194], [734, 517]] 
coords = bez4(*points) 

class Bezier(QWidget): 
    def __init__(self, parent=None): 
     QWidget.__init__(self, parent) 

     self.lcd = QLCDNumber(self) 
     self.slider = QSlider(Qt.Horizontal, self) 
     self.slider.setRange(0, 100) 
     self.closeButton = QPushButton('Close') 

     bottomLayout = QHBoxLayout() 
     bottomLayout.addWidget(self.lcd) 
     bottomLayout.addWidget(self.slider) 
     bottomLayout.addWidget(self.closeButton) 

     mainLayout = QVBoxLayout() 
     mainLayout.addStretch(1) 
     mainLayout.addLayout(bottomLayout) 

     self.setLayout(mainLayout) 

     self.time = 0 
     self.time_step = 0.025 
     self.timer_id = self.startTimer(1) 

     self.tracking = None 

     QObject.connect(self.closeButton, SIGNAL('clicked(bool)'), app.exit) 
     QObject.connect(self.slider, SIGNAL('valueChanged(int)'), self.lcd, SLOT('display(float)')) 
     #self.slider.valueChanged.connect(self.lcd.display) 

     self.setWindowTitle('Bonus Example') 

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

    def bezAnimation(self): 
     painter = QPainter(self) 
     painter.setRenderHints(QPainter.Antialiasing) 

     pts = points[:] 
     crds = coords[:] 

     painter.setPen(QPen(QColor(Qt.lightGray), 3, Qt.DashDotDotLine)) 
     painter.drawPolyline(self.poly(pts)) 

     painter.setBrush(QBrush(QColor(255, 025, 0))) 
     painter.setPen(QPen(QColor(Qt.lightGray), 1)) 
     for x, y in pts: 
      painter.drawEllipse(QRectF(x - 4, y - 4, 8, 8)) 

    def paintEvent(self, event): 
     self.bezAnimation() 

    # As you know, 0.00 > 't' > 1.00 
    # and QSlider does not provide support for float numbers, 
    # so we simply divide t by 100 
    def liniar_bez(self, p, t): 
     t = t/100.0 
     new_p[0] = (1 - t) * p[0][0] + t * p[1][0] 
     new_p[1] = (1 - t) * p[0][1] + t * p[1][1] 

    def biliniar_bez(self, p, t): 
     t = t/100.0 
     new_p[0][0] = ((1 - t) ** 2) * p[0][0] + 2 * (1 - t) * t * p[1][0] + (t ** 2) * p[2][0] 
     new_p[0][1] = ((1 - t) ** 2) * p[0][1] + 2 * (1 - t) * t * p[1][1] + (t ** 2) * p[2][1] 

    def cubic_bez(self, p, t): 
     t = t/100.0 
     new_p[0][0] = ((1 - t) ** 3) * p[0][0] + 3 * ((1 - t) ** 2) * t * p[1][0] + (t ** 2) * p[2][0] + (t ** 3) * p[3][0] 
     new_p[0][1] = ((1 - t) ** 3) * p[0][1] + 3 * ((1 - t) ** 2) * t * p[1][1] + (t ** 2) * p[2][1] + (t ** 3) * p[3][1] 

    def fourG_bez(self, p, t): 
     t = t/100.0 
     new_p[0][0] = ((1 - t) ** 4) * p[0][0] + 4 * ((1 - t) ** 3) * t * p[1][0] + 4 * (1 - t) * (t ** 2) * p[2][0] + 4 * (1 - t) * (t ** 3) * p[3][0] + (t ** 4) * p[4][0] 
     new_p[0][1] = ((1 - t) ** 4) * p[0][1] + 4 * ((1 - t) ** 3) * t * p[1][1] + 4 * (1 - t) * (t ** 2) * p[2][1] + 4 * (1 - t) * (t ** 3) * p[3][1] + (t ** 4) * p[4][1] 

    def timerEvent(self, event): 
     if self.timer_id == event.timerId(): 
      #self.bezAnimation() 
      self.time += self.time_step 
      self.update() 

    def closeEvent(self, event): 
     self.deleteLater() 

    #================================================================ 
    # And this one too 
    # http://stackoverflow.com/a/3220819/736306 
    def mousePressEvent(self, event): 
     i = min(range(len(points)), 
      key=lambda i: (event.x() - points[i][0]) ** 2 + 
         (event.y() - points[i][1]) ** 2) 

     self.tracking = lambda p: points.__setitem__(i, p) 

    def mouseMoveEvent(self, event): 
     if self.tracking: 
      self.tracking((event.x(), event.y())) 
      self.update() 

    def mouseReleaseEvent(self, event): 
     self.tracking = None 
    #================================================================ 

if __name__ == '__main__': 
    app = QApplication(sys.argv) 
    form = Bezier() 
    form.setGeometry(10, 30, 871, 727) 
    form.show() 
    sys.exit(app.exec_()) 

: 여기 Quartic Bézier curve

내가 지금 가지고있는 코드입니다 그래서,이 문제를 해결하는 방법에 대한 나의 생각은 다음과 같습니다. 1. self.biliniar_bez(), ..., self.fourG_bez() 으로 모든 커브를 Buid합니다. 2. 그들은 같은 점을 공유하므로 실제로이 점에서 일치합니다. 전철기. 3. 이동 점

문제는의 방법이다 (I 자신의 위치가 t와 일치해야한다는 생각 때문에 t = 0.25는 모든 지점은 라인이 시간 분기에 통과해야 할 경우) 움직이는 점. 저는 모든 선의 첫 번째와 두 번째 점의 좌표를가집니다. 중간이 필요합니다.

+0

아직 시도한 적이 있습니까? 너는 어떤 부분에 붙어 있니? 움직이는 물결에 대한 다른 질문에 답하는 것을 기억합니다. 그래서 이것이 당신이 올바르게 시작했을 것이라고 생각 했습니까? http://stackoverflow.com/questions/9465047/make-an-animated-wave-with-drawpolyline-in-pyside-pyqt. 분명히 당신이 붙인 수학이 아닙니다 ... 그래서 당신은 더 구체적으로 문제가 있습니까? – jdi

답변

2
  1. 당신이 QGraphics와 Graphics를 사용하는 경우 다음 QPainter.drawPath()
  2. 보고 될 것이다 정상 위젯을 사용하는 경우, 당신은 유사한 방법 어디 것이 두 가지의 QGraphicsPathItem

어느 쪽을보고 할 것 그리는 동안 시간이 지남에 따라 점진적으로 선을 만들어야합니다. 시간 경과에 따라 선을 그리는 방법에 대한 구체적인 예제를 검토하십시오. Make an animated wave with drawPolyline in PySide/PyQt

예를 들어, QGraphics를 사용하는 경우 장면에 QGraphicsPathItem 인스턴스가 있습니다. 그런 다음 전체 경로를 점진적으로 설정하여 QPainterPath를 루핑하고 업데이트하여 애니메이션을 만듭니다. http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qpainterpath.html#cubicTo

+0

나는 몇 가지 코드를 추가하고 해결 아이디어를 게시했지만, 제 의견으로는 충분하지 않습니다. – SaulTigh

+0

@Saul_Tigh :이 코드 예제를보다 작고 실행 가능한 문제로 나눌 수있는 방법이 있습니까? 모든 코드를 통해 귀하의 문제를 해결할 수있는 위치가 확실치 않습니다. 또한, 그 runnable 어쨌든. 'self.updatePoints (painter)'는 없습니다. 특정 질문을해야합니다. 이것은 너무 모호합니다. – jdi

+0

내 질문은 두 개의 다른 점 (첫 번째 이미지 에서처럼) 사이의 한 지점을 이동하여 선을 그리는 방법에 관한 것입니다. 나는 2 점 (그들 사이에 포인트가 없다)과 그 원인이 내 문제. – SaulTigh

2

Qt에 선을 그리려면 페인트 이벤트에서 아래 코드를 참조하십시오.

def paintEvent(self, event): 
    painter = Qt.QPainter() 
    painter.begin(self) 
    painter.pen().setWidth(int width) 
painter.setPen(Qt.QColor(100,100,100)) 
    painter.drawLine(x1,y1,x2,y2) 
    painter.end() 
    Qt.QFrame.paintEvent(self, event) 
관련 문제