PyQt でベジェ曲線を描画してマウスでグリグリする

190701

  • Qtのパス操作で、path.cubicTo を使えばベジェが書けるのは知ってたけど、自分で数字を与えて希望通りの曲線になるはずも無く、現実には使えてなかった

  • ふとマウスでグリグリするアイデアを思いついたので早速試してみたら意外と簡単にできたので公開してみます
    こんな感じ、各ポイントをマウスで自由に移動できます

  • コードです
    環境はPython 3.72 と PyQt 5.12です

import sys
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtCore import QPointF ,QRectF ,Qt
from PyQt5.QtGui import QPainterPath ,QPainter ,QPen

class App(QWidget):

    def __init__(self):
        super().__init__()
        self.setWindowTitle('hogetta')
        self.setGeometry( 500, 50 , 500 , 500 )
        self.setMouseTracking (True)  # <----------------*****
        self.plist=[]
        self.plist.append( QPointF(10,250) )  # start point
        self.plist.append( QPointF(100,100) )  #
        self.plist.append( QPointF(300,200) )  #
        self.plist.append( QPointF(490,250) )  #
        self.a = QPointF(4,4) # Tolerance in detecting point
        self.pselected = -1             # selectedpoint
        self.pmove = 0                  # move flag
        self.show()

    def mouseMoveEvent(self ,e):
        if self.pmove :  # Dorag ing
            self.plist[self.pselected] = e.pos()
            self.update()
        else:            # non Dorag ing
            self.pselected = -1
            rc = QRectF ( e.pos() - self.a , e.pos() + self.a )
            for i, l in  enumerate( self.plist ) :
                if rc.contains(  l ) :
                    self.pselected = i
                    # self.setWindowTitle('HIT  {}'.format(str(i)))
                    self.update()
                    break
    def mousePressEvent(self ,e):
        if self.pselected != -1 :
            self.pmove = True
    def mouseReleaseEvent(self ,e):
        self.pmove = False
        self.pselected = -1

    def paintEvent (self,e):
        # 与えられたパラメータでパスを設定した後でパスをドローする
        path = QPainterPath( self.plist[0] )
        path.cubicTo( self.plist[1] , self.plist[2] , self.plist[3] )

        painter= QPainter()
        painter.begin(self) 
        painter.setPen( QPen( Qt.black , 2, style=Qt.SolidLine ) )
        painter.drawPath(path)

        painter.setPen( QPen( Qt.red , 2, style=Qt.DotLine ) )
        painter.drawLine( self.plist[0] , self.plist[1] ) # sub
        painter.drawLine( self.plist[2] , self.plist[3] ) # sub

        if self.pselected != -1:
            painter.drawRect( QRectF(QPointF(self.plist[self.pselected]-self.a) ,
                QPointF(self.plist[self.pselected]+self.a) ))

        painter.end() 

if __name__ == "__main__":
    app = QApplication(sys.argv)
    ex = App()
    sys.exit(app.exec_())

解説的なこと

  • ウイジェットを一つ作って(500-500)そこに直接描画してます
  • 最初のキューブは値をリストで与えてますが、端点は四つ、これをマウスでグリグリ
  • 端点の上にマウスを持って行けば■が表示されるようにして、その状態でドラッグすれば移動できます
  • とりあえずの実験です
    このキューブをつなげて複数にしていけば普通にベクターグラフィックの世界に入れると思います

参考

  • 一から学ぶベジェ曲線
    https://postd.cc/bezier-curves/
    このサイト素晴らしい、わかりやすい、動くページ素晴らしいですね
  • ペイントの基本
    https://qiita.com/kenasman/items/87c12f3a8b63f5948153
    ここはQtのドキュメントの邦訳
    いきなり読んでもわけわかんないけど振り返って読むとああそういうことだったんだなあと納得できたりします
    でもQtやるんなら英語ですが公式ドキュメントを読むようにした方が良いと思う、頑張って

→ 戻る

--> PyQt でベクターオブジェクト作成ツールを作る #

END

Close