2017-01-03 3 views
0

ここにコードがあります。それは動く。このバグを表示する。楕円を右クリックし、楕円をクリックしてドラッグします。それを右クリックして[完了]をクリックします。それから、同じことを「回転」する。QPainter(またはその他の方法)を使用してカスタムQGraphicsItemを回転することはできないようです。

私はself.setRotationself.setTransformpainter.rotateを使用しての10以上の異なる順列を試してみた、というように...私はself.setTransform(self.transform().rotate(r))をしたときに回転をした唯一の時間だったが、結果が間違っていた:規模&は間違った順序で回転させますか何か。

from PyQt5.QtWidgets import QGraphicsItem, QMenu 
from PyQt5.QtGui import QTransform, QPen, QPainter, QColor, QBrush 
from PyQt5.QtCore import Qt, QPointF, QRectF, QEvent 
from math import sqrt 


def scaleRect(rect, scale_x, scale_y): 
    T = QTransform.fromScale(scale_x, scale_y) 
    return T.mapRect(rect) 

def debugPrintTransformMatrix(T): 
    print(str(T.m11()) + ' ' + str(T.m12()) + ' ' + str(T.m13())) 
    print(str(T.m21()) + ' ' + str(T.m22()) + ' ' + str(T.m23())) 
    print(str(T.m31()) + ' ' + str(T.m32()) + ' ' + str(T.m33())) 

# Assumes no shearing or stretching. 
# Only Rotation, Translation, and Scaling. 
def extractTransformScale(T): 
    # This is math matrix notation transposed (debug print self.sceneTransform() to see) 
    Sx = sqrt(T.m11()**2 + T.m12()**2)  
    Sy = sqrt(T.m21()**2 + T.m22()**2) 
    return Sx, Sy  

def extractTransformTranslate(T): 
    return T.m31(), T.m32() 


class Object(QGraphicsItem): 
    def sceneEvent(self, event): 
     if event.type() == QEvent.GraphicsSceneMouseMove: 
      # move, scale, or rotate 
      if self._mode in ['scale', 'rotate']: 
       mouse_pos = event.scenePos() 
       last_pos = event.lastScenePos() 
       if self._mode == 'scale': 
        s = self.mouseScalingFactors(mouse_pos, last_pos) 
        self.setTransform(self.transform().scale(*s)) 
       if self._mode == 'rotate': 
        r = self.mouseRotationAngle(mouse_pos, last_pos) 
        self.setRotation(self.rotation() + r) 
       return True 

     return super().sceneEvent(event) 

    def __init__(self): 
     super().__init__() 
     self.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsFocusable | QGraphicsItem.ItemIsSelectable) 
     self._selectionPen = QPen(Qt.black, 1.0, style=Qt.DotLine, cap=Qt.FlatCap) 
     self._lastPos = QPointF(0, 0) 
     self.setPos(self._lastPos) 
     self._mode = 'neutral' 

    def setRenderHints(self, painter): 
     painter.setRenderHints(QPainter.SmoothPixmapTransform | QPainter.HighQualityAntialiasing | QPainter.TextAntialiasing) 

    def boundingRectExtraScale(self): 
     return (1.2 , 1.2) 

    def mouseScalingFactors(self, pos, last_pos): 
     delta = pos - last_pos 
     return (1.01 ** delta.x(), 1.01 ** delta.y()) 

    def mouseRotationAngle(self, pos, last_pos): 
     return 1 #TODO 

    def createDefaultContextMenu(self): 
     menu = QMenu() 
     if self._mode == 'neutral': 
      menu.addAction('Scale').triggered.connect(lambda: self.setMode('scale')) 
      menu.addAction('Rotate').triggered.connect(lambda: self.setMode('rotate')) 
     else: 
      menu.addAction('Done Editing').triggered.connect(lambda: self.setMode('neutral')) 
     return menu 

    def contextMenuEvent(self, event): 
      menu = self.createDefaultContextMenu() 
      menu.exec(event.screenPos()) 

    def setMode(self, mode): 
     self._mode = mode 

    def setSelected(self, selected): 
     super().setSelected(selected) 
     self.update() 


