Изменить размер центра QGraphicsItem пропорционально скорости мыши? - PullRequest
0 голосов
/ 18 февраля 2020

Я прошу прощения за повторяющиеся вопросы относительно pyqt, но ограниченные ресурсы заставляют меня делать это.

Я пытался реализовать функцию изменения размера для моей формы круга, которая является расширением QGraphicsItem, который имеет эллипс и центрированный текст. Я могу изменить свою форму по желанию, но форма требует времени, чтобы догнать мышь, т. Е. При переключении направлений круг продолжает увеличиваться, но требуется некоторое время для переключения направлений, более того, круг изменяется с помощью якоря вверху левый угол ограничивающего прямоугольника.

См. в этом вопросе для части кода фона

def updateHandlesPos(self):
        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):
        self.prepareGeometryChange()
        if self.handleSelected in [self.handleTopLeft,
                                   self.handleTopRight,
                                   self.handleBottomLeft,
                                   self.handleBottomRight,
                                   self.handleTopMiddle,
                                   self.handleBottomMiddle,
                                   self.handleMiddleLeft,
                                   self.handleMiddleRight]:
            self.radius += (mousePos.y() + mousePos.x() + self.mousePressPos.x() - self.mousePressPos.y())/64
            self.setPos(self.x(),self.y())
        self.update()   
        self.updateHandlesPos()

1 Ответ

2 голосов
/ 19 февраля 2020

Так как ОП не предоставил MRE, я создал пример с нуля. Логика c предназначена для отслеживания изменений предметов и в соответствии с этим вычисляет новую геометрию и устанавливает sh новую позицию других предметов.

from PyQt5 import QtWidgets, QtGui, QtCore


class GripItem(QtWidgets.QGraphicsPathItem):
    circle = QtGui.QPainterPath()
    circle.addEllipse(QtCore.QRectF(-5, -5, 10, 10))
    square = QtGui.QPainterPath()
    square.addRect(QtCore.QRectF(-10, -10, 20, 20))

    def __init__(self, annotation_item, index):
        super(GripItem, self).__init__()
        self.m_annotation_item = annotation_item
        self.m_index = index

        self.setPath(GripItem.circle)
        self.setBrush(QtGui.QColor("green"))
        self.setPen(QtGui.QPen(QtGui.QColor("green"), 2))
        self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
        self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
        self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, True)
        self.setAcceptHoverEvents(True)
        self.setZValue(11)
        self.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))

    def hoverEnterEvent(self, event):
        self.setPath(GripItem.square)
        self.setBrush(QtGui.QColor("red"))
        super(GripItem, self).hoverEnterEvent(event)

    def hoverLeaveEvent(self, event):
        self.setPath(GripItem.circle)
        self.setBrush(QtGui.QColor("green"))
        super(GripItem, self).hoverLeaveEvent(event)

    def mouseReleaseEvent(self, event):
        self.setSelected(False)
        super(GripItem, self).mouseReleaseEvent(event)

    def itemChange(self, change, value):
        if change == QtWidgets.QGraphicsItem.ItemPositionChange and self.isEnabled():
            self.m_annotation_item.movePoint(self.m_index, value)
        return super(GripItem, self).itemChange(change, value)


class DirectionGripItem(GripItem):
    def __init__(self, annotation_item, direction=QtCore.Qt.Horizontal, parent=None):
        super(DirectionGripItem, self).__init__(annotation_item, parent)
        self._direction = direction

    @property
    def direction(self):
        return self._direction

    def itemChange(self, change, value):
        if change == QtWidgets.QGraphicsItem.ItemPositionChange and self.isEnabled():
            p = QtCore.QPointF(self.pos())
            if self.direction == QtCore.Qt.Horizontal:
                p.setX(value.x())
            elif self.direction == QtCore.Qt.Vertical:
                p.setY(value.y())
            self.m_annotation_item.movePoint(self.m_index, p)
            return p
        return super(DirectionGripItem, self).itemChange(change, value)


