PyQt6实现N阶贝塞尔曲线
先看效果
贝塞尔曲线的教程以及代码网上资料蛮多的,这里也不做过多叙述。
制作思路:鼠标右键点击生成新点,左键移动点调节贝塞尔曲线,生成新点和移动点时调用self.update()函数刷新控件,实现实时调节贝塞尔曲线,完整代码如下。
import sys
from PyQt6.QtGui import *
from PyQt6.QtGui import QPaintEvent
from PyQt6.QtWidgets import *
from PyQt6.QtCore import *
import math
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.points = [] # 存储所有点
self.current_point_index = -1 # 记录当前点的索引
self.setGeometry(300, 300, 800, 600)
self.setWindowTitle('Bézier curve')
self.show()
def mouseMoveEvent(self, e: QMouseEvent | None) -> None:
if e.buttons() == Qt.MouseButton.LeftButton and self.current_point_index != -1:
self.points[self.current_point_index] = e.pos()
self.update()
def mouseReleaseEvent(self, e: QMouseEvent | None) -> None:
self.current_point_index = -1
def mousePressEvent(self, e: QMouseEvent | None) -> None:
if e.button() == Qt.MouseButton.RightButton:
self.points.append(e.pos())
self.update()
elif e.button() == Qt.MouseButton.LeftButton:
for i,p in enumerate(self.points):
if pow(e.pos().x() - p.x(), 2) + pow(e.pos().y() - p.y(), 2) < 30:
self.current_point_index = i
break
def paintEvent(self, e: QPaintEvent | None) -> None:
qp = QPainter()
qp.begin(self)
qp.setRenderHint(QPainter.RenderHint.Antialiasing)
# 绘制点
self.drawPoint(qp)
# 绘制曲线
self.drawBezierCurve(qp)
qp.end()
def drawPoint(self, qp:QPainter):
pen = QPen(Qt.GlobalColor.red, 15)
pen.setCapStyle(Qt.PenCapStyle.RoundCap)
for i,p in enumerate(self.points):
qp.setPen(pen)
qp.drawPoint(p)
# 绘制索引
qp.setPen(Qt.GlobalColor.white)
rect = QFontMetrics(qp.font()).boundingRect(str(i))
qp.drawText(p.toPointF() + QPointF(-rect.width() / 2, rect.height() / 2 - 2.0), str(i))
def drawBezierCurve(self, qp:QPainter):
# 贝塞尔曲线
n = len(self.points) - 1
if n <= 0:
return
pos = []
step = 0.001 # 精度
t = 0
while t <= 1:
b = []
for j in range(n + 1):
b.append(math.comb(n, j) * pow(t, j) * pow(1 - t, n - j))
x = y = 0
for j in range(n + 1):
x += self.points[j].x() * b[j]
y += self.points[j].y() * b[j]
pos.append(QPointF(x, y))
t += step
pos.append(QPointF(self.points[-1]))
# 开始绘制
qp.setPen(QPen(Qt.GlobalColor.black, 3))
path = QPainterPath()
path.moveTo(pos[0])
for i in range(len(pos) - 1):
path.lineTo(pos[i + 1])
qp.drawPath(path)
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec())
if __name__ == '__main__':
main()