【问题标题】:How to translate/drag/move QGraphicsScene?如何翻译/拖动/移动 QGraphicsScene?
【发布时间】:2020-04-01 23:28:05
【问题描述】:

我知道这个问题被回答了很多次,尤其是在 Qt 的 C++ 版本中,但我的 C++ 不太好,我找不到解决方案。

我有一个带有QGraphicsView 的代码,中间有一个由QGraphicsPolygonItem 组成的矩形。我正在尝试找到一种方法使 QGraphicsScene 可由用户翻译/移动/拖动(一切都可以,我只是想给用户一个在场景中移动的选项)。但我的任何尝试都行不通。

我试过设置:

  • self.horizontalScrollBar().setValue()self.verticalScrollBar().setValue()

  • self._scene.setSceneRect(x,y,w,h)

  • 将锚点设置为AnchorUnderMouseNoAnchor

  • 使用translate()

它们都不会让我的场景移动...唯一让我的场景移动的是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_())

【问题讨论】:

    标签: python pyqt pyqt5 qgraphicsview qgraphicsscene


    【解决方案1】:

    您的方法不起作用,因为您一直在使用 event.pos() 坐标“平移”sceneRect 的原点,并且由于这些值始终为正值,因此您正在“关注”一个明显被平移的矩形反方向。
    例如,如果您将鼠标拖动到右侧,就像在右侧移动相机一样:图片的内容将在左侧“移动”。

    虽然使用负的xy 位置是最合乎逻辑的解决方案,但它对于真正的“拖动”操作无效,因为坐标是基于小部件的;如果从原点(小部件的左上角)拖动很远,平移会更大:如果从视图中心开始拖动,场景矩形将水平平移 250 像素,垂直平移 450 像素(因为您的窗口大小为 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 修饰符通常用于移动窗口。

    【讨论】:

    • 再次感谢 :) 我不知道 event.pos() 只给出正值。我认为它总是指向你的鼠标在 x,y 坐标上的位置。对我来说也是一个很好的发现,使用 self.transform()。但有必要吗?您在 cmets 中写道,它用于缩放比例,但您可以从 sceneRect 的宽度和高度中获得,对吗?不一样吗?
    • 它们是xy坐标,但是从左上角到右下角都是正数(当然,如果你把鼠标拖到窗口外超出左上角,它们就是负数,但这不是重点)。使用 transform() 是必要的,因为视图的 sceneRect 可能小于或大于视口 rect(滚动区域边界内场景的可见部分),并且它可能与实际场景的 sceneRect 不同,但这不会告诉你任何关于缩放:如果场景矩形是 (0, 0, 100, 100) 并且你放大,矩形仍然是一样的。
    猜你喜欢
    • 1970-01-01
    • 2016-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-19
    • 1970-01-01
    相关资源
    最近更新 更多