Qt源码分析: QChildEvent

发布于:2025-06-24 ⋅ 阅读:(16) ⋅ 点赞:(0)

本文记录QChildEvent事件相关代码分析。

注1:限于笔者研究水平,难免有表述不当,欢迎批评指正。

注2:博文会不定期更新,敬请关注。

一、QChildEvent的发送

分析QObject::setParent代码,当修改父对象时,便会旧parent发送QEvent::ChildRemoved事件;向新parent发送QEvent::ChildAdded事件。

/*!
    Makes the object a child of \a parent.

    \sa parent(), children()
*/
void QObject::setParent(QObject *parent)
{
    Q_D(QObject);
    Q_ASSERT(!d->isWidget);
    d->setParent_helper(parent);
}
void QObjectPrivate::setParent_helper(QObject *o)
{
    Q_Q(QObject);
    Q_ASSERT_X(q != o, Q_FUNC_INFO, "Cannot parent a QObject to itself");
#ifdef QT_DEBUG
    const auto checkForParentChildLoops = qScopeGuard([&](){
        int depth = 0;
        auto p = parent;
        while (p) {
            if (++depth == CheckForParentChildLoopsWarnDepth) {
                qWarning("QObject %p (class: '%s', object name: '%s') may have a loop in its parent-child chain; "
                         "this is undefined behavior",
                         q, q->metaObject()->className(), qPrintable(q->objectName()));
            }
            p = p->parent();
        }
    });
#endif

    if (o == parent)
        return;

    if (parent) {
        QObjectPrivate *parentD = parent->d_func();
        if (parentD->isDeletingChildren && wasDeleted
            && parentD->currentChildBeingDeleted == q) {
            // don't do anything since QObjectPrivate::deleteChildren() already
            // cleared our entry in parentD->children.
        } else {
            const int index = parentD->children.indexOf(q);
            if (parentD->isDeletingChildren) {
                parentD->children[index] = 0;
            } else {
                parentD->children.removeAt(index);
                if (sendChildEvents && parentD->receiveChildEvents) {
                    QChildEvent e(QEvent::ChildRemoved, q);
                    QCoreApplication::sendEvent(parent, &e);
                }
            }
        }
    }
    parent = o;
    if (parent) {
        // object hierarchies are constrained to a single thread
        if (threadData != parent->d_func()->threadData) {
            qWarning("QObject::setParent: Cannot set parent, new parent is in a different thread");
            parent = 0;
            return;
        }
        parent->d_func()->children.append(q);
        if(sendChildEvents && parent->d_func()->receiveChildEvents) {
            if (!isWidget) {
                QChildEvent e(QEvent::ChildAdded, q);
                QCoreApplication::sendEvent(parent, &e);
            }
        }
    }
    if (!wasDeleted && !isDeletingChildren && declarativeData && QAbstractDeclarativeData::parentChanged)
        QAbstractDeclarativeData::parentChanged(declarativeData, q, o);
}

因为QObject构造函数可以设置parent,此时发送QChildEvent事件,但对象并不一定创建完毕。

Refs. from QObject::childEvent(QChildEvent *)

QEvent::ChildAdded and QEvent::ChildRemoved events are sent to objects when children are added or removed. In both cases you can only rely on the child being a QObject, or if isWidgetType() returns true, a QWidget. (This is because, in the ChildAdded case, the child is not yet fully constructed, and in the ChildRemoved case it might have been destructed already).

二、应用案例

在SALOME中,SUIT_Desktop通过响应QChildEvent消息来讲SUIT_ViewWindow添加到SUIT_Desktop中。

class SUIT_Desktop::ReparentEvent : public QEvent
{
public:
  ReparentEvent( Type t, QObject* obj ) : QEvent( t ), myObj( obj ) {};

  QObject* object() const { return myObj; }

private:
  QPointer<QObject> myObj;
};
/*!
  Child event.
*/
void SUIT_Desktop::childEvent( QChildEvent* e )
{
  if ( e->type() == QEvent::ChildAdded && e->child()->isWidgetType() ) {
    // The following line is a workaround to avoid showing view window as a top-level window
    // before re-parenting it to workstack (issue #23467).
    // See SUIT_ViewWindow::setVisible() and SUIT_Desktop::customEvent().
    e->child()->setProperty("blockShow", true );
    QApplication::postEvent( this, new ReparentEvent( QEvent::Type( Reparent ), e->child() ) );
  }
  else {
    QtxMainWindow::childEvent( e );
  }
}

void SUIT_Desktop::customEvent( QEvent* e )
{
  if ( (int)e->type() != Reparent )
    return;

  ReparentEvent* re = (ReparentEvent*)e;
  SUIT_ViewWindow* wid = ::qobject_cast<SUIT_ViewWindow*>( re->object() );
  if ( wid )
  {
    bool invis = wid->testAttribute( Qt::WA_WState_ExplicitShowHide ) &&
                 wid->testAttribute( Qt::WA_WState_Hidden );

    // The following line is a workaround to avoid showing view window as a top-level window
    // before re-parenting it to workstack (issue #23467).
    // See SUIT_ViewWindow::setVisible() and SUIT_Desktop::childEvent().
    wid->setProperty("blockShow", false);
    addWindow( wid );
    wid->setVisible( !invis );
  }
}

Refs. from QObject::customEvent(QEvent *event) 

This event handler can be reimplemented in a subclass to receive custom events. Custom events are user-defined events with a type value at least as large as the QEvent::User item of the QEvent::Type enum, and is typically a QEvent subclass. The event is passed in the event parameter. 

/*! Constructor.*/
SUIT_ViewWindow::SUIT_ViewWindow( SUIT_Desktop* theDesktop )
  : QMainWindow( theDesktop ), myManager( 0 ), myIsDropDown( true ), mySyncAction( 0 )
{
  myDesktop = theDesktop;

  setWindowIcon( myDesktop ? myDesktop->windowIcon() : QApplication::windowIcon() );

  setAttribute( Qt::WA_DeleteOnClose );

  myToolMgr = new QtxActionToolMgr( this );

  setProperty( "VectorsMode", false );
}

网络

QChildEventhttps://doc.qt.io/qt-6/qchildevent.html

QObject::childEvent(QChildEvent* event)https://doc.qt.io/qt-6/qobject.html#childEvent

QObject::customEvent(QEvent *event)https://doc.qt.io/qt-6/qobject.html#customEvent

SALOME源码分析:GUI模块https://blog.csdn.net/qq_26221775/article/details/127759145?spm=1001.2014.3001.5502

SALOME源码分析: GUI模块主要组件https://blog.csdn.net/qq_26221775/article/details/148845050?spm=1001.2014.3001.5502


网站公告

今日签到

点亮在社区的每一天
去签到