如何阻止部分子QGraphicsItem随父QGraphicsItem移动?
解决QGraphicsBoxItem子项不随父项移动的问题
我来给你几个实用的方案,都是Qt图形框架里常用的思路,完美匹配你的需求:
方法一:把固定子项的父对象切换到场景
Qt里QGraphicsItem的位置是相对于父项的,所以父项移动时子项会跟着动。如果想让部分子项固定不动,最直接的办法就是把这些子项的父从GraphicsBoxItem改成你的QGraphicsScene。这样父项移动时,这些子项因为属于场景,位置就不会受父项影响了。
代码示例(在focusInEvent中创建子项时):
void GraphicsBoxItem::focusInEvent(QFocusEvent* event) { QGraphicsItem::focusInEvent(event); // 创建需要随父项移动的子项(父设为this) QGraphicsImageItem* movableItem = new QGraphicsImageItem(this); movableItem->setPos(10, 10); // 相对于父项的位置 // 创建需要固定的子项(父设为场景) QGraphicsImageItem* fixedItem = new QGraphicsImageItem(); fixedItem->setParentItem(scene()); // 把父项内的坐标转换成场景坐标,确保初始位置正确 QPointF fixedScenePos = this->mapToScene(50, 10); fixedItem->setPos(fixedScenePos); // 给固定子项加个标记,方便后续focusOut时清理 fixedItem->setData(Qt::UserRole, "FixedChildOfBox"); }
清理注意事项:
因为固定子项的父是场景,GraphicsBoxItem销毁时不会自动清理它们,所以要在focusOutEvent里额外处理:
void GraphicsBoxItem::focusOutEvent(QFocusEvent* event) { QGraphicsItem::focusOutEvent(event); // 清理父为this的子项(随动的那些) for (auto item : childItems()) { delete item; } // 清理场景中的固定子项 if (scene()) { QList<QGraphicsItem*> sceneItems = scene()->items(); for (auto item : sceneItems) { if (item->data(Qt::UserRole) == "FixedChildOfBox") { delete item; } } } }
方法二:重写子项的itemChange方法拦截位置更新
如果不想改变父对象关系,也可以给固定子项自定义一个子类,重写itemChange方法,拦截父项移动带来的位置变化,强制保持原来的场景位置。
自定义固定子项类示例:
class FixedGraphicsImageItem : public QGraphicsImageItem { public: FixedGraphicsImageItem(QGraphicsItem* parent = nullptr) : QGraphicsImageItem(parent) { // 初始化时记录当前的场景位置 m_fixedScenePos = mapToScene(pos()); } QVariant itemChange(GraphicsItemChange change, const QVariant& value) override { // 当父项移动导致子项场景位置变化时,强制拉回原来的位置 if (change == ItemScenePositionHasChanged && parentItem()) { // 将固定场景位置转换成父项坐标系下的位置 QPointF localPos = mapFromScene(m_fixedScenePos); setPos(localPos); // 返回空QVariant阻止后续的位置更新逻辑 return QVariant(); } return QGraphicsImageItem::itemChange(change, value); } private: QPointF m_fixedScenePos; };
使用方式:
在focusInEvent中创建这个自定义类的实例,父项设为GraphicsBoxItem即可,后续focusOutEvent清理时直接遍历childItems删除就行,不用额外处理场景里的项。
这两种方法都能实现你的需求,第一种更简单直观,第二种适合不想改动父项结构的场景,你可以根据自己的代码情况选~
内容的提问来源于stack exchange,提问作者user2246120
相关产品推荐
相关产品推荐

