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処理
    文字入れ、パスの属性変更
  • ここまでやらないと実用化は難しそうだ ( ̄― ̄)

---> PyQt で画像にパスを加えるツールを作る

0

メモを他の人に見せる

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

コメント(0)

  • someone

  • someone