Issue
I know this question was answered a lot of times especially in C++ version of Qt, but I am not so good in C++ and I can't find the solution.
I have a code with QGraphicsView
with rectangle made out of QGraphicsPolygonItem
in center. I am trying to find a way to make QGraphicsScene translatable/movable/dragable by a user(anything would be okay, I am just trying to give a user an option to move around scene). But none of my tries would work.
I tried setting :
self.horizontalScrollBar().setValue()
andself.verticalScrollBar().setValue()
self._scene.setSceneRect(x,y,w,h)
setting anchor to
AnchorUnderMouse
andNoAnchor
using
translate()
None of it makes my scene move... Only thing which made my scene move is setSceneRect()
, but once I put it under mouseMoveEvent(self,event)
it stops working. Can somebody help me to learn how to move around that rectangle in scene ?
Code:
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_())
Solution
Your approach doesn't work because you are constantly "translating" the origin point of the sceneRect with the event.pos()
coordinates, and since those values are always positive, you are "focusing" on a rectangle that is apparently translated in the opposite direction.
For example, if you drag moving your mouse on your right, it's like moving a camera on the right: the contents of the picture will be "moved" on the left.
While using negative x
and y
positions would be the most logic solution, it wouldn't be effective for a real "drag" operation, since the coordinates are based on the widget; if you drag far from the origin point (the top left corner of the widget), the translation will be bigger: if you start dragging from the center of the view, the scene rectangle will be translated by 250 pixel horizontally and 450 vertically (since your window size is 500x900).
The best approach is to keep track of the previous mouse position (starting from the mouse press event) and translate the scene rect by the difference between the mouseMoveEvent position.
Since there could be some scaling applied to the scene (as you are using the wheel to zoom), we have to take into account that ratios too.
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)
Note that I used ControlModifier
because on Linux the Alt modifier is commonly used to move windows.
Answered By - musicamante
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.