It's not perfect as if you drag an endpoint, the control point nearest to it doesn't move. But it illustrates the basic idea. ```python # # Aim: single cubic segment with draggable handles # import sys from PySide6.QtCore import * from PySide6.QtGui import * from PySide6.QtWidgets import * from PySide6.QtNetwork import * class Handle: def __init__(self,x=0,y=0,r=10,shape="square",color=None): self.x = x self.y = y self.r = r self.shape = shape if color is not None: self.color = color else: self.color = QColor.fromRgb(0,0,0) def hit(self,x,y): if self.shape == "square": if (x < self.x - self.r) or (x > self.x + self.r) or (y < self.y - self.r ) or (y > self.y + self.r): return False return True elif self.shape == "circle": dx = x - self.x dy = y - self.y r2 = self.r * self.r dist2 = dx*dx + dy*dy return dist2 <= r2 else: print(f"Invalid shape {self.shape}") return false def mouseMove(self,x,y): self.x = x self.y = y def paint(self,painter): x = self.x y = self.y r = self.r rect = QRect(x-r,y-r,2*r,2*r) path = QPainterPath() if self.shape == "square": path.addRect(rect) elif self.shape == "circle": path.addEllipse(rect) else: print(f"Invalid shape {self.shape}") return painter.save() painter.setBrush(self.color) painter.setPen(Qt.NoPen) painter.drawPath(path) painter.restore() class Cubic: def __init__(self,x0,y0,xa,ya,xb,yb,x1,y1,color=None,width=5): self.p0 = Handle(x0,y0,10,"square",QColorConstants.Red) self.p1 = Handle(x1,y1,10,"square",QColorConstants.Green) self.pa = Handle(xa,ya,10,"circle",QColorConstants.Blue) self.pb = Handle(xb,yb,10,"circle",QColorConstants.Magenta) self.handles = [self.pa,self.pb,self.p0,self.p1] if color is not None: self.color = color else: self.color = QColor.fromRgb(0,0,0) self.width = width def hit(self,x,y): for handle in self.handles: if handle.hit(x,y): return handle return None def paint(self,painter): pa,pb,p0,p1 = self.handles path = QPainterPath() path.moveTo(p0.x,p0.y) path.cubicTo(pa.x,pa.y,pb.x,pb.y,p1.x,p1.y) painter.save() painter.setPen(QPen(self.color,self.width)) painter.setBrush(Qt.NoBrush) painter.drawPath(path) painter.setPen(QPen(QColorConstants.Black,1)) path = QPainterPath() path.moveTo(p0.x,p0.y) path.lineTo(pa.x,pa.y) painter.drawPath(path) path = QPainterPath() path.moveTo(p1.x,p1.y) path.lineTo(pb.x,pb.y) painter.drawPath(path) for handle in self.handles: handle.paint(painter) painter.restore() class MyWidget(QWidget): def __init__(self,*xs,**kw): super().__init__(*xs,**kw) self.cubic = Cubic(50,50,150,50,150,150,250,150) self.sel = None def paintEvent(self,event): with QPainter(self) as painter: self.cubic.paint(painter) def mousePressEvent(self,event): pos = event.position().toPoint() x,y = pos.x(),pos.y() if handle := self.cubic.hit(x,y): self.sel = handle return super().mousePressEvent(event) def mouseReleaseEvent(self,event): self.sel = None return super().mouseReleaseEvent(event) def mouseMoveEvent(self,event): if self.sel is not None: pos = event.position().toPoint() x,y = pos.x(),pos.y() self.sel.mouseMove(x,y) self.update() return super().mouseMoveEvent(event) app = QApplication(sys.argv) win = QMainWindow() w = MyWidget() win.setCentralWidget(w) win.setFixedSize(640,512) win.show() exit(app.exec()) ```