将 child QGraphicsItem 约束到场景?
Constraining child QGraphicsItem to scene?
有没有人有更好的方法来将 QGraphicsItem
的 child 约束到场景?
我已经通过覆盖 itemChange
成功地将 parent QGraphicsItem
正确地约束到它的场景,但现在我需要对 child [=12] 做同样的事情=].
示例Use-case:
此代码在大多数情况下都有效。唯一的问题是 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;
}
对于 parent 项,我使用了:
newPos.setX(qMin(scRect.right(), qMax(newPos.x(), scRect.left())));
效果很好,但我不知道如何或是否可以在这里使用它。
首先,具体来说,场景实际上是没有边界的。您要做的是将项目限制在您在别处设置的场景矩形中。
我看到的问题是您对 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" 并行。它们完全一样,但我认为在这种情况下读起来稍微好一些。
有没有人有更好的方法来将 QGraphicsItem
的 child 约束到场景?
我已经通过覆盖 itemChange
成功地将 parent QGraphicsItem
正确地约束到它的场景,但现在我需要对 child [=12] 做同样的事情=].
示例Use-case:
此代码在大多数情况下都有效。唯一的问题是 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;
}
对于 parent 项,我使用了:
newPos.setX(qMin(scRect.right(), qMax(newPos.x(), scRect.left())));
效果很好,但我不知道如何或是否可以在这里使用它。
首先,具体来说,场景实际上是没有边界的。您要做的是将项目限制在您在别处设置的场景矩形中。
我看到的问题是您对 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" 并行。它们完全一样,但我认为在这种情况下读起来稍微好一些。