Как обрабатывать кнопки прокси внутри 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_())