2017-01-03 2 views
0

다음은 코드입니다. 그것은 작동한다. 이 버그를 전시하십시오. 타원을 마우스 오른쪽 버튼으로 클릭하고 타원을 클릭하여 드래그하여 크기를 조절합니다. 마우스 오른쪽 버튼으로 클릭하고 '완료'를 클릭하십시오. 그런 다음 '회전'을 사용하여 동일한 작업을 수행하십시오.QPainter (또는 다른 방법을 사용하여) 사용자 지정 QGraphicsItem을 회전 할 수없는 것 같습니다.

나는 등 self.setRotation, self.setTransform, painter.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()를 사용하여 scale & rotate에 대한 두 개의 독립적 인 변환을 설정하려고했습니다. 척도는 평소와 같이 계속 작동했습니다. –

답변

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_()) 
관련 문제