0 PyQt で画像にパスを加えるツールを作る 4
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'<fullpath>')
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'<fullpath>')
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処理
文字入れ、パスの属性変更 - ここまでやらないと実用化は難しそうだ ( ̄― ̄)
コメント(0)