Как обрабатывать кнопки прокси внутри QGraphicsView

Я собираюсь обрабатывать некоторые кнопки внутри QGraphicsView, но каждый раз, когда я применяю какое-то преобразование, кнопки прокси меняют положение, и все становится беспорядком. Я не знаю, правильно ли я использую линейные преобразования. Когда я перетаскиваю экран, мои "кнопки" покидают желаемое положение. Смотрите на картинке:

Начальное состояние:

введите описание изображения здесь

После обновления sceneRect:

введите описание изображения здесь

Вот мой код:

from PyQt5 import QtGui
from PyQt5 import QtCore
from PyQt5 import QtWidgets
import os
from PyQt5 import QtCore, QtGui, QtWidgets


class ArrowItem(QtWidgets.QGraphicsPathItem):
    """
    Arrow item.
    """

    def __init__(self, parent=None):
        super(ArrowItem, self).__init__(parent)
        self._animation = QtCore.QVariantAnimation()
        self._length = -1
        self._points = (
            QtCore.QPointF(),
            QtCore.QPointF(),
            QtCore.QPointF(),
        )
        self.length = 40.0

    @property
    def length(self):
        return self._length

    @length.setter
    def length(self, l):
        self._length = l

        pos_top = QtCore.QPointF(0, l * 4 / 5)
        pos_left = QtCore.QPointF(-l * 3 / 5, -l / 5)
        pos_right = QtCore.QPointF(
            l * 3 / 5,
            -l / 5,
        )

        path = QtGui.QPainterPath()
        path.moveTo(pos_top)
        path.lineTo(pos_right)
        path.lineTo(pos_left)

        self.setPath(path)

        self._points = pos_top, pos_left, pos_right

    def paint(self, painter, option, widget):
        pos_top, pos_left, pos_right = self._points

        left_color = QtGui.QColor("#cc0000")
        right_color = QtGui.QColor("#ff0000")
        bottom_color = QtGui.QColor("#661900")

        path_left = QtGui.QPainterPath()
        path_left.lineTo(pos_top)
        path_left.lineTo(pos_left)

        path_right = QtGui.QPainterPath()
        path_right.lineTo(pos_top)
        path_right.lineTo(pos_right)

        path_bottom = QtGui.QPainterPath()
        path_bottom.lineTo(pos_left)
        path_bottom.lineTo(pos_right)

        painter.setPen(QtGui.QColor("black"))
        painter.setBrush(left_color)
        painter.drawPath(path_left)
        painter.setBrush(right_color)
        painter.drawPath(path_right)
        painter.setBrush(bottom_color)
        painter.drawPath(path_bottom)

    def moveTo(self, next_position: QtCore.QPointF, duration: int = 100) -> None:
        """

        :param next_position:
        :param duration:
        """
        self._animation.setStartValue(self.pos())
        self._animation.setEndValue(next_position)
        self._animation.setDuration(duration)
        self._animation.start(QtCore.QAbstractAnimation.DeleteWhenStopped)
        self._animation.valueChanged.connect(self.setPos)

    def rotate(self, theta=15):
        """

        :param theta: the angle of rotation.
        """
        self.setRotation(self.rotation() + theta)

