来源
来源于 fly542 的一篇blog中描述的一个问题:将一个QMainWindow 作为 QDockWidget的内容widget时,QMainWindow 会作为顶级窗口弹出。
本着精简的原则,我们提供下面可重现问题的例子:
#include <QtGui/QApplication>
#include <QtGui/QMainWindow>
#include <QtGui/QDockWidget>
#define CASE1
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QDockWidget dw;
#ifdef CASE1
QMainWindow mw(&dw);
#elif defined CASE2
QMainWindow mw;
mw.setParent(&dw);
#else
QMainWindow mw;
#endif
dw.setWidget(&mw)
dw.show();
return app.exec();
}
编译预处理部分的两段代码会产生什么不同的结果呢??
| QMainWindow mw(&dw); | 会出现两个窗口,一个是dw,一个是mw |
| QMainWindow mw; | 只出现一个窗口dw,而mw是dw的子widget |
| QMainWindow mw; | 同上 |
原因何在?
其实,在这之前,我一直认为上面的CASE1和CASE2代码是等价的。但现在,显然不能再这样认为了。
之所以会这样,是因为两种情况下,WindowFlags 标记不同!!前者之所以会弹出,是因为mw是顶级窗口(设置有 Qt::Window 标记位),后者则清除了该标记位。
源码
- QMainWindow 构造时会设置 Qt::Window 标记位!
QMainWindow::QMainWindow(QWidget *parent, Qt::WindowFlags flags)
: QWidget(*(new QMainWindowPrivate()), parent, flags | Qt::Window)
{
d_func()->init();
}
- QWidget::setParent 会清除 Qt::Window 标记位!
void QWidget::setParent(QWidget *parent)
{
if (parent == parentWidget())
return;
setParent((QWidget*)parent, windowFlags() & ~Qt::WindowType_Mask);
}
- QDockWidget如果发现被设置的widget不是自己的子对象,则也辗转调用 QWidget::setParent,进而清除Qt::Window 标记
- 其实是QLayout 提供的功能
void QDockWidget::setWidget(QWidget *widget)
{
QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout());
layout->setWidgetForRole(QDockWidgetLayout::Content, widget);
}
void QLayout::addChildWidget(QWidget *w)
{
QWidget *mw = parentWidget();
QWidget *pw = w->parentWidget();
...
if (!pw && mw)
w->setParent(mw);
...
}
疑问??
去掉 dw.show() 后,CASE1 其实仍然会弹出mw窗口。原因何在呢?
答案在:QDockWidget::setWidget 调用的函数:
void QDockWidgetLayout::setWidgetForRole(Role r, QWidget *w)
{
...
if (w != 0) {
addChildWidget(w);
item_list[r] = new QWidgetItemV2(w);
w->show();
会设置 w 可见!!
验证
贴出另一个例子:感兴趣可以自己来做些测试。该说的前面都说了,就不解释了
#include <QtGui/QApplication>
#include <QtGui/QMainWindow>
#include <QtGui/QDockWidget>
#define CASE3
class MainWindow:public QMainWindow
{
public:
MainWindow()
{
QDockWidget * dock1 = new QDockWidget(this);
#ifdef CASE1
QWidget * w = new QMainWindow(dock1);
#elif defined CASE2
QWidget * w = new QMainWindow;
#elif defined CASE3
QWidget * w = new QWidget(dock1);
w->setWindowFlags(w->windowFlags()|Qt::Window);
#endif
w->setStyleSheet("background-color: rgb(0, 255, 255);");
dock1->setWidget(w);
// dock1->hide();
this->addDockWidget(Qt::LeftDockWidgetArea, dock1);
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow w;
w.show();
return app.exec();
}
本文详细解析了在使用Qt库时,将QMainWindow作为QDockWidget的内容widget时,不同构造方式导致的不同窗口行为。通过分析QMainWindow、QDockWidget及QLayout的相关源码,揭示了QMainWindow构造时默认设置的Qt::Window标志位及其在父子关系建立过程中的作用。同时,展示了通过QDockWidget的setWidget方法调整widget可见性的机制。
401

被折叠的 条评论
为什么被折叠?



