PyQt: Moving multiple items with different ItemIgn

2019-05-22 02:36发布

问题:

Sometimes selected items do not move together.

This happens in an application with two types of items:

  • "regular items"
  • "handles" with ItemIgnoresTransformations flag (they must keep the same size upon zooming)

When they are selected together, and moved by mouse, they are expected to be translated by the same amount (they should move as a whole). It works fine when only "regular items" or only "handles" are moved, but not when both types are selected.

Here is a minimal example displaying the problem. It can also be found on sourceforge, together with an enhanced version displaying also some properties of the items.

import sys
from PyQt4 import QtGui, QtCore

class View(QtGui.QGraphicsView):
    def __init__(self):
        QtGui.QGraphicsView.__init__(self)

        self.scene = QtGui.QGraphicsScene(self)
        #self.scene.setSceneRect(QtCore.QRectF(0, 0, 245, 245))

        self.setScene(self.scene)

        self.item1 = QtGui.QGraphicsRectItem(0, 0, 60, 40)
        self.item1.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
        self.item1.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
        self.scene.addItem(self.item1)

        self.item2 = QtGui.QGraphicsRectItem(0, 0, 40, 60)
        self.item2.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
        self.item2.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
        self.item2.setFlag(QtGui.QGraphicsItem.ItemIgnoresTransformations, True)
        self.scene.addItem(self.item2)

        self.item1.setSelected(True)
        self.item2.setSelected(True)


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    view = View()
    view.show()
    sys.exit(app.exec_())

Both items are already selected. Clicking on one item and dragging (say moving to the left) seems to move only one item. This seems to happen when the sceneBoundingRect is updated. Moving back (say to the right), both are translated together until the sceneBoundingRect has to be enlarged again.

When the setSceneRect line is not commented, the behaviour is correct.

The documentation for ItemIgnoresTransformations says

The item ignores inherited transformations (i.e., its position is still anchored to its parent, but the parent or view rotation, zoom or shear transformations are ignored). This flag is useful for keeping text label items horizontal and unscaled, so they will still be readable if the view is transformed. When set, the item's view geometry and scene geometry will be maintained separately. You must call deviceTransform() to map coordinates and detect collisions in the view. By default, this flag is disabled. This flag was introduced in Qt 4.3. Note: With this flag set you can still scale the item itself, and that scale transformation will influence the item's children.

I asked a specific question about the first bold face sentence What is the meaning of "the item's view geometry and scene geometry will be maintained separately"?. This might be relevant here.

Reading its doc and searching, deviceTransform() does not seem to do any magical stuff like updating boundingRect. Would it really be useful here ? How ?

In Maintaining relative child position after applying QGraphicsItem::ItemIgnoresTransformations the recommendation is to not use the ItemIgnoresTransformations flag. But it was about parent/child relationship.

This thread was worth reading, but also more about parent/child.

Issue with fitInView of QGraphicsView when ItemIgnoresTransformations is on was interesting also, but changing the origin does not change the core problem here.

So there are several possible solutions (of course, answers showing solutions would be useful too), but I'm more looking for an in-depth explanation of what's going on here.