class Win(QtWidgets.QMainWindow):
    zoom_signal = QtCore.pyqtSignal(bool)
    threshold_zoomIn = 3
    threshold_zoomOut = -5

    def __init__(self):
        super(Win, self).__init__()
        self.scene = QtWidgets.QGraphicsScene(self)
        self.view = QtWidgets.QGraphicsView(self.scene) #View(self.scene)  #
        # self.view.setBackgroundBrush(QtCore.Qt.green)
        self.action = QtWidgets.QPushButton("Action")
        self.dx = QtWidgets.QLineEdit()
        self.dx.setText(str(100))
        self.Ldx = QtWidgets.QLabel("dx")
        self.dy = QtWidgets.QLineEdit()
        self.dy.setText(str(100))
        self.Ldy = QtWidgets.QLabel("dy")
        self.setAttribute(QtCore.Qt.WA_AcceptTouchEvents, True)
        central_widget = QtWidgets.QWidget()
        hlayl = QtWidgets.QHBoxLayout()
        hlayl.addWidget(self.Ldx)
        hlayl.addWidget(self.dx)
        hlayl.addWidget(self.Ldy)
        hlayl.addWidget(self.dy)
        vlayl = QtWidgets.QVBoxLayout(central_widget)
        vlayl.addLayout(hlayl)
        vlayl.addWidget(self.action)
        vlayl.addWidget(self.view)
        self.setFixedSize(625, 900)
        self.view.setFixedSize(600, 800)
        self.setCentralWidget(central_widget)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.action.clicked.connect(self.do)
        self.arrow = ArrowItem()
        self.arrow.rotate(180)
        self.arrow.setPos(0, 0)
        self.scene.addItem(self.arrow)
        self.view.setTransformationAnchor(QtWidgets.QGraphicsView.AnchorViewCenter)
        self.view.centerOn(self.arrow)
        self.r = self.view.mapToScene(self.view.viewport().rect()).boundingRect()
        self.view.setSceneRect(self.r)
        self.pen = QtGui.QPen(QtGui.QColor(0, 0, 255, 127),
                              20,
                              QtCore.Qt.SolidLine,
                              QtCore.Qt.RoundCap,
                              QtCore.Qt.RoundJoin)
        self.view.setBackgroundBrush(QtGui.QBrush(QtGui.QPixmap('bg_f.jpg')))
        self.zoomIn_btn = QtWidgets.QPushButton("&Zoom In")
        font = QtGui.QFont()
        font.setPixelSize(18)
        rect = QtCore.QRect(0, 0, 125, 35)
        self.zoomIn_btn.setFont(font)
        self.zoomIn_btn.setIcon(QtGui.QIcon('zoom_in.png'))
        self.zoomIn_btn.setGeometry(rect)
        self.zoomOut_btn = QtWidgets.QPushButton("&Zoom Out")
        self.zoomOut_btn.setFont(font)
        self.zoomOut_btn.setIcon(QtGui.QIcon('zoom_out.png'))
        self.zoomOut_btn.setGeometry(rect)


        self.proxyWidget_zoom_in = self.scene.addWidget(self.zoomIn_btn)
        # btn_2.QtGui.QIcon('zoom_out.png')
        self.proxyWidget_zoom_out = self.scene.addWidget(self.zoomOut_btn)
        self.proxyWidget_zoom_in.setFlag(QtWidgets.QGraphicsItem.ItemIgnoresTransformations)
        self.proxyWidget_zoom_out.setFlag(QtWidgets.QGraphicsItem.ItemIgnoresTransformations)
        rectangle = self.view.mapToScene(self.view.viewport().rect()).boundingRect()
        self.proxyWidget_zoom_in.setPos(rectangle.x() - 20, rectangle.y())
        self.proxyWidget_zoom_out.setPos(rectangle.x() - 20, rectangle.y() -rectangle.y()*0.09)
        self.__zoom = 0
        self.startPos = None

        self.view.viewport().installEventFilter(self)
        self.__startPosition = None
        self.__endPosition = None


    def updateProxyWidget(self):
        rectangle = self.view.mapToScene(self.view.viewport().rect()).boundingRect()
        self.proxyWidget_zoom_in.setPos(rectangle.x(), rectangle.y())
        self.proxyWidget_zoom_out.setPos(rectangle.x(), rectangle.y() - rectangle.y() * 0.09)

    def zoomIn(self):
        if self.__zoom < self.threshold_zoomIn:
            factor = 1.25
            self.__zoom += 1
            self.view.scale(factor, factor)
            self.updateProxyWidget()

        else:
            self.__zoom = self.threshold_zoomIn
            self.updateProxyWidget()

    def zommOut(self):
        if self.__zoom > self.threshold_zoomOut:
            factor = 0.8
            self.__zoom -= 1
            self.view.scale(factor, factor)
            self.updateProxyWidget()

        else:
            self.__zoom = self.threshold_zoomOut
            self.updateProxyWidget()

    def eventFilter(self, widget, event):
        if isinstance(widget, QtWidgets.QWidget):
            if event.type() == QtCore.QEvent.MouseButtonPress:
                self.__startPosition = event.pos()
                if self.proxyWidget_zoom_in.contains(event.pos()):
                    print("Zoom in")
                    self.zoomIn()
                elif self.proxyWidget_zoom_out.contains(event.pos()):
                    print("Zoom Out")
                    self.zommOut()
                self.updateProxyWidget()
                return True
            elif event.type() == QtCore.QEvent.MouseButtonRelease:
                self.__endPosition = event.pos()
                if self.__endPosition is not None and self.__startPosition is not None:
                    ds = self.__startPosition - self.__endPosition
                    if ds.x() != 0 and ds.y() != 0:
                        transform = self.view.transform()
                        dx = ds.x() / transform.m11()
                        dy = ds.y() / transform.m22()
                        self.view.setSceneRect(self.view.sceneRect().translated(dx, dy))
                        self.updateProxyWidget()
                return True
        return False

    def wheelEvent(self, event):

        if event.angleDelta().y() > 0:
            # zoom in
            self.zoomIn()

        else:
            # zoom out
            self.zommOut()

    def do(self):
        dx = float(self.dx.text())
        dy = float(self.dy.text())

        current_pos = self.arrow.pos().x(), self.arrow.pos().y()
        current_point = self.arrow.pos()
        new_pos = QtCore.QPointF(current_pos[0] + dx, current_pos[1] + dy)
        self.arrow.setPos(new_pos)
        print(1)
        track = QtCore.QLineF(current_point, new_pos)
        print(track.length(), current_pos, new_pos)
        print(2)
        self.scene.addLine(track, pen=self.pen)
        print(3)
        self.view.setSceneRect(self.view.sceneRect().translated(dx, dy))
        print(f"The scenerect position is {self.view.sceneRect().x(), self.view.sceneRect().y()}")
        print(f"the view position is: {self.view.pos()}")
        print(f"The arrow position is: x:{self.arrow.x()}, y: {self.arrow.y()}")
        self.updateProxyWidget()




if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = Win()
    w.show()
    sys.exit(app.exec_())

0 ответов

Другие вопросы по тегам