Cambiar el tamaño de un QGraphicsItem con el mouse

Después de colocar un rectángulo en una QGraphicsScene y hacer que se mueva con el mouse, ¿cómo podría cambiar su tamaño con el mouse?

Si nadie sabe una respuesta, un ejemplo también podría ser bueno, estoy desarrollando Python utilizando PyQt4.

Sé que hay preguntas similares aquí, pero no me responden respectivamente.

Mi código simplificado:

import sys from PyQt4.QtCore import * from PyQt4.QtGui import * app = QApplication(sys.argv) grview = QGraphicsView() scene = QGraphicsScene() scene.setSceneRect(0, 0, 512, 512) scene.addPixmap(QPixmap('01.png')) grview.setScene(scene) item = QGraphicsRectItem(0, 0, 300, 150) pen = QPen(Qt.darkMagenta) pen.setWidth(4) item.setPen(pen) item.setFlag(QGraphicsItem.ItemIsMovable) scene.addItem(item) grview.fitInView(scene.sceneRect(), Qt.KeepAspectRatio) grview.show() sys.exit(app.exec_()) 

Usted tendría que implementar el cambio de tamaño a ti mismo. Para hacer eso, necesita subclase QGraphicsRectItem e implementar controladores de eventos de mouse: mousePressEvent , mouseMoveEvent , mouseReleaseEvent . La idea general es la siguiente:

  • Crea un conjunto de controladores que puede utilizar para cambiar el tamaño de la forma.
  • Al rastrear el cambio de posición de las manijas calcula la nueva geometría de la forma.

