Qt show背后发生的一些事

来源:互联网 发布:java 视频断点上传 编辑:程序博客网 时间:2024/06/07 11:13

Qt show背后发生的一些事

  • Qt show背后发生的一些事
    • 分析
    • test1
    • test2
    • test3
    • references

分析

一个窗口要在操作系统中显示出来,必然要调用操作系统提供的接口。例如在window平台上编写界面程序时,程序员需要设计窗口类,向操作系统注册窗口类,创建窗口句柄,显示窗口1

show并没有做什么:

void QWidget::show()    {        Qt::WindowState defaultState =           QGuiApplicationPrivate::platformIntegration()->defaultWindowState(          data->window_flags);        if (defaultState == Qt::WindowFullScreen)            showFullScreen();        else if (defaultState == Qt::WindowMaximized)            showMaximized();        else            setVisible(true); // FIXME: Why not showNormal(), like QWindow::show()?    }

真正创建窗口句柄的任务在setVisible中被调用:

void QWidget::setVisible(bool visible){        if (visible) { // show            if (testAttribute(Qt::WA_WState_ExplicitShowHide) &&                                !testAttribute(Qt::WA_WState_Hidden))                return;            Q_D(QWidget);          /*          如果该widget的句柄还未被创建,且该窗口是一个独立窗口或者是父窗口          已经创建了窗口句柄的一个非独立窗口,那么就创建一个窗口句柄(对应win32的实现为          注册窗口类和回调过程,创建窗口)。          */            //create toplevels but not children of non-visible parents            QWidget *pw = parentWidget();            if (!testAttribute(Qt::WA_WState_Created)                && (isWindow() || pw->testAttribute(Qt::WA_WState_Created))) {                create();   //调用它创建窗口句柄            }          ...            /*            如果该widget是一个独立窗口,或者是一个父窗口可见的非独立窗口,就调用            QWidgetPrivate::show_helper()==》showChildren()递归地将它“当前”的子窗口            都显示出来。            */            if (isWindow() || parentWidget()->isVisible()) {                    d->show_helper();                    ...            }          ...}

就如注释上说的,你直接或间接的调用了setVisible,如果窗口句柄还没有被创建,那就会通过create创建,后面的代码还将递归的创建这个窗口当前的所有子窗口,这就是父窗口一次show就可以将它当前所有子窗口显示出来的原因。

接下来瞧瞧create:

void QWidget::create(WId window, bool initializeWindow, bool destroyOldWindow){        Q_D(QWidget);        if (Q_UNLIKELY(window))        qWarning("QWidget::create(): Parameter 'window' does not have any effect.");       /*        防止重复创建        */        if (testAttribute(Qt::WA_WState_Created) && window == 0 && internalWinId())            return;      ...        //设置已创建标记         setAttribute(Qt::WA_WState_Created);   // set created flag        //调用平台相关的实现,创建窗口         d->create_sys(window, initializeWindow, destroyOldWindow);      ... }     

最终的创建肯定是和平台密切相关的,这一点无法逃避,下面是window平台的实现2:

void QWidgetPrivate::create_sys(WId window,                                 bool initializeWindow,                                bool destroyOldWindow) {   ...    //注册窗口类    QString windowClassName = qt_reg_winclass(q);   ...    //创建窗口 WinAPI   id = CreateWindowEx(exsty,                        reinterpret_cast<const wchar_t *>(windowClassName.utf16()),                       reinterpret_cast<const wchar_t *>(title.utf16()), style,                       x, y, w, h,                       parentw, NULL, appinst, NULL);   ...      //显示窗口 WinAPI    ShowWindow(q->internalWinId(), SW_SHOW);   ... }
const QString qt_reg_winclass(QWidget *w)        // register window class {    ...        WNDCLASS wc;        wc.style        = style;        wc.lpfnWndProc  = (WNDPROC)QtWndProc; //窗口过程        wc.cbClsExtra   = 0;        wc.cbWndExtra   = 0;        wc.hInstance    = qWinAppInst();        wc.hCursor      = 0;        HBRUSH brush = 0;        if (w && !qt_widget_private(w)->isGLWidget)            brush = (HBRUSH)GetSysColorBrush(COLOR_WINDOW);        wc.hbrBackground = brush;        wc.lpszMenuName  = 0;        wc.lpszClassName = (wchar_t*)cname.utf16();        ATOM atom = RegisterClass(&wc); //注册窗口类 WinAPI    ...    return cname; }

接下来是一些小实验:

test1

#include "myclass.h"#include "childwin.h"#include "childwin1.h"#include <QtWidgets/QApplication>int main(int argc, char *argv[]){    QApplication a(argc, argv);    MyClass w;    childWin cw(&w);    childWin1 cw1(&w);    w.setVisible(true);    return a.exec();}

上面的cw和cw1是w的子窗口,cw和cw1都是继承自QWidget的窗口类并且各自都有一个QLabel的子窗口。代码只对w调用了setVisible,在此之前cw和cw1已在w的孩子窗口链表中且w还未显示出来。按照前面的分析,第一次调用w.setVisible时,他除了将自己的窗口句柄创建并显示出来之外,还会递归将他的所有孩子窗口句柄创建出来并显示。这意味着它会显示,它的孩子会显示,它的孩子的孩子也会显示…实验证实了这个推理:


qt-show-001

test2

int main(int argc, char *argv[]){    QApplication a(argc, argv);    MyClass w;    childWin cw(&w);    w.setVisible(true);    childWin1 cw1(&w);    return a.exec();}

和test1比,test2做了一点小调整,我将cw1对象的创建(注意是对象,不是窗口句柄)放在了w.setVisible之后。这样的调整会带来什么变化呢?哦,那就是w和cw以及cw的所有孩子窗口都被创建和显示但是cw1以及他的孩子窗口都不会被创建和显示。为什么了?因为在w.setVisible调用时,cw1还不在w的孩子链表中。所以递归创建窗口的过程不会涉及cw1。实验结果如下:


qt-show-002

test3

int main(int argc, char *argv[]){    QApplication a(argc, argv);    MyClass w;    childWin cw(&w);    //w.setVisible(true);    childWin1 cw1(&w);    cw.show();    cw1.show();    return a.exec();}

这次,我将w.setVisible注释掉了,然后对cw和cw1分别调用show,期望他们能显示出来。下面运行,结果肯定让你大吃一惊,没有任何窗口显示出来!怎么会这样,代码里明明白白的调用了show,这还能有假吗?

代码不会欺骗人,直觉在欺骗人。由于w是cw和cw1的父窗口,但是作为父窗口的w都还没有创建和显示出来,子窗口怎么会创建和显示出来呢!

void QWidget::setVisible(bool visible){ ...          /*          如果该widget的句柄还未被创建,且该窗口是一个独立窗口或者是父窗口          已经创建了窗口句柄的一个非独立窗口,那么就创建一个窗口句柄(对应win32的实现为          注册窗口类和回调过程,创建窗口)。          */            //create toplevels but not children of non-visible parents            QWidget *pw = parentWidget();            if (!testAttribute(Qt::WA_WState_Created)                && (isWindow() || pw->testAttribute(Qt::WA_WState_Created))) {                create();   //调用它创建窗口句柄            }...}

references


  1. 跟我一起玩Win32开发(2):完整的开发流程 ↩
  2. qwidget_win.cpp ↩
0 0
原创粉丝点击