最近在使用QTabWidget控件的时候发现,QTabWidget控件的addTab(pWidget, "myTab")方法会改变pWidget控件原有父子关系,因为这个问题,害我找了半天,我本意是想通过pWidget->parent()获取pWidget的父指针,但是由于addTab会改变pWidget的原来的父子关系,导致找不到原来的父指针,下面我们来看看addTab是怎么来改变这个父指针的:
首先看一段简单的应用代码:
#ifndef _INFO_H
#define _INFO_H
#include <QtGui>
class CInfo1 : public QWidget {
public:
CInfo1(QWidget *parent) : QWidget(parent)
{
}
void getResult();
};
class CInfo2 : public QWidget {
public:
CInfo2(QWidget *parent) : QWidget(parent)
{
}
void getResult();
};
class CInfo : public QTabWidget {
public:
CInfo(QWidget *parent = NULL) : QTabWidget(parent)
{
pInfo1 = new CInfo1(this);
pInfo2 = new CInfo2(this);
addTab(pInfo1, "info1");
addTab(pInfo2, "info2");
a = 1;
b = 2;
qDebug() << a << b;
}
CInfo1 *pInfo1;
CInfo2 *pInfo2;
int a;
int b;
};
#endif
#include "info.h"
void CInfo1::getResult()
{
CInfo *pInfo = (CInfo *)parent();
qDebug() << pInfo->a << pInfo->b;
}
void CInfo2::getResult()
{
CInfo *pInfo = (CInfo *)parent();
qDebug() << pInfo->a << pInfo->b;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
CInfo info;
info.pInfo1->getResult();
info.pInfo2->getResult();
info.show();
return a.exec();
}
从上面的代码若父指针没有更改,则会打印出三行:
1 2
1 2
1 2
而事实的打印却是:
1 2
X X
X X
X是一个随机数,从而我们不难发现,getResult()函数里的parent()已不是指向info对象了,接下来我们看看到底addTab是怎么改变父指针的,看一下调用关系:
addTab(pInfo1, "info1");
int QTabWidget::addTab(QWidget *child, const QString &label)
{
return insertTab(-1, child, label);
}
int QTabWidget::insertTab(int index, QWidget *w, const QIcon& icon, const QString &label)
{
Q_D(QTabWidget);
if(!w)
return -1;
index = d->stack->insertWidget(index, w);
d->tabs->insertTab(index, icon, label);
setUpLayout();
tabInserted(index);
return index;
}
int QStackedWidget::insertWidget(int index, QWidget *widget)
{
return d_func()->layout->insertWidget(index, widget);
}
int QStackedLayout::insertWidget(int index, QWidget *widget)
{
Q_D(QStackedLayout);
addChildWidget(widget);
index = qMin(index, d->list.count());
if (index < 0)
index = d->list.count();
QWidgetItem *wi = QLayoutPrivate::createWidgetItem(this, widget);
d->list.insert(index, wi);
invalidate();
if (d->index < 0) {
setCurrentIndex(index);
} else {
if (index <= d->index)
++d->index;
if (d->stackingMode == StackOne)
widget->hide();
widget->lower();
}
return index;
}
void QLayout::addChildWidget(QWidget *w)
{
QWidget *mw = parentWidget();
QWidget *pw = w->parentWidget();
//Qt::WA_LaidOut is never reset. It only means that the widget at some point has
//been in a layout.
if (pw && w->testAttribute(Qt::WA_LaidOut)) {
QLayout *l = pw->layout();
if (l && removeWidgetRecursively(l, w)) {
#ifdef QT_DEBUG
if (layoutDebug())
qWarning("QLayout::addChildWidget: %s \"%s\" is already in a layout; moved to new layout",
w->metaObject()->className(), w->objectName().toLocal8Bit().data());
#endif
}
}
if (pw && mw && pw != mw) {
#ifdef QT_DEBUG
if (layoutDebug())
qWarning("QLayout::addChildWidget: %s \"%s\" in wrong parent; moved to correct parent",
w->metaObject()->className(), w->objectName().toLocal8Bit().data());
#endif
pw = 0;
}
bool needShow = mw && mw->isVisible() && !(w->isHidden() && w->testAttribute(Qt::WA_WState_ExplicitShowHide));
if (!pw && mw)
w->setParent(mw);
w->setAttribute(Qt::WA_LaidOut);
if (needShow)
QMetaObject::invokeMethod(w, "_q_showIfNotHidden", Qt::QueuedConnection); //show later
}
从调用栈(注意红色代码)发现,最终addTab会调用addChildWidget,在w->setParent会将pWidget的父指针更改,哈哈,终于水落石出了。