Escribí algunos códigos hace un tiempo que funcionaba perfectamente con PyQt5, pero también debería funcionar con PyQt4. Debes poder adaptarlo a tus necesidades:

 import sys from PyQt5.QtCore import Qt, QRectF, QPointF from PyQt5.QtGui import QBrush, QPainterPath, QPainter, QColor, QPen, QPixmap from PyQt5.QtWidgets import QGraphicsRectItem, QApplication, QGraphicsView, QGraphicsScene, QGraphicsItem class GraphicsRectItem(QGraphicsRectItem): handleTopLeft = 1 handleTopMiddle = 2 handleTopRight = 3 handleMiddleLeft = 4 handleMiddleRight = 5 handleBottomLeft = 6 handleBottomMiddle = 7 handleBottomRight = 8 handleSize = +8.0 handleSpace = -4.0 handleCursors = { handleTopLeft: Qt.SizeFDiagCursor, handleTopMiddle: Qt.SizeVerCursor, handleTopRight: Qt.SizeBDiagCursor, handleMiddleLeft: Qt.SizeHorCursor, handleMiddleRight: Qt.SizeHorCursor, handleBottomLeft: Qt.SizeBDiagCursor, handleBottomMiddle: Qt.SizeVerCursor, handleBottomRight: Qt.SizeFDiagCursor, } def __init__(self, *args): """ Initialize the shape. """ super().__init__(*args) self.handles = {} self.handleSelected = None self.mousePressPos = None self.mousePressRect = None self.setAcceptHoverEvents(True) self.setFlag(QGraphicsItem.ItemIsMovable, True) self.setFlag(QGraphicsItem.ItemIsSelectable, True) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True) self.setFlag(QGraphicsItem.ItemIsFocusable, True) self.updateHandlesPos() def handleAt(self, point): """ Returns the resize handle below the given point. """ for k, v, in self.handles.items(): if v.contains(point): return k return None def hoverMoveEvent(self, moveEvent): """ Executed when the mouse moves over the shape (NOT PRESSED). """ if self.isSelected(): handle = self.handleAt(moveEvent.pos()) cursor = Qt.ArrowCursor if handle is None else self.handleCursors[handle] self.setCursor(cursor) super().hoverMoveEvent(moveEvent) def hoverLeaveEvent(self, moveEvent): """ Executed when the mouse leaves the shape (NOT PRESSED). """ self.setCursor(Qt.ArrowCursor) super().hoverLeaveEvent(moveEvent) def mousePressEvent(self, mouseEvent): """ Executed when the mouse is pressed on the item. """ self.handleSelected = self.handleAt(mouseEvent.pos()) if self.handleSelected: self.mousePressPos = mouseEvent.pos() self.mousePressRect = self.boundingRect() super().mousePressEvent(mouseEvent) def mouseMoveEvent(self, mouseEvent): """ Executed when the mouse is being moved over the item while being pressed. """ if self.handleSelected is not None: self.interactiveResize(mouseEvent.pos()) else: super().mouseMoveEvent(mouseEvent) def mouseReleaseEvent(self, mouseEvent): """ Executed when the mouse is released from the item. """ super().mouseReleaseEvent(mouseEvent) self.handleSelected = None self.mousePressPos = None self.mousePressRect = None self.update() def boundingRect(self): """ Returns the bounding rect of the shape (including the resize handles). """ o = self.handleSize + self.handleSpace return self.rect().adjusted(-o, -o, o, o) def updateHandlesPos(self): """ Update current resize handles according to the shape size and position. """ s = self.handleSize b = self.boundingRect() self.handles[self.handleTopLeft] = QRectF(b.left(), b.top(), s, s) self.handles[self.handleTopMiddle] = QRectF(b.center().x() - s / 2, b.top(), s, s) self.handles[self.handleTopRight] = QRectF(b.right() - s, b.top(), s, s) self.handles[self.handleMiddleLeft] = QRectF(b.left(), b.center().y() - s / 2, s, s) self.handles[self.handleMiddleRight] = QRectF(b.right() - s, b.center().y() - s / 2, s, s) self.handles[self.handleBottomLeft] = QRectF(b.left(), b.bottom() - s, s, s) self.handles[self.handleBottomMiddle] = QRectF(b.center().x() - s / 2, b.bottom() - s, s, s) self.handles[self.handleBottomRight] = QRectF(b.right() - s, b.bottom() - s, s, s) def interactiveResize(self, mousePos): """ Perform shape interactive resize. """ offset = self.handleSize + self.handleSpace boundingRect = self.boundingRect() rect = self.rect() diff = QPointF(0, 0) self.prepareGeometryChange() if self.handleSelected == self.handleTopLeft: fromX = self.mousePressRect.left() fromY = self.mousePressRect.top() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() diff.setX(toX - fromX) diff.setY(toY - fromY) boundingRect.setLeft(toX) boundingRect.setTop(toY) rect.setLeft(boundingRect.left() + offset) rect.setTop(boundingRect.top() + offset) self.setRect(rect) elif self.handleSelected == self.handleTopMiddle: fromY = self.mousePressRect.top() toY = fromY + mousePos.y() - self.mousePressPos.y() diff.setY(toY - fromY) boundingRect.setTop(toY) rect.setTop(boundingRect.top() + offset) self.setRect(rect) elif self.handleSelected == self.handleTopRight: fromX = self.mousePressRect.right() fromY = self.mousePressRect.top() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() diff.setX(toX - fromX) diff.setY(toY - fromY) boundingRect.setRight(toX) boundingRect.setTop(toY) rect.setRight(boundingRect.right() - offset) rect.setTop(boundingRect.top() + offset) self.setRect(rect) elif self.handleSelected == self.handleMiddleLeft: fromX = self.mousePressRect.left() toX = fromX + mousePos.x() - self.mousePressPos.x() diff.setX(toX - fromX) boundingRect.setLeft(toX) rect.setLeft(boundingRect.left() + offset) self.setRect(rect) elif self.handleSelected == self.handleMiddleRight: print("MR") fromX = self.mousePressRect.right() toX = fromX + mousePos.x() - self.mousePressPos.x() diff.setX(toX - fromX) boundingRect.setRight(toX) rect.setRight(boundingRect.right() - offset) self.setRect(rect) elif self.handleSelected == self.handleBottomLeft: fromX = self.mousePressRect.left() fromY = self.mousePressRect.bottom() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() diff.setX(toX - fromX) diff.setY(toY - fromY) boundingRect.setLeft(toX) boundingRect.setBottom(toY) rect.setLeft(boundingRect.left() + offset) rect.setBottom(boundingRect.bottom() - offset) self.setRect(rect) elif self.handleSelected == self.handleBottomMiddle: fromY = self.mousePressRect.bottom() toY = fromY + mousePos.y() - self.mousePressPos.y() diff.setY(toY - fromY) boundingRect.setBottom(toY) rect.setBottom(boundingRect.bottom() - offset) self.setRect(rect) elif self.handleSelected == self.handleBottomRight: fromX = self.mousePressRect.right() fromY = self.mousePressRect.bottom() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() diff.setX(toX - fromX) diff.setY(toY - fromY) boundingRect.setRight(toX) boundingRect.setBottom(toY) rect.setRight(boundingRect.right() - offset) rect.setBottom(boundingRect.bottom() - offset) self.setRect(rect) self.updateHandlesPos() def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. """ path = QPainterPath() path.addRect(self.rect()) if self.isSelected(): for shape in self.handles.values(): path.addEllipse(shape) return path def paint(self, painter, option, widget=None): """ Paint the node in the graphic view. """ painter.setBrush(QBrush(QColor(255, 0, 0, 100))) painter.setPen(QPen(QColor(0, 0, 0), 1.0, Qt.SolidLine)) painter.drawRect(self.rect()) painter.setRenderHint(QPainter.Antialiasing) painter.setBrush(QBrush(QColor(255, 0, 0, 255))) painter.setPen(QPen(QColor(0, 0, 0, 255), 1.0, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) for handle, rect in self.handles.items(): if self.handleSelected is None or handle == self.handleSelected: painter.drawEllipse(rect) def main(): app = QApplication(sys.argv) grview = QGraphicsView() scene = QGraphicsScene() scene.setSceneRect(0, 0, 680, 459) scene.addPixmap(QPixmap('01.png')) grview.setScene(scene) item = GraphicsRectItem(0, 0, 300, 150) scene.addItem(item) grview.fitInView(scene.sceneRect(), Qt.KeepAspectRatio) grview.show() sys.exit(app.exec_()) if __name__ == '__main__': main() 

También estaba tratando de hacer una tarea similar, excepto en mi caso, necesitaba un objeto reutilizable que pudiera heredar el elemento principal que necesitaba ser redimensionado. Este es un ejemplo del uso de esta clase de Resizer para agregar un anclaje de tamaño a su QGraphicsRectItem principal.

La clase Resizer hereda QGraphicsObject y reimplementa el método de pintura. El método itemChange de Resizer emite una señal que se asigna al método de cambio de resize en la clase de Box principal. Esto, a su vez, utiliza la información de cambio de posición del ajuste del tamaño para ajustar el rectángulo de la caja.

De esta manera, el cambio de tamaño también se puede volver a utilizar para otros elementos, sin tener que volver a implementar los eventos de presionar / mover / liberar el mouse.

 import sys from PyQt4.QtGui import * from PyQt4.QtCore import * class Box(QGraphicsRectItem): def __init__(self, position, rect=QRectF(0, 0, 100, 50), parent=None, scene=None): super().__init__(rect, parent, scene) self.setFlag(QGraphicsItem.ItemIsSelectable, True) self.setFlag(QGraphicsItem.ItemIsMovable, True) self.setFlag(QGraphicsItem.ItemIsFocusable, True) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True) self.setPos(position) self.resizer = Resizer(parent=self) resizerWidth = self.resizer.rect.width() / 2 resizerOffset = QPointF(resizerWidth, resizerWidth) self.resizer.setPos(self.rect().bottomRight() - resizerOffset) self.resizer.resizeSignal.connect(self.resize) def paint(self, painter, option, widget=None): pen = QPen() pen.setColor(Qt.black) painter.setPen(pen) painter.setBrush(Qt.transparent) painter.drawRect(self.rect()) @pyqtSlot() def resize(self, change): self.setRect(self.rect().adjusted(0, 0, change.x(), change.y())) self.prepareGeometryChange() self.update() class Resizer(QGraphicsObject): resizeSignal = pyqtSignal(QPointF) def __init__(self, rect=QRectF(0, 0, 10, 10), parent=None): super().__init__(parent) self.setFlag(QGraphicsItem.ItemIsMovable, True) self.setFlag(QGraphicsItem.ItemIsSelectable, True) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True) self.rect = rect def boundingRect(self): return self.rect def paint(self, painter, option, widget=None): if self.isSelected(): pen = QPen() pen.setStyle(Qt.DotLine) painter.setPen(pen) painter.drawEllipse(self.rect) def itemChange(self, change, value): if change == QGraphicsItem.ItemPositionChange: if self.isSelected(): self.resizeSignal.emit(value - self.pos()) return value if __name__ == "__main__": app = QApplication(sys.argv) view = QGraphicsView() scene = QGraphicsScene() scene.setSceneRect(0, 0, 500, 1000) view.setScene(scene) box = Box(QPointF(50, 50), scene=scene) view.show() sys.exit(app.exec_()) 

Aquí hay un ejemplo de cambio de tamaño de un artículo de QGraphics , está en C ++. Si usted o alguien aquí lo reescribe en python, podría ser útil.

También estaba intentando esto con la clase QGraphicsPixmapItem, modificando la clase de cambio de tamaño de @pbreach, esto es lo que funcionó para mí:

 class Resizer(QGraphicsObject): resizeSignal = pyqtSignal(QGraphicsItem.GraphicsItemChange,QPointF) def __init__(self,rect = QRectF(0,0,10,10),parent=None): super().__init__(parent) self.setFlag(QGraphicsItem.ItemIsMovable,True) self.setFlag(QGraphicsItem.ItemIsSelectable,True) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges,True) self.setCursor(Qt.SizeFDiagCursor) self.rect = rect self.hide() def boundingRect(self): return self.rect def paint(self,painter,option,widget=None): if self.isSelected(): pen = QPen() pen.setStyle(Qt.DotLine) painter.setPen(pen) painter.setRenderHint(QPainter.Antialiasing) painter.drawEllipse(self.rect) self.update() def itemChange(self,change,value): self.prepareGeometryChange() if change == QGraphicsItem.ItemPositionChange: if self.isSelected(): self.resizeSignal.emit(change,self.pos()) return super(Resizer,self).itemChange(change,value) '''END CLASS''' class GraphicLayer(QGraphicsPixmapItem): def __init__(self,top_left_x,top_left_y,graphic,rect=QRectF(0,0,100,100),parent=None,scene=None): super().__init__(parent=parent) self.rect = rect self.setPixmap(graphic) self.graphic = graphic self.mousePressPos = None self.mousePressRect = None self.setAcceptHoverEvents(True) self.setFlag(QGraphicsItem.ItemIsMovable,True) self.setFlag(QGraphicsItem.ItemIsSelectable,True) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges,True) self.setFlag(QGraphicsItem.ItemIsFocusable,True) self.setPos(top_left_x,top_left_y) # Resizer actions self.resizer = Resizer(parent=self) r_width = self.resizer.boundingRect().width() - 2 self.r_offset = QPointF(r_width,r_width) self.resizer.setPos(self.boundingRect().bottomRight()-self.r_offset) self.resizer.resizeSignal.connect(self.resize) def set_tag(self,item_id): self.tag = item_id def get_tag(self): return self.tag def hoverMoveEvent(self,event): if self.isSelected(): self.resizer.show() else: self.resizer.hide() def hoverLeave(self,event): self.resizer.hide() @pyqtSlot() def resize(self,change,value): pixmap = self.graphic.scaled(value.x(),value.y(),transformMode=Qt.SmoothTransformation) self.setPixmap(pixmap) self.prepareGeometryChange() self.update() """END OF CLASS""" 

Aquí hay un pequeño gif de esta clase trabajando. ¡Importante! Esto se hizo en PyQt5. No hay suficiente reputación para publicar imágenes.

¡Por favor, hágame saber sus pensamientos!