QGraphicsItemGroup 的翻译被销毁后发送到哪里?

Where is QGraphicsItemGroup's translation dispatched after it had been destroyed?

根据 .

,我使用 QGraphicsItemGroup 共同移动场景中的 QGraphicsItem 个对象

当组处于活动状态时,它会保留自己的变换。现在一旦它被摧毁,这些变换就不复存在,必须被派遣。我的问题是它是如何完成的——尤其是翻译。

我做了一个小例子,其中显示了一个矩形,然后在停顿 5 秒后缩放和平移。每个步骤后都会显示对象的转换。

from PyQt5.QtCore import Qt, QThread
from PyQt5.QtGui import QTransform
from PyQt5.QtWidgets import QApplication, QGraphicsRectItem, QGraphicsScene, QGraphicsView, QMainWindow
import sys
import time


def print_transforms(obj):
  t = obj.transform()
  print(t.m11(), t.m12(), t.m13(), t.m21(), t.m22(), t.m23(), t.m31(), t.m32(), t.m33(), t.dx(), t.dy())
  assert len(obj.transformations()) == 0
  print(obj.rotation(), obj.scale())


class Thread(QThread):

  def run(self):
    print('initial transforms')
    print_transforms(rect)
    time.sleep(5)

    group = scene.createItemGroup([rect])
    group.setTransform(QTransform().scale(1.5, 1.5))
    scene.destroyItemGroup(group)
    print('after scaling')
    print_transforms(rect)
    time.sleep(5)

    group = scene.createItemGroup([rect])
    group.setTransform(QTransform().translate(100., 100.))
    scene.destroyItemGroup(group)
    print('after translation')
    print_transforms(rect)
    time.sleep(5)


app = QApplication([])
window = QMainWindow()
window.setGeometry(100, 100, 400, 400)
view = QGraphicsView()
scene = QGraphicsScene()
view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

rect = QGraphicsRectItem(0, 0, 150, 150)
scene.addItem(rect)

view.setScene(scene)
window.setCentralWidget(view)
window.show()
thread = Thread()
thread.finished.connect(app.exit)
thread.start()
sys.exit(app.exec_())

它打印以下内容:

initial transforms
1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0
0.0 1.0
after scaling
1.5 0.0 0.0 0.0 1.5 0.0 0.0 0.0 1.0 0.0 0.0
0.0 1.0
after translation
1.5 0.0 0.0 0.0 1.5 0.0 0.0 0.0 1.0 0.0 0.0
0.0 1.0

所以我们从一个恒等变换矩阵开始,在缩放之后它变成了一个缩放矩阵来保存缩放因子。

然而,在平移之后,变换矩阵没有改变,尽管矩形确实移动了。

所以我的问题是,群的翻译被摧毁后被派往哪里?

首先不要使用QThread + sleep来做延迟,因为你可能会有奇怪的行为,Qt通过指向控制台上的警告来提醒你:

initial transforms
1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0
0.0 1.0
<b>QObject::startTimer: Timers cannot be started from another thread
QObject::startTimer: Timers cannot be started from another thread</b>
after scaling
1.5 0.0 0.0 0.0 1.5 0.0 0.0 0.0 1.0 0.0 0.0
0.0 1.0
<b>QObject::startTimer: Timers cannot be started from another thread
QObject::startTimer: Timers cannot be started from another thread</b>
after translation
1.5 0.0 0.0 0.0 1.5 0.0 0.0 0.0 1.0 0.0 0.0
0.0 1.0

改为使用 QTimer。


回到问题,让我们分析应用更改时项目的位置:

from PyQt5.QtCore import Qt, QObject, QTimer
from PyQt5.QtGui import QTransform
from PyQt5.QtWidgets import (
    QApplication,
    QGraphicsRectItem,
    QGraphicsScene,
    QGraphicsView,
    QMainWindow,
)
import sys


def print_transforms(obj):
    t = obj.transform()
    print(
        t.m11(),
        t.m12(),
        t.m13(),
        t.m21(),
        t.m22(),
        t.m23(),
        t.m31(),
        t.m32(),
        t.m33(),
        t.dx(),
        t.dy(),
    )
    assert len(obj.transformations()) == 0
    print(obj.rotation(), obj.scale())


class Test(QObject):
    def initial(self):
        print(rect.pos())
        print("initial transforms")
        print_transforms(rect)
        QTimer.singleShot(5000, self.scaling)

    def scaling(self):
        print(rect.pos())
        group = scene.createItemGroup([rect])
        group.setTransform(QTransform().scale(1.5, 1.5))
        scene.destroyItemGroup(group)
        print("after scaling")
        print_transforms(rect)
        QTimer.singleShot(5000, self.translation)
        print(rect.pos())

    def translation(self):
        print(rect.pos())

        group = scene.createItemGroup([rect])
        group.setTransform(QTransform().translate(100.0, 100.0))
        print(rect.pos())
        scene.destroyItemGroup(group)
        print("after translation")
        print_transforms(rect)

        print(rect.pos())

        QTimer.singleShot(1000, QApplication.quit)


if __name__ == "__main__":

    app = QApplication([])
    window = QMainWindow()
    window.setGeometry(100, 100, 400, 400)
    view = QGraphicsView()
    scene = QGraphicsScene()
    view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
    view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

    rect = QGraphicsRectItem(0, 0, 150, 150)
    scene.addItem(rect)

    view.setScene(scene)
    window.setCentralWidget(view)
    window.show()
    test = Test()
    test.initial()
    sys.exit(app.exec_())

你得到以下内容:

PyQt5.QtCore.QPointF()
initial transforms
1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0
0.0 1.0
PyQt5.QtCore.QPointF()
after scaling
1.5 0.0 0.0 0.0 1.5 0.0 0.0 0.0 1.0 0.0 0.0
0.0 1.0
PyQt5.QtCore.QPointF()
PyQt5.QtCore.QPointF()
PyQt5.QtCore.QPointF()
after translation
1.5 0.0 0.0 0.0 1.5 0.0 0.0 0.0 1.0 0.0 0.0
0.0 1.0
PyQt5.QtCore.QPointF(100.0, 100.0)

可以看到翻译应用于项目的位置而不是 QTransform,因为使用 destroyItemGroup() 时表示:

void QGraphicsScene::destroyItemGroup(QGraphicsItemGroup *group)

Reparents all items in group to group's parent item, then removes group from the scene, and finally deletes it. The items' positions and transformations are mapped from the group to the group's parent.

(强调我的)

结论:当组被销毁时,除了平移之外的所有转换都传递给项目,因为项目是使用 setPos 移动的。