class CircleAnnotation(QtWidgets.QGraphicsEllipseItem):
    def __init__(self, radius=1, parent=None):
        super(CircleAnnotation, self).__init__(parent)
        self.setZValue(11)
        self.m_items = []

        self.setPen(QtGui.QPen(QtGui.QColor("green"), 4))

        self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
        self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
        self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, True)

        self.setAcceptHoverEvents(True)

        self.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))

        self._radius = radius
        self.update_rect()

    @property
    def radius(self):
        return self._radius

    @radius.setter
    def radius(self, r):
        if r <= 0:
            raise ValueError("radius must be positive")
        self._radius = r
        self.update_rect()
        self.add_grip_items()
        self.update_items_positions()

    def update_rect(self):
        rect = QtCore.QRectF(0, 0, 2 * self.radius, 2 * self.radius)
        rect.moveCenter(self.rect().center())
        self.setRect(rect)

    def add_grip_items(self):
        if self.scene() and not self.m_items:
            for i, (direction) in enumerate(
                (
                    QtCore.Qt.Vertical,
                    QtCore.Qt.Horizontal,
                    QtCore.Qt.Vertical,
                    QtCore.Qt.Horizontal,
                )
            ):
                item = DirectionGripItem(self, direction, i)
                self.scene().addItem(item)
                self.m_items.append(item)

    def movePoint(self, i, p):
        if 0 <= i < min(4, len(self.m_items)):
            item_selected = self.m_items[i]
            lp = self.mapFromScene(p)
            self._radius = (lp - self.rect().center()).manhattanLength()
            k = self.indexOf(lp)
            if k is not None:
                self.m_items = [item for item in self.m_items if not item.isSelected()]
                self.m_items.insert(k, item_selected)
                self.update_items_positions([k])
                self.update_rect()

    def update_items_positions(self, index_no_updates=None):
        index_no_updates = index_no_updates or []
        for i, (item, direction) in enumerate(
            zip(
                self.m_items,
                (
                    QtCore.Qt.Vertical,
                    QtCore.Qt.Horizontal,
                    QtCore.Qt.Vertical,
                    QtCore.Qt.Horizontal,
                ),
            ),
        ):
            item.m_index = i
            if i not in index_no_updates:
                pos = self.mapToScene(self.point(i))
                item = self.m_items[i]
                item._direction = direction
                item.setEnabled(False)
                item.setPos(pos)
                item.setEnabled(True)

    def indexOf(self, p):
        for i in range(4):
            if p == self.point(i):
                return i

    def point(self, index):
        if 0 <= index < 4:
            return [
                QtCore.QPointF(0, -self.radius),
                QtCore.QPointF(self.radius, 0),
                QtCore.QPointF(0, self.radius),
                QtCore.QPointF(-self.radius, 0),
            ][index]

    def itemChange(self, change, value):
        if change == QtWidgets.QGraphicsItem.ItemPositionHasChanged:
            self.update_items_positions()
            return
        if change == QtWidgets.QGraphicsItem.ItemSceneHasChanged:
            self.add_grip_items()
            self.update_items_positions()
            return
        return super(CircleAnnotation, self).itemChange(change, value)

    def hoverEnterEvent(self, event):
        self.setBrush(QtGui.QColor(255, 0, 0, 100))
        super(CircleAnnotation, self).hoverEnterEvent(event)

    def hoverLeaveEvent(self, event):
        self.setBrush(QtGui.QBrush(QtCore.Qt.NoBrush))
        super(CircleAnnotation, self).hoverLeaveEvent(event)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)

    scene = QtWidgets.QGraphicsScene()
    view = QtWidgets.QGraphicsView(scene)
    view.setRenderHints(QtGui.QPainter.Antialiasing)
    item = CircleAnnotation()
    item.radius = 100
    scene.addItem(item)
    view.showMaximized()

    sys.exit(app.exec_())
...