Qt 之 WindowFlags 引發的有趣問題一則

pamxy發表於2013-06-30

轉自:http://blog.csdn.net/dbzhang800/article/details/6680563

來源

來源於 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;
mw.setParent(&dw);

只出現一個視窗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();
}

參考


相關文章