Qt show背后发生的一些事

Posted unclerunning

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Qt show背后发生的一些事相关的知识,希望对你有一定的参考价值。

Qt show背后发生的一些事

分析

一个窗口要在操作系统中显示出来,必然要调用操作系统提供的接口。例如在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时,他除了将自己的窗口句柄创建并显示出来之外,还会递归将他的所有孩子窗口句柄创建出来并显示。这意味着它会显示,它的孩子会显示,它的孩子的孩子也会显示…实验证实了这个推理:



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。实验结果如下:



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

以上是关于Qt show背后发生的一些事的主要内容,如果未能解决你的问题,请参考以下文章

Qt 中的事件是如何工作的?

端掉了一个色情网站!真绝!

工作中上的一点思考

云原生不仅颠覆了技术栈,背后的每个岗位也在悄然发生改变

写几行代码,了解响应式原理

马斯克雷军竞速「机器人」背后,一场机器革命正在发生 | 幂集创新