将子QGraphicsItem约束到场景?

时间:2016-10-31 21:19:41

标签: c++ qt parent-child qgraphicsscene qgraphicsitem

有没有人有更好的方法将QGraphicsItem的孩子约束到某个场景?

我已成功通过覆盖QGraphicsItem将父级itemChange正确约束到其场景,但现在我需要为子级QGraphicsItem执行相同操作。

示例用例:

An Example Use Case with two handles (children) and a bar (parent)

此代码在大多数情况下都有效。唯一的问题是 QGraphicsItem的速度,当击中任何一方会影响其终点位置

QVariant SizeGripItem::HandleItem::itemChange(GraphicsItemChange change,
                                              const QVariant &value)
{
    QPointF newPos = value.toPointF();
    if (change == ItemPositionChange)
    {
        if(scene())
        {
            newPos.setY(pos().y()); // Y-coordinate is constant.

            if(scenePos().x() < 0 ) //If child item is off the left side of the scene,
            {
                if (newPos.x() < pos().x()) // and is trying to move left,
                {
                  newPos.setX(pos().x()); // then hold its position
                }
            }
            else if( scenePos().x() > scene()->sceneRect().right()) //If child item is off the right side of the scene,
            {
                if (newPos.x() > pos().x()) //and is trying to move right,
                {
                  newPos.setX(pos().x()); // then hold its position
                }
            }
        }
    }
 return newPos;
}

对于父项,我用过: newPos.setX(qMin(scRect.right(), qMax(newPos.x(), scRect.left()))); 这完美无缺,但我很难知道如何在这里使用它。

1 个答案:

答案 0 :(得分:2)

首先,具体而言,场景实际上没有边界。您要做的是将项目约束到您在其他位置设置的场景矩形。

我看到的问题在于你使用了scenePos。这是一个ItemPositionChange;项目的scenePos尚未使用新位置进行更新,因此当您检查scenePos是否超出场景时,您实际上是在检查上次位置更改的结果,而不是当前位置更改的结果。因此,您的项目最终会离开场景矩形的边缘,然后粘在那里。离边缘有多远取决于移动鼠标的速度,这决定了ItemPositionChange通知之间的距离。

相反,您需要将新位置与场景矩形进行比较,然后将返回的值限制在场景矩形内。您需要场景坐标中的新位置进行比较,因此您需要以下内容:

QPoint new_scene_pos = mapToScene (new_pos);

if (new_scene_pos.x() < scene()->sceneRect().left())
    {
    new_scene_pos.setX (scene()->sceneRect().left());
    new_pos = mapFromScene (new_scene_pos);
    }

这显然不是完整的代码,但这些是您需要做的转换和检查,以便将其保留在左侧。右侧非常相似,所以只需使用new_scene_pos进行比较。

请注意,我没有假设sceneRecT的左边缘为0.我确定你在设置sceneRect的位置编码,但是使用实际的左值而不是假设它为0会消除任何问题,如果你最后改变你计划使用的场景坐标范围。

我在sceneRect调用中使用了“left”而不是“x”,因为它与另一侧的“right”平行。它们完全相同,但我认为在这种情况下读数略好一些。