class ShapedObject(Object): 
    def __init__(self): 
     super().__init__() 
     self._shape = { 
      'name' : 'ellipse', 
      'radius': 35 
     } 
     self._brush = QBrush(Qt.darkGreen) 
     self._pen = QPen(Qt.yellow, 3)   

    def shapeDef(self): 
     return self._shape 

    def boundingRect(self): 
     rect = self.shapeRect() 
     s = self.boundingRectExtraScale() 
     return scaleRect(rect, *s) 

    def shape(self):  #TODO QPainterPath shape for collision detection 
     # Should call self.boundingRectExtraScale() 
     return super().shape() 

    def paint(self, painter, option, widget): 
     self.setRenderHints(painter) 
     #super().paint(painter, option, widget) 

     shape = self.shapeDef() 
     name = shape['name'] 

     painter.setBrush(self._brush) 
     painter.setPen(self._pen) 

     painter.save() 

     # ********** HERE IS THE PROBLEM ************* 

     debugPrintTransformMatrix(painter.transform()) 
     painter.rotate(5) 
     debugPrintTransformMatrix(painter.transform()) 

     rect = self.shapeRect() 
     if name == 'ellipse': 
      painter.drawEllipse(rect) 


     painter.restore() 


    def shapeRect(self): 
     shape = self.shapeDef() 
     name = shape['name'] 
     if name == 'ellipse': 
      r = shape['radius'] 
      rect = QRectF(-r, -r, 2*r, 2*r) 
     return rect 


#### 

import sys 
from PyQt5.QtWidgets import QMainWindow, QGraphicsScene, QGraphicsView, QApplication 

if __name__ == '__main__': 
    app = QApplication(sys.argv) 

    window = QMainWindow() 
    view = QGraphicsView() 
    scene = QGraphicsScene() 
    view.setScene(scene) 
    window.setCentralWidget(view) 

    ellipse = ShapedObject() 
    scene.addItem(ellipse) 

    window.show() 

    sys.exit(app.exec_()) 
+0

ドット付き選択QPainterPathを回転させることができたので、楕円をパスに変換しようとします。バグでなければならない!私はsetTransformations()を使ってスケール&回転の2つの独立した変換を設定しようと試みました。スケールは今まで通り働いた。 –

答えて

0

それは操作の回転/スケールの順序でした。回転コードをテストするためにサークルを使用しないでください。

from PyQt5.QtWidgets import QGraphicsItem, QMenu, QGraphicsRotation, QGraphicsScale 
from PyQt5.QtGui import QTransform, QPen, QPainter, QColor, QBrush, QPainterPath 
from PyQt5.QtCore import Qt, QPointF, QRectF, QEvent 
from math import sqrt 


def scaleRect(rect, scale_x, scale_y): 
    T = QTransform.fromScale(scale_x, scale_y) 
    return T.mapRect(rect) 

def debugPrintTransformMatrix(T): 
    print(str(T.m11()) + ' ' + str(T.m12()) + ' ' + str(T.m13())) 
    print(str(T.m21()) + ' ' + str(T.m22()) + ' ' + str(T.m23())) 
    print(str(T.m31()) + ' ' + str(T.m32()) + ' ' + str(T.m33())) 

# Assumes no shearing or stretching. 
# Only Rotation, Translation, and Scaling. 
def extractTransformScale(T): 
    # This is math matrix notation transposed (debug print self.sceneTransform() to see) 
    Sx = sqrt(T.m11()**2 + T.m12()**2)  
    Sy = sqrt(T.m21()**2 + T.m22()**2) 
    return Sx, Sy  

def extractTransformTranslate(T): 
    return T.m31(), T.m32() 


