--- Title: PyQt で画像にパスを加えるツールを作る 4 Author: yamasyuh68 Web: https://mimemo.io/m/vj5XN4jex3l86d7 --- 190816 # Transform - かなり悩んだが一応このテーマは終了にする 回転をマウスで綺麗に出来てないがホイールで問題ないのでこれ以上追求しない エクセルをはじめ多くのソフトで簡単にできるてことなので、コードを見てみたい アプローチは全然違うんだと思う - 回転はアイテム、スケールはパスが基本。アイテムに対して両方やると変になる。 移動はデフォルトに投げたが、自力だと回転させたときが難しかった マウスの動きはHoverで取得するので、アイテムのパスのBoundingRectを最初に作ってアイテムのパスはこれにしてしまう オリジナルのパスはアイテムの派生クラス内で別管理して、ただし描画は常時オリジナルを描画し、矩形は選択時のみ描画するようにした ``` import sys , os ,math from PyQt5.QtWidgets import (QApplication, QGraphicsItem, QGraphicsScene, QGraphicsView,QMenu,QAction, QGraphicsDropShadowEffect ,QGraphicsPathItem ) from PyQt5.QtGui import QPixmap , QColor , QPainterPath ,QPen,QCursor,QImage,QPainter,QTransform,\ QBrush from PyQt5.QtCore import Qt,QSizeF ,QPoint,QPointF ,Qt,QDataStream,QIODevice,QDataStream,QFile,QRect,QRectF class pathitem(QGraphicsPathItem): def __init__(self,path): path=path.translated( -path.boundingRect().x() , -path.boundingRect().y() ) self.original = path # originalpath self.item = path.translated(0,0) # copy p=QPainterPath() p.addRect(path.boundingRect()) super().__init__(p) self.setAcceptedMouseButtons(Qt.LeftButton) self.setFlag(QGraphicsItem.ItemIsSelectable,1) self.setFlag(QGraphicsItem.ItemIsMovable,1) self.setGraphicsEffect(QGraphicsDropShadowEffect()) self.setAcceptHoverEvents(1) self.distance=5.0 self.radius=1.0 self.pos=None # key press position --use drag self.dragflag=0 self.drawflag=0 self.angle = 0 self.makecrect() def makecrect(self): a=8 # size of drag rect br=self.path().boundingRect() # print(br) self.setTransformOriginPoint( br.width()/2 , br.height()/2 ) self.crect=[ QRectF( br.width() -a , br.height()-a , a,a ) , QRectF( 0 , br.height()-a , a,a ) ] def hoverEnterEvent(self,e): self.drawflag=1 def hoverLeaveEvent(self,e): self.drawflag=0 def hoverMoveEvent(self,event): self.dragflag=0 for i,l in enumerate(self.crect): if l.contains(event.pos()): self.dragflag=i+1 break if self.dragflag==1: self.setCursor(Qt.SizeFDiagCursor) elif self.dragflag==2: self.setCursor(Qt.PointingHandCursor) else: self.setCursor( Qt.OpenHandCursor) # Qt.SizeAllCursor def mousePressEvent(self, event): if self.dragflag: self.pos=event.pos() else : self.setCursor(Qt.ClosedHandCursor) super().mousePressEvent(event) def mouseReleaseEvent(self, event): self.dragflag=0 super().mouseReleaseEvent(event) def mouseMoveEvent(self, event): if self.dragflag==1 : # scaling br=self.boundingRect() scalex=(event.pos().x()-br.x()) /self.boundingRect().width() scaley=(event.pos().y()-br.y()) /self.boundingRect().height() qt = QTransform() qt.scale( scalex , scaley ) self.setPath( qt.map( self.path() ) ) self.item=qt.map( self.item ) self.makecrect() elif self.dragflag==2 : # rotating br=self.boundingRect() a,b = br.width()/2 , br.height()/2 # center a1,b1 = self.pos.x(),self.pos.y() # MousePress P a2,b2=event.pos().x(),event.pos().y() # MouseMove P a1,b1=a1-a , b1-b a2,b2=a2-a , b2-b cosθ = (a1*a2+b1*b2) / ( math.sqrt(a1**2+b1**2) * math.sqrt(a2**2+b2**2) ) deg= math.degrees(math.acos( cosθ )) # rad -> 度 self.setRotation(-deg ) print(a,b,a1,b1,a2,b2,deg) else: super().mouseMoveEvent(event) return def mouseDoubleClickEvent(self,e): # reset # print('kita dc') self.resetTransform() self.setRotation( 0 ) self.tx = 1 self.ty = 1 self.angle = 0 def keyPressEvent(self, event): if (event.modifiers() & Qt.ControlModifier): # ctrl if (event.key() == Qt.Key_Up): self.y += 0.1 elif (event.key() == Qt.Key_Down): self.y -= 0.1 elif (event.key() == Qt.Key_Q): self.setPath( self.original ) self.y=1 return elif (event.key() == Qt.Key_Right): self.x += 0.1 elif (event.key() == Qt.Key_Left): self.x -= 0.1 qt = QTransform() qt.scale( self.x, self.y ) self.setPath( qt.map( self.original ) ) self.makecrect() return if (event.modifiers() & Qt.ShiftModifier): # shift if (event.key() == Qt.Key_Up): self.distance += 0.5 elif (event.key() == Qt.Key_Down): self.distance -= 0.5 elif (event.key() == Qt.Key_Right): self.radius += 0.5 elif (event.key() == Qt.Key_Left): self.radius -= 0.5 self.graphicsEffect().setOffset(self.distance) self.graphicsEffect().setBlurRadius(self.distance) return if (event.key() == Qt.Key_Right): self.angle += 1 elif (event.key() == Qt.Key_Left): self.angle -= 1 elif (event.key() == Qt.Key_Up): self.scale += 0.1 elif (event.key() == Qt.Key_Down): self.scale -= 0.1 self.setScale(self.scale) self.setRotation(self.angle) self.makecrect() def wheelEvent (self, e):# mousewheel 引いたら-yが128 if (e.modifiers() & Qt.ShiftModifier): # shift if e.delta() < 0 : # increase self.angle += 1 else: self.angle -= 1 self.setScale(self.scale) else: if e.delta() < 0 : # increase self.angle += 5 else: self.angle -= 5 # self.setRotation(self.angle) self.setRotation(self.angle ) def paint(self,painter,option,n): if self.drawflag: painter.setPen( QPen( Qt.red , 2, style=Qt.DotLine ) ) # painter.setBrush( Qt.red ) painter.drawPath(self.path()) painter.setPen( QPen( Qt.black , 2, style=Qt.SolidLine ) ) for l in self.crect : painter.drawRect( l ) painter.setPen( QPen( Qt.red , 2, style=Qt.SolidLine ) ) painter.setBrush( Qt.red ) painter.drawPath(self.item) class SvgView(QGraphicsView): def __init__(self, parent=None): super(SvgView, self).__init__(parent) self.setScene(QGraphicsScene(self)) self.pixmap = QPixmap(r'') self.pix = self.scene().addPixmap(self.pixmap) fname=os.path.join(os.path.dirname(__file__) , 'qtpath.qtdata') f = QFile(fname) f.open( QIODevice.ReadOnly) ds = QDataStream(f) path=QPainterPath() ds.__rshift__(path) f.close() self.path=pathitem(path) self.scene().addItem(self.path) self.scene().selectionChanged.connect(self.selectionChanged) self.selected=None self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect( self.custommenu ) def custommenu(self): menu=QMenu(self) action = QAction('SavePicture', self) action.triggered.connect(self.savepicture) menu.addAction(action) menu.exec_(QCursor.pos ()) def savepicture(self): w,h=self.pixmap.width(),self.pixmap.height() vw,vh = self.viewport().rect().width(),self.viewport().rect().height() vsx,vsy=self.mapToScene( QPoint(0,0) ).x(),self.mapToScene( QPoint(0,0) ).y() if w > vw : x= -1*self.horizontalScrollBar().value() else: x= -vsx if h > vh : y= -1*self.verticalScrollBar().value() else: y=-vsy qi=QImage(w,h ,QImage.Format_RGB32) pa=QPainter() pa.begin(qi) self.render(pa , source=QRect(x , y , w,h ) ) qi.save(r'') pa.end() def selectionChanged(self): if self.selected : self.selected.ungrabKeyboard() p=self.scene().selectedItems() if len(p) >= 1 : p=p[0] p.grabKeyboard() self.selected=p def keyPressEvent(self, event): # jikken if event.key() == Qt.Key_A : print( self.mapToScene( QPoint(0,0) ).x() ) app = QApplication(sys.argv) window = SvgView() window.show() sys.exit(app.exec_()) ``` - ひとまずこのテーマはこれで終了 - # ToDo - 前に作ったパスの読み込みクラスの実装、背景画像のDD処理 文字入れ、パスの属性変更 - ここまでやらないと実用化は難しそうだ ( ̄― ̄) --------- ---> [PyQt で画像にパスを加えるツールを作る](https://mimemo.io/m/JkWVal6ZmJlBEqd)