0 ベクタパス作成ツール 5

190721

ポイントの挿入と削除

  • 出来た(・∀・)ノ
import sys , os , pickle
from PyQt5.QtWidgets import QApplication, QWidget,QMessageBox,QMenu,QAction
from PyQt5.QtCore import QPointF ,QRectF ,Qt,QDataStream,QIODevice,QDataStream,QFile
from PyQt5.QtGui import QPainterPath ,QPainter ,QPen,QCursor

class mypath(QPainterPath):
    def __init__(self,p):
        super().__init__(p)
        self.pointrect=[]
        self.a = QPointF(4,4) # Tolerance in detecting point
        self.closed=0

    def makepointrect(self): # ポイント変更の都度呼びたいけど
        self.pointrect.clear()
        for i in range(self.elementCount() ): # 0か2の前の3  or 0か二つ目の3
            self.pointrect.append( [ i , QRectF( QPointF( self.elementAt(i).x,self.elementAt(i).y) -self.a  ,
                QPointF( self.elementAt(i).x,self.elementAt(i).y) + self.a)  ] )

    def setclosed(self,flag):
        self.closed=flag
    def index(self,p):
        max=self.elementCount()
        if self.closed : 
            if p==-1:
                return max-2
            elif p==max:
                return max-1
            else :
                return p
        else :
            if p < 0 :
                return 0
            elif p > max -1 :
                return max -1
            else :
                return p

    def movepoint(self, p , pos , flag ): # flag : true-->sync
        if flag :
            lam = lambda q: QPointF(self.elementAt(q).x , self.elementAt(q).y )
            type1,type2 = self.elementAt(p).type ,self.elementAt(self.index(p-1)).type 
            if type1==2 :                  # cp symmetry first
                self.movepoint( self.index(p-2) , lam(self.index(p-1)) + lam(self.index(p-1)) - pos ,0) # (p-1) + pos->(p-1)
            elif type1==3 and type2==2 :   # cp symmetry second
                self.movepoint( self.index(p+2) , lam(self.index(p+1)) + lam(self.index(p+1)) - pos ,0) # (p+1) + pos->(p+1)
            else :                                     # point parerell
                self.movepoint( self.index(p+1) , lam(self.index(p+1)) - lam(p) + pos , 0) # (p+1) + p->pos
                self.movepoint( self.index(p-1) , lam(self.index(p-1)) - lam(p) + pos , 0) # (p-1) + p->pos
        if self.closed:
            if p==0:
                self.setElementPositionAt( self.elementCount()-1 , pos.x() , pos.y() )
        self.setElementPositionAt( p , pos.x() , pos.y() )
        #####ここにrect計算を書く
        self.makepointrect()
    def delpoint(self,p):
        if p <0 :
            print('select point')
            return
        list=[ QPointF(self.elementAt(l).x,self.elementAt(l).y) for l in range(self.elementCount())]
        if p == self.elementCount()-1 :  # last point
            del list[p-2:]
        elif p==0 :
            del list[0 : 3]
        else:
            del list[p-1 : p+2]
        self.remakepath(list)
    def insertpoint(self,p):
        if p <0 or p > self.elementCount() :
            print('select point')
            return
        # vec = QPointF( (self.elementAt(p).x - self.elementAt(p-3).x )/4 ,
        #     (self.elementAt(p).y - self.elementAt(p-3).y )/4 )
        # po = QPointF( self.elementAt(p-3).x , self.elementAt(p-3).y )
        # list1=[ po+vec , po+vec+vec , po+ vec+vec+vec ]
        p1=(self.elementAt(p-3).x , self.elementAt(p-3).y )
        p2=(self.elementAt(p-2).x , self.elementAt(p-2).y )
        p3=(self.elementAt(p-1).x , self.elementAt(p-1).y )
        p4=(self.elementAt(p).x , self.elementAt(p).y )
        tt=1/4
        list1=[]
        for l in range(1,4):      # Bezier calculate
            t=l*tt
            x = (1-t)**3*p1[0] + 3*(1-t)**2*t*p2[0] + 3*(1-t)*t**2*p3[0] + t**3*p4[0]
            y = (1-t)**3*p1[1] + 3*(1-t)**2*t*p2[1] + 3*(1-t)*t**2*p3[1] + t**3*p4[1]
            list1.append(QPointF( x , y))

        list=[ QPointF(self.elementAt(l).x,self.elementAt(l).y) for l in range(self.elementCount())]
        list[ p-1 : p-1 ] = list1
        self.remakepath(list)
    def remakepath(self,list):
        path = QPainterPath( list[0] )
        for i in range (1, len(list) ,3):
            path.cubicTo( list[i] , list[i+1] , list[i+2] )
        self.__init__(path)
        self.makepointrect()

    def contains_m(self,  p ) : # QPointF p
        for l in self.pointrect :
            if l[1].contains(p) :
                return l[0] #        hit point
        if self.contains(p):
            return -1 #               contain but point
        else : 
            return -2 #                not contain

