--- Title: ベクタパス作成ツール 1 Author: yamasyuh68 Web: https://mimemo.io/m/mqLXOlJV06lzQ19 --- # とりあえず出来た( ´∀`) - ベクタパス作成ツール - 前に作ったベジェ操作ウィジェットの発展型で、割とうまく出来た ポイント追加と端点操作が自由に出来てデータの保存・読込も出来る https://live.staticflickr.com/65535/48307147471_1acc28b2b3_z.jpg ``` import sys , os , pickle from PyQt5.QtWidgets import QApplication, QWidget,QMessageBox,QMenu,QAction from PyQt5.QtCore import QPointF ,QRectF ,Qt from PyQt5.QtGui import QPainterPath ,QPainter ,QPen,QCursor 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.a = QPointF(4,4) # Tolerance in detecting point self.pselected = -1 # selected point self.pmove = 0 # move flag self.drawp=0 # selected start point self.isclosed=0 # closed flag 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('Close Path', self) action.triggered.connect(self.closepath) menu.addAction(action) action = QAction('Open Path', self) action.triggered.connect(self.openpath) menu.addAction(action) action = QAction('Save Path', self) action.triggered.connect(self.topickle) menu.addAction(action) action = QAction('Load Path', self) action.triggered.connect(self.frompickle) menu.addAction(action) menu.exec_(QCursor.pos ()) def addpoint(self): # ctrl + click e=self.mapFromGlobal( QCursor.pos() ) p = QPointF( (e.x() - self.plist[-1].x())/3 , (e.y() - self.plist[-1].y())/3 ) self.plist.append( self.plist[-1] + p ) self.plist.append( self.plist[-1] + p ) self.plist.append( self.plist[-1] + p ) # new start point self.update() def closepath(self): self.plist[-1] = self.plist[0] self.isclosed=1 # set close flag self.update() def openpath(self): self.isclosed=0 # set close flag def topickle(self): fname=os.path.join(os.path.dirname(__file__) , 'hoge') with open( fname, 'wb') as f : pickle.dump( self.plist ,f) def frompickle(self): fname=os.path.join(os.path.dirname(__file__) , 'hoge') with open( fname, 'rb') as f : self.plist=pickle.load( f) self.update() def mouseMoveEvent(self ,e): if self.pmove : # Dorag ing if self.isclosed and self.pselected==0 : self.plist[-1]=e.pos() if e. modifiers().__eq__(Qt.ShiftModifier): # shift if self.drawp > self.pselected : self.plist[self.drawp+1]=self.plist[self.drawp]+(self.plist[self.drawp]-self.plist[self.pselected]) elif self.drawp < self.pselected : self.plist[self.drawp-1]=self.plist[self.drawp]+(self.plist[self.drawp]-self.plist[self.pselected]) else: self.plist[self.drawp+1] += ( e.pos() - self.plist[self.pselected]) self.plist[self.drawp-1] += ( e.pos() - self.plist[self.pselected]) 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.drawp = int( (i+1) / 3 )*3 # selected start point self.update() self.setWindowTitle('dawp {} selected {}'.format(self.drawp,self.pselected)) break def mousePressEvent(self ,e): if e. modifiers().__eq__(Qt.ControlModifier): # ctrl self.addpoint() elif self.pselected != -1 : self.pmove = True def mouseReleaseEvent(self ,e): self.pmove = False self.pselected = -1 def paintEvent (self,e): # 与えられたパラメータでパスを作成した後でパスをドローする→ mousemove に移したい path = QPainterPath( self.plist[0] ) for i in range (1, len(self.plist) ,3): path.cubicTo( self.plist[i] , self.plist[i+1] , self.plist[i+2] ) painter= QPainter() painter.begin(self) painter.setPen( QPen( Qt.black , 2, style=Qt.SolidLine ) ) painter.drawPath(path) for i in range( 0 , len(self.plist)-3 , 3 ) : # draw all start point painter.drawRect( QRectF(QPointF(self.plist[i]-self.a) , QPointF(self.plist[i] + self.a) )) painter.setPen( QPen( Qt.red , 2, style=Qt.DotLine ) ) painter.drawRect( QRectF(QPointF(self.plist[self.drawp]-self.a) , QPointF(self.plist[self.drawp] + self.a) )) if self.drawp==0: painter.drawLine( self.plist[self.drawp] , self.plist[self.drawp+1] ) painter.drawRect( QRectF(QPointF(self.plist[self.drawp+1]-self.a) , QPointF(self.plist[self.drawp+1] + self.a) )) painter.drawLine( self.plist[-1] , self.plist[-2] ) painter.drawRect( QRectF(QPointF(self.plist[self.drawp-2]-self.a) , QPointF(self.plist[self.drawp-2] + self.a) )) elif self.drawp== len(self.plist)-1 : painter.drawLine( self.plist[self.drawp] , self.plist[self.drawp-1] ) # sub painter.drawRect( QRectF(QPointF(self.plist[self.drawp-1]-self.a) , QPointF(self.plist[self.drawp-1] + self.a) )) else : painter.drawLine( self.plist[self.drawp] , self.plist[self.drawp-1] ) painter.drawLine( self.plist[self.drawp] , self.plist[self.drawp+1] ) painter.drawRect( QRectF(QPointF(self.plist[self.drawp+1]-self.a) , QPointF(self.plist[self.drawp+1] + self.a) )) painter.drawRect( QRectF(QPointF(self.plist[self.drawp-1]-self.a) , QPointF(self.plist[self.drawp-1] + self.a) )) if self.pselected != -1 : painter.setPen( QPen( Qt.blue , 2, style=Qt.DotLine ) ) 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 = makepath() sys.exit(app.exec_()) ``` - パスは全てcubu、ベジェに統一した - コントロールポイントはシフトキーでシンメトリに動く。基点はシフトキーで二つのコントロールポイントと平行に動く ただし始点と終点の動作はすっきりしてない この部分は今後の仕様も含めてしっかり考えた方が良いのかもしれない - 描画ルーチンがゴテゴテしてる、分岐が多すぎ、もっとすっきりさせたい - picklesのデータ保存は凄く簡単だったが、データに名前はつけれないんだろうか? - パスの派生クラスを作れば複数パスを並列して処理できそうだが?? - 現在のポイント追加は常に終点に加える形だが、途中に追加できるようにしたい ベジェの演算が必要になるんだろうか??? --- --> [PyQt でベクターオブジェクト作成ツールを作る # もくじ ](https://mimemo.io/m/3Rx1XoR367le95E)