如何translate/drag/move QGraphicsScene?
How to translate/drag/move QGraphicsScene?
我知道这个问题已经回答了很多次,尤其是在 Qt 的 C++ 版本中,但是我的 C++ 不太好,我找不到解决方案。
我有一个带有 QGraphicsView
的代码,中间有一个由 QGraphicsPolygonItem
组成的矩形。我正在尝试找到一种让用户制作 QGraphicsScene translatable/movable/dragable 的方法(任何事情都可以,我只是想让用户可以选择在场景中移动)。但是 none 我的尝试是可行的。
我试过设置:
self.horizontalScrollBar().setValue()
和 self.verticalScrollBar().setValue()
self._scene.setSceneRect(x,y,w,h)
将锚点设置为 AnchorUnderMouse
和 NoAnchor
使用translate()
None 让我的场景移动...唯一让我的场景移动的是 setSceneRect()
,但是一旦我把它放在 mouseMoveEvent(self,event)
下它就停止工作了。有人可以帮我学习如何在场景中绕着那个矩形移动吗?
代码:
from PyQt5.QtGui import QColor, QPolygonF, QPen, QBrush
from PyQt5.QtCore import Qt, QPointF, QPoint, pyqtSignal
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QGraphicsView, QGraphicsScene, QGraphicsPolygonItem, QApplication, \
QFrame, QSizePolicy
points_list = [[60.1, 19.6, 0.0], [60.1, 6.5, 0.0], [60.1, -6.5, 0.0], [60.1, -19.6, 0.0], [60.1, -19.6, 0.0],
[20.0, -19.6, 0.0], [-20, -19.6, 0.0], [-60.1, -19.6, 0.0], [-60.1, -19.6, 0.0], [-60.1, -6.5, 0.0],
[-60.1, 6.5, 0.0], [-60.1, 19.6, 0.0], [-60.1, 19.6, 0.0], [-20.0, 19.6, 0.0], [20.0, 19.6, 0.0],
[60.1, 19.6, 0.0]]
class MainWindow(QDialog):
def __init__(self, parent=None):
QDialog.__init__(self, parent=parent)
self.create()
def create(self, **kwargs):
main_layout = QVBoxLayout()
graphics = MainGraphicsWidget()
main_layout.addWidget(graphics)
self.setLayout(main_layout)
class MainGraphicsWidget(QGraphicsView):
zoom_signal = pyqtSignal(bool)
def __init__(self, parent=None):
super(MainGraphicsWidget, self).__init__(parent)
self._scene = QGraphicsScene(backgroundBrush=Qt.gray)
self.__zoom = 0
self.setScene(self._scene)
self.setTransformationAnchor(QGraphicsView.NoAnchor)
#self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setBackgroundBrush(QBrush(QColor(30, 30, 30)))
self.setFrameShape(QFrame.NoFrame)
self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
self.sceneRect = self._scene.sceneRect()
self.testButton = GraphicsButton()
self._scene.addItem(self.testButton)
#self.horizontalScrollBar().setValue(199)
#self.verticalScrollBar().setValue(500)
def mouseMoveEvent(self, event):
modifierPressed = QApplication.keyboardModifiers()
if (modifierPressed & Qt.AltModifier) == Qt.AltModifier and event.buttons() == Qt.LeftButton:
#self._scene.setSceneRect(event.pos().x(), event.pos().y(), self.sceneRect.width(), self.sceneRect.height())
pass
super(MainGraphicsWidget, self).mouseMoveEvent(event)
def wheelEvent(self, event):
if event.angleDelta().y() > 0:
factor = 1.25
self.__zoom += 1
else:
factor = 0.8
self.__zoom -= 1
self.scale(factor, factor)
self.zoom_signal.emit(self.__zoom < 10)
class GraphicsButton(QGraphicsPolygonItem):
def __init__(self, parent=None):
super(GraphicsButton, self).__init__(parent)
self.myPolygon = QPolygonF([QPointF(v1, v2) for v1, v2, v3 in points_list])
self.setPen(QPen(QColor(0, 0, 0), 0, Qt.SolidLine, Qt.FlatCap, Qt.MiterJoin))
self.setPolygon(self.myPolygon)
self.setBrush(QColor(220, 40, 30))
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = MainWindow()
window.setGeometry(500, 100, 500, 900)
window.show()
sys.exit(app.exec_())
您的方法不起作用,因为您始终 "translating" sceneRect 的原点与 event.pos()
坐标,并且由于这些值始终为正,因此您 "focusing"在一个明显向相反方向平移的矩形上。
例如,如果您在右侧拖动鼠标,就像在右侧移动相机一样:图片的内容将在左侧"moved"。
虽然使用负 x
和 y
位置将是最合乎逻辑的解决方案,但对于真正的 "drag" 操作来说它不会有效,因为坐标是基于小部件;如果你拖动远离原点(小部件的左上角),平移会更大:如果你从视图的中心开始拖动,场景矩形将水平平移 250 像素,垂直平移 450 像素(因为你的 window 尺寸是 500x900)。
最好的方法是跟踪先前的鼠标位置(从鼠标按下事件开始)并根据 mouseMoveEvent 位置之间的差异平移场景。
由于场景可能会应用一些缩放比例(因为您正在使用滚轮缩放),我们也必须考虑该比例。
class MainGraphicsWidget(QGraphicsView):
zoom_signal = pyqtSignal(bool)
def __init__(self, parent=None):
super(MainGraphicsWidget, self).__init__(parent)
# ...
# I'm commenting this line, as sceneRect is a property of QGraphicsView
# and should not be overwritten
# self.sceneRect = self._scene.sceneRect()
self.testButton = GraphicsButton()
self._scene.addItem(self.testButton)
self.startPos = None
def mousePressEvent(self, event):
if event.modifiers() & Qt.ControlModifier and event.button() == Qt.LeftButton:
# store the origin point
self.startPos = event.pos()
else:
super(MainGraphicsWidget, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if self.startPos is not None:
# compute the difference between the current cursor position and the
# previous saved origin point
delta = self.startPos - event.pos()
# get the current transformation (which is a matrix that includes the
# scaling ratios
transform = self.transform()
# m11 refers to the horizontal scale, m22 to the vertical scale;
# divide the delta by their corresponding ratio
deltaX = delta.x() / transform.m11()
deltaY = delta.y() / transform.m22()
# translate the current sceneRect by the delta
self.setSceneRect(self.sceneRect().translated(deltaX, deltaY))
# update the new origin point to the current position
self.startPos = event.pos()
else:
super(MainGraphicsWidget, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
self.startPos = None
super(MainGraphicsWidget, self).mouseReleaseEvent(event)
请注意,我使用 ControlModifier
是因为在 Linux 上,Alt 修饰符通常用于移动 windows。
我知道这个问题已经回答了很多次,尤其是在 Qt 的 C++ 版本中,但是我的 C++ 不太好,我找不到解决方案。
我有一个带有 QGraphicsView
的代码,中间有一个由 QGraphicsPolygonItem
组成的矩形。我正在尝试找到一种让用户制作 QGraphicsScene translatable/movable/dragable 的方法(任何事情都可以,我只是想让用户可以选择在场景中移动)。但是 none 我的尝试是可行的。
我试过设置:
self.horizontalScrollBar().setValue()
和self.verticalScrollBar().setValue()
self._scene.setSceneRect(x,y,w,h)
将锚点设置为
AnchorUnderMouse
和NoAnchor
使用
translate()
None 让我的场景移动...唯一让我的场景移动的是 setSceneRect()
,但是一旦我把它放在 mouseMoveEvent(self,event)
下它就停止工作了。有人可以帮我学习如何在场景中绕着那个矩形移动吗?
代码:
from PyQt5.QtGui import QColor, QPolygonF, QPen, QBrush
from PyQt5.QtCore import Qt, QPointF, QPoint, pyqtSignal
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QGraphicsView, QGraphicsScene, QGraphicsPolygonItem, QApplication, \
QFrame, QSizePolicy
points_list = [[60.1, 19.6, 0.0], [60.1, 6.5, 0.0], [60.1, -6.5, 0.0], [60.1, -19.6, 0.0], [60.1, -19.6, 0.0],
[20.0, -19.6, 0.0], [-20, -19.6, 0.0], [-60.1, -19.6, 0.0], [-60.1, -19.6, 0.0], [-60.1, -6.5, 0.0],
[-60.1, 6.5, 0.0], [-60.1, 19.6, 0.0], [-60.1, 19.6, 0.0], [-20.0, 19.6, 0.0], [20.0, 19.6, 0.0],
[60.1, 19.6, 0.0]]
class MainWindow(QDialog):
def __init__(self, parent=None):
QDialog.__init__(self, parent=parent)
self.create()
def create(self, **kwargs):
main_layout = QVBoxLayout()
graphics = MainGraphicsWidget()
main_layout.addWidget(graphics)
self.setLayout(main_layout)
class MainGraphicsWidget(QGraphicsView):
zoom_signal = pyqtSignal(bool)
def __init__(self, parent=None):
super(MainGraphicsWidget, self).__init__(parent)
self._scene = QGraphicsScene(backgroundBrush=Qt.gray)
self.__zoom = 0
self.setScene(self._scene)
self.setTransformationAnchor(QGraphicsView.NoAnchor)
#self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setBackgroundBrush(QBrush(QColor(30, 30, 30)))
self.setFrameShape(QFrame.NoFrame)
self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
self.sceneRect = self._scene.sceneRect()
self.testButton = GraphicsButton()
self._scene.addItem(self.testButton)
#self.horizontalScrollBar().setValue(199)
#self.verticalScrollBar().setValue(500)
def mouseMoveEvent(self, event):
modifierPressed = QApplication.keyboardModifiers()
if (modifierPressed & Qt.AltModifier) == Qt.AltModifier and event.buttons() == Qt.LeftButton:
#self._scene.setSceneRect(event.pos().x(), event.pos().y(), self.sceneRect.width(), self.sceneRect.height())
pass
super(MainGraphicsWidget, self).mouseMoveEvent(event)
def wheelEvent(self, event):
if event.angleDelta().y() > 0:
factor = 1.25
self.__zoom += 1
else:
factor = 0.8
self.__zoom -= 1
self.scale(factor, factor)
self.zoom_signal.emit(self.__zoom < 10)
class GraphicsButton(QGraphicsPolygonItem):
def __init__(self, parent=None):
super(GraphicsButton, self).__init__(parent)
self.myPolygon = QPolygonF([QPointF(v1, v2) for v1, v2, v3 in points_list])
self.setPen(QPen(QColor(0, 0, 0), 0, Qt.SolidLine, Qt.FlatCap, Qt.MiterJoin))
self.setPolygon(self.myPolygon)
self.setBrush(QColor(220, 40, 30))
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = MainWindow()
window.setGeometry(500, 100, 500, 900)
window.show()
sys.exit(app.exec_())
您的方法不起作用,因为您始终 "translating" sceneRect 的原点与 event.pos()
坐标,并且由于这些值始终为正,因此您 "focusing"在一个明显向相反方向平移的矩形上。
例如,如果您在右侧拖动鼠标,就像在右侧移动相机一样:图片的内容将在左侧"moved"。
虽然使用负 x
和 y
位置将是最合乎逻辑的解决方案,但对于真正的 "drag" 操作来说它不会有效,因为坐标是基于小部件;如果你拖动远离原点(小部件的左上角),平移会更大:如果你从视图的中心开始拖动,场景矩形将水平平移 250 像素,垂直平移 450 像素(因为你的 window 尺寸是 500x900)。
最好的方法是跟踪先前的鼠标位置(从鼠标按下事件开始)并根据 mouseMoveEvent 位置之间的差异平移场景。
由于场景可能会应用一些缩放比例(因为您正在使用滚轮缩放),我们也必须考虑该比例。
class MainGraphicsWidget(QGraphicsView):
zoom_signal = pyqtSignal(bool)
def __init__(self, parent=None):
super(MainGraphicsWidget, self).__init__(parent)
# ...
# I'm commenting this line, as sceneRect is a property of QGraphicsView
# and should not be overwritten
# self.sceneRect = self._scene.sceneRect()
self.testButton = GraphicsButton()
self._scene.addItem(self.testButton)
self.startPos = None
def mousePressEvent(self, event):
if event.modifiers() & Qt.ControlModifier and event.button() == Qt.LeftButton:
# store the origin point
self.startPos = event.pos()
else:
super(MainGraphicsWidget, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if self.startPos is not None:
# compute the difference between the current cursor position and the
# previous saved origin point
delta = self.startPos - event.pos()
# get the current transformation (which is a matrix that includes the
# scaling ratios
transform = self.transform()
# m11 refers to the horizontal scale, m22 to the vertical scale;
# divide the delta by their corresponding ratio
deltaX = delta.x() / transform.m11()
deltaY = delta.y() / transform.m22()
# translate the current sceneRect by the delta
self.setSceneRect(self.sceneRect().translated(deltaX, deltaY))
# update the new origin point to the current position
self.startPos = event.pos()
else:
super(MainGraphicsWidget, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
self.startPos = None
super(MainGraphicsWidget, self).mouseReleaseEvent(event)
请注意,我使用 ControlModifier
是因为在 Linux 上,Alt 修饰符通常用于移动 windows。