class makepath(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.path = QPainterPath( self.plist[0] )
        self.path = mypath(QPointF(10,250)  )
        for i in range (1, len(self.plist) ,3):
            self.path.cubicTo( self.plist[i] , self.plist[i+1] , self.plist[i+2] )

        self.a = QPointF(4,4) # Tolerance in detecting point
        self.pselected = -1             # selected point
        self.pmoving = 0                # move index

        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect( self.custommenu )

        self.show()
    def custommenu(self):
        menu=QMenu(self)
        action = QAction('Add Point', self)
        action.triggered.connect(self.addpoint)
        menu.addAction(action)
        action = QAction('Insert Point', self)
        action.triggered.connect(self.insertpoint)
        menu.addAction(action)
        action = QAction('Dell Point', self)
        action.triggered.connect(self.delpoint)
        menu.addAction(action)
        menu.addSeparator()

        action = QAction('Close Path', self)
        action.triggered.connect(self.pathclose)
        menu.addAction(action)
        action = QAction('Open Path', self)
        action.triggered.connect(self.pathopen)
        menu.addAction(action)
        menu.addSeparator()

        action = QAction('Point Info', self)
        action.triggered.connect(self.showelem)
        menu.addAction(action)

        action = QAction('Save Path', self)
        action.triggered.connect(self.pathsave)
        menu.addAction(action)
        action = QAction('Load Path', self)
        action.triggered.connect(self.pathload)
        menu.addAction(action)

        menu.exec_(QCursor.pos ())   

    def pathsave(self):
        fname=os.path.join(os.path.dirname(__file__) , 'qtsave')
        f = QFile(fname)
        f.open( QIODevice.WriteOnly )
        ds = QDataStream(f)
        ds.__lshift__(self.path)
        f.close() 
    def pathload(self):
        self.path=mypath(QPointF(0,0))
        fname=os.path.join(os.path.dirname(__file__) , 'qtsave')
        f = QFile(fname)
        f.open( QIODevice.ReadOnly)
        ds = QDataStream(f)
        ds.__rshift__(self.path)
        f.close() 
        self.path.makepointrect()
        self.update()
        # self.showelem()

    def pathclose(self):
        self.path.setclosed(1)
        self.path.movepoint( 0 ,QPointF( self.path.elementAt(0).x,self.path.elementAt(0).y ),0)
        self.update()
    def pathopen(self):
        self.path.setclosed(0)

    def showelem(self):
        print('show element')
        for i in range(self.path.elementCount() ):
            print('format: {} x: {} y: {}'.format( self.path.elementAt(i).type ,
                            self.path.elementAt(i).x , self.path.elementAt(i).y ) )

    def addpoint(self): # ctrl + click
        e=self.mapFromGlobal( QCursor.pos() )
        p=QPointF( self.path.elementAt(self.path.elementCount()-1).x,
            self.path.elementAt(self.path.elementCount()-1).y )
        q = QPointF( (e.x() - p.x()) /3 , (e.y() - p.y())/3 )
        self.path.cubicTo(p+q,p+q+q,p+q+q+q)
        self.path.makepointrect()
        self.update()
    def delpoint(self):
        self.path.delpoint(self.pselected)
        self.update()
    def insertpoint(self):
        self.path.insertpoint(self.pselected)
        self.update()

    def mouseMoveEvent(self ,e):
        if self.pmoving == -1 : # not Dragging
            l = self.path.contains_m( e.pos() )  # <------------判定メソッド
            if self.pselected >= 0 : # point selecting
                if l >= self.pselected-1 and l <= self.pselected+1 :
                    return             # same point -- ignore
                elif l< 0 :
                    return
                else :
                    type1=self.path.elementAt(l).type
                    type2=self.path.elementAt(l-1).type
                    if type1==0 or type1==1 or (type1==3 and type2==3):
                        self.pselected = l # reselect point
                        self.setWindowTitle('selected point {}'.format(l))
            else:
                if self.pselected == l :  # case of -1 -2 , same--ignore
                    return
                else :
                     self.pselected = l
        else : # dragging 
            self.path.movepoint( self.pmoving ,e.pos(), e.modifiers().__eq__(Qt.ShiftModifier))
        self.update()

    def mousePressEvent(self ,e):
        if e. modifiers().__eq__(Qt.ControlModifier): # ctrl 
            if self.pselected==-2:
                self.addpoint()
                return
            elif self.pselected==-1:
                self.addpoint()
                return
            else:
                self.insertpoint()
                return

        l = self.path.contains_m( e.pos() )  # <------------判定メソッド
        if l >= 0 :
            self.pmoving = l
            self.setWindowTitle('moving point {}'.format(l))

    def mouseReleaseEvent(self ,e):
        self.pmoving = -1
        if self.path.contains_m( e.pos() ) == -2 : 
            self.pselected = -2   # release selected mode
            self.update()

    def paintEvent (self,e):
       
        painter= QPainter()
        painter.begin(self) 
        if self.pselected==-2:
            painter.setPen( QPen( Qt.black , 2, style=Qt.SolidLine ) )
            painter.drawPath(self.path)
        elif self.pselected >=-1:
            painter.setPen( QPen( Qt.blue , 2, style=Qt.SolidLine ) )
            painter.drawPath(self.path)
            for i,l in enumerate(self.path.pointrect) :
                type1=self.path.elementAt(i).type
                type2=self.path.elementAt(i-1).type
                if type1==0 or type1==1 or (type1==3 and type2==3):
                    painter.drawRect(l[1])
        if self.pselected >=0:
            painter.setPen( QPen( Qt.red , 2, style=Qt.SolidLine ) )
            p = QPointF( self.path.elementAt(self.pselected).x , self.path.elementAt(self.pselected).y)

            p2 = QPointF( self.path.elementAt(1).x , self.path.elementAt(1).y) \
                if self.pselected==self.path.elementCount()-1 else \
                QPointF( self.path.elementAt(self.pselected+1).x , self.path.elementAt(self.pselected+1).y)

            p3 = QPointF( self.path.elementAt(self.path.elementCount()-2).x , 
                self.path.elementAt(self.path.elementCount()-2).y) if self.pselected==0 else \
                QPointF( self.path.elementAt(self.pselected-1).x , self.path.elementAt(self.pselected-1).y)
 
            painter.drawRect( QRectF( p-self.a , p+self.a)) 
            if self.pselected!=self.path.elementCount()-1 :
                painter.drawLine( p , p2 ) 
                painter.drawRect( QRectF( p2-self.a , p2+self.a)) 
            if self.pselected!=0  or self.path.closed :
                painter.drawLine( p , p3 ) 
                painter.drawRect( QRectF( p3-self.a , p3+self.a)) 

        painter.end() 

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

  • 考え方
    派生クラスの内部で実装
    ①内部でポイントをリストに書き出す
    ②リストベースで追加削除の操作をする
    ③リストを元に操作パスを作成して自分自身にセットする

  • 閉じているか開いているかで動作は違うか??

  • 削除するポイントpを選択させてから実行
    ①始点 p , p+1 , p+2
    ②終点 p , p-1 , p-2
    ③中点 p , p+1 , p-1
    これで場合分けする

  • 挿入するポイントpを選択して実行(前に挿入)
    ・p-1 の位置に三点作って挿入する
    ・pとp-3をベジェカーブで四分割した時の三点を挿入する
    ・始点の選択は許さない(終点に追加すればいいから)


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

0

メモを他の人に見せる

このメモを見せたい人に、このURL(今開いているページのURLです)を教えてあげてください

コメント(0)

  • someone

  • someone