class Object(QGraphicsItem): 
    def sceneEvent(self, event): 
     if event.type() == QEvent.GraphicsSceneMouseMove: 
      # move, scale, or rotate 
      if self._mode in ['scale', 'rotate']: 
       mouse_pos = event.scenePos() 
       last_pos = event.lastScenePos() 
       if self._mode == 'scale': 
        s = self.mouseScalingFactors(mouse_pos, last_pos) 
        self.applyScaleTransform(*s) 
       if self._mode == 'rotate': 
        r = self.mouseRotationAngle(mouse_pos, last_pos) 
        self.applyRotateTransform(r) 
       return True 

     return super().sceneEvent(event) 

    def __init__(self): 
     super().__init__() 
     self.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsFocusable | QGraphicsItem.ItemIsSelectable) 
     self._selectionPen = QPen(Qt.black, 1.0, style=Qt.DotLine, cap=Qt.FlatCap) 
     self._lastPos = QPointF(0, 0) 
     self.setPos(self._lastPos) 
     self._mode = 'neutral' 
     self._scale = QGraphicsScale() 
     self._rotate = QGraphicsRotation() 
     self.setTransformations([self._rotate, self._scale]) 

    def applyRotateTransform(self, angle): 
     self._rotate.setAngle(self._rotate.angle() + 15) 

    def applyScaleTransform(self, sx, sy): 
     self._scale.setXScale(sx * self._scale.xScale()) 
     self._scale.setYScale(sy * self._scale.yScale()) 

    def setRenderHints(self, painter): 
     painter.setRenderHints(QPainter.SmoothPixmapTransform | QPainter.HighQualityAntialiasing | QPainter.TextAntialiasing) 

    def boundingRectExtraScale(self): 
     return (1.2 , 1.2) 

    def mouseScalingFactors(self, pos, last_pos): 
     delta = pos - last_pos 
     return (1.01 ** delta.x(), 1.01 ** delta.y()) 

    def mouseRotationAngle(self, pos, last_pos): 
     return 1 #TODO 

    def createDefaultContextMenu(self): 
     menu = QMenu() 
     if self._mode == 'neutral': 
      menu.addAction('Scale').triggered.connect(lambda: self.setMode('scale')) 
      menu.addAction('Rotate').triggered.connect(lambda: self.setMode('rotate')) 
     else: 
      menu.addAction('Done Editing').triggered.connect(lambda: self.setMode('neutral')) 
     return menu 

    def contextMenuEvent(self, event): 
      menu = self.createDefaultContextMenu() 
      menu.exec(event.screenPos()) 

    def setMode(self, mode): 
     self._mode = mode 

    def setSelected(self, selected): 
     super().setSelected(selected) 
     self.update() 


class ShapedObject(Object): 
    def __init__(self): 
     super().__init__() 
     self._shape = { 
      'name' : 'ellipse', 
      'radius': 35 
     } 
     self._brush = QBrush(Qt.darkGreen) 
     self._pen = QPen(Qt.yellow, 3)   

    def shapeDef(self): 
     return self._shape 

    def boundingRect(self): 
     rect = self.shapeRect() 
     s = self.boundingRectExtraScale() 
     return scaleRect(rect, *s) 

    def shape(self):  #TODO QPainterPath shape for collision detection 
     # Should call self.boundingRectExtraScale() 
     return super().shape() 

    def paint(self, painter, option, widget): 
     self.setRenderHints(painter) 
     #super().paint(painter, option, widget) 

     shape = self.shapeDef() 
     name = shape['name'] 

     painter.setBrush(self._brush) 
     painter.setPen(self._pen) 

     painter.save() 
     path = QPainterPath() 

     if name == 'ellipse': 
      r = shape['radius'] 
      path.addEllipse(QPointF(0, 0), r, r) 

     painter.drawPath(path) 
     painter.restore() 

    def shapeRect(self): 
     shape = self.shapeDef() 
     name = shape['name'] 
     if name == 'ellipse': 
      r = shape['radius'] 
      rect = QRectF(-r, -r, 2*r, 2*r) 
     return rect 


#### 

import sys 
from PyQt5.QtWidgets import QMainWindow, QGraphicsScene, QGraphicsView, QApplication 

if __name__ == '__main__': 
    app = QApplication(sys.argv) 

    window = QMainWindow() 
    view = QGraphicsView() 
    scene = QGraphicsScene() 
    view.setScene(scene) 
    window.setCentralWidget(view) 

    ellipse = ShapedObject() 
    scene.addItem(ellipse) 

    window.show() 

    sys.exit(app.exec_()) 
関連する問題