本文记录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