问题描述:

I have a list of points. For example, points = [[160, 75], [115, 567]].

How to draw a line in PyQt4, so it would be something like this:

Thanks in advance.

EDIT: For the record, I'm trying to implement Bezier Curves, so it looked like this:

Here is the code I have at the moment:

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)

p0123 = avg(p012, p123)

p1234 = avg(p123, p234)

p01234 = avg(p0123, p1234)

return [(p0, p01, p012, p0123, p01234),

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

So, my idea about how to solve this problem:

1. Buid all curves with self.biliniar_bez(),..., self.fourG_bez()

2. They share the same points, so they'll actually match in this points.

3. Move points (I think, that their position must match with t, so if t=0.25 then every point must pass at this time quarter of their lines)

The problem is in the way of moving points. I have coordinates of first and second point of every line. Need middle ones.

网友答案:

Refer the below code in your paint event to draw a line in 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)
网友答案:
  1. If you are using normal widgets then you would be looking at QPainter.drawPath()
  2. If you are using QGraphics, then you would be looking at QGraphicsPathItem

Either of these two would have similar approaches where during your draw you would have to build the line incrementally over time. Review your own previous question for a specific example of how to draw a line over time: Make an animated wave with drawPolyline in PySide/PyQt

As an example, if you were using QGraphics, then you would have a QGraphicsPathItem instance in your scene. Then you would animate it by looping and updating QPainterPath by incrementally setting the complete path: http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qpainterpath.html#cubicTo

相关阅读:
Top