如何防止子 QGraphicsItem 与父级一起移动?

Posted

技术标签:

【中文标题】如何防止子 QGraphicsItem 与父级一起移动?【英文标题】:How to prevent child QGraphicsItem from being moved with parent? 【发布时间】:2018-03-12 15:41:24 【问题描述】:

我有一个GraphicsBoxItem 包含GraphicsImageItem 的列表(框周围的某种浮动按钮)。我在focusInEvent() 中即时创建它们,并在focusOutEvent() 中销毁它们。 一些GraphicsImageItem 在单击时应与父级一起移动,其中一些应在单击时保持在同一位置,直到通过单击任何图形项外部框获得焦点。 有没有办法防止子 QGraphicsItem 与父级一起移动?

class GraphicsBoxItem : public QObject, public QGraphicsItem

    Q_OBJECT
private:
    QColor mColor;
    QVector<GraphicsImageItem*> mItemList;
public:
    GraphicsBoxItem(QGraphicsItem *parent = NULL)
        :QGraphicsItem(parent)
    
        mColor = Qt::lightGray;
        setFlag(QGraphicsItem::ItemIsSelectable);
        setFlag(QGraphicsItem::ItemIsFocusable);
    

    QRectF boundingRect() const  return QRectF(50, 20, 100, 60); 

    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    
        painter->setBrush(mColor);
        painter->drawRect(boundingRect());
    
    virtual void focusOutEvent(QFocusEvent * event)
    
        foreach(GraphicsImageItem *item1, mItemList)
        
            item1->deleteLater();
            item1 = NULL;
        
        mItemList.clear();
        mColor = Qt::lightGray;
        QGraphicsItem::focusOutEvent(event);
    

    virtual void focusInEvent(QFocusEvent * event)
    
        GraphicsImageItem *movableItem = new GraphicsImageItem(this);
        movableItem->setPos(150, 20);
        mItemList.push_back(movableItem);
        movableItem->installSceneEventFilter(this);
        connect(movableItem, SIGNAL(SignalClicked()), this, SLOT(SlotButtonClicked()));

        GraphicsImageItem *nonMovableItem = new GraphicsImageItem(this);
        nonMovableItem->setPos(20, 20);
        mItemList.push_back(nonMovableItem);
        nonMovableItem->installSceneEventFilter(this);
        connect(nonMovableItem, SIGNAL(SignalClicked()), this, SLOT(SlotButtonClicked()));

        mColor = Qt::blue;
        QGraphicsItem::focusInEvent(event);
    

    bool sceneEventFilter(QGraphicsItem* target, QEvent* event)
    
        if(event->type() == QEvent::GraphicsSceneMousePress || event->type() == QEvent::GraphicsSceneMouseDoubleClick)
        
            GraphicsImageItem* item = dynamic_cast<GraphicsImageItem*>(target);
            if(item)
            
                item->SignalClicked();
                qDebug() << "image button was clicked: Need to set the focus back to the box";
                setFocus();
                return true;
            
        
        return QGraphicsItem::sceneEventFilter(target, event);
    
public slots:

    void SlotButtonClicked()
    
        setPos(pos().x() + 10, pos().y()+10);
    
;

SignalClicked()GraphicsBoxItem 移动 10 个像素。一些GraphicsImageItem 留在原地,一些与GraphicsBoxItem 一起移动:

class GraphicsImageItem : public QGraphicsSvgItem

    Q_OBJECT
public:
    GraphicsImageItem::GraphicsImageItem(QGraphicsItem *parent = NULL)
    : QGraphicsSvgItem(QString(":/images/icon.svg"), parent)
    
        setFlag(QGraphicsItem::ItemIsSelectable);
    

    QRectF boundingRect() const  return QRectF(0, 0, 30, 30); 

    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    
        painter->setBrush(Qt::lightGray);
        painter->drawRect(boundingRect());
        renderer()->render(painter, boundingRect().adjusted(3, 3, -3, -3));
    

Q_SIGNALS:
    void SignalClicked();
;

【问题讨论】:

【参考方案1】:

孩子的位置总是相对于它的父母,如果父母移动,那么孩子的绝对位置也会改变。

如果您想制造这种情况不会发生的错觉,则必须手动将孩子朝相反的方向移动,以便它可以保持在相同的绝对位置。

另一种选择是首先不将这些项目作为子项。

也许您应该创建一个不可见的项目,然后将可移动项目以及不可移动的子项添加到它的父项,以及可移动的子项 - 在可移动项下。然后您只需移动可移动项目 - 它的子项移动,其兄弟项保持在同一位置,因为它们的父项不移动。

【讨论】:

我尝试了为可移动项目使用不同父级的方法。但是这种方法不起作用,因为我重新创建了focusIn() 中的所有项目。在该功能中,我不知道我是因为单击按钮(按钮应保持在同一位置)还是因为我单击了任何项目之外而到达那里。在下一次单击框后,我需要将不可移动按钮放置在框旁边。有没有办法在按下GraphicsImageItem 按钮时防止框获得focusOut() 事件? @user2246120 Inside that function I can't know 可以,只需传递一个参数来指示来源。 我的意思是 focusInEvent(QFocusEvent*) 来自 Qt。最初,当框获得焦点时,不可移动项位于框的左上角。然后该项目在盒子移动时保持不变。当在场景外按下并再次选择框时,不可移动的项目现在应该再次放置在框旁边。在单击不可移动项目时不会失去对框的关注将解决问题......这可能吗?【参考方案2】:

对于您不想移动的子项,请尝试设置此标志:

setFlag(QGraphicsItem::ItemIgnoresTransformations);

【讨论】:

这在这种情况下似乎不起作用。文档说:“它的位置仍然锚定到它的父级”......【参考方案3】:

如果您可以子类化您的子项,那么您可以重写 itemChange() 方法并注意:

ItemScenePositionHasChanged

该项目的场景位置已更改。发送此通知,如果 ItemSendsScenePositionChanges 标志已启用,并且在 项目的场景位置已更改(即位置或 项目本身的变换或位置或变换 任何祖先都改变了)。 value 参数是新场景 位置(与 scenePos() 相同),并且 QGraphicsItem 忽略 此通知的返回值(即只读通知)。

然后返回子项的最后一个 scenePos(),您必须将其存储为成员并更新每个场景位置更改。

记得设置标志:

ItemSendsGeometryChanges

在您的子类的 ctor 中。

【讨论】:

以上是关于如何防止子 QGraphicsItem 与父级一起移动?的主要内容,如果未能解决你的问题,请参考以下文章

JavaFX-与父级分开的子边界框

MAX日期的Laravel子查询-与父级的子查询链接中的日期时间格式无效

如何使 div 与父级高度相同(显示为表格单元格)

单击子锚点时,如何防止触发父级的 onclick 事件?

嵌套视图不遵守旋转父级的约束(变换)

如何从可能是与给定 id 匹配的父级或子级之一的列表中获取对象