一个窗口要在操作系统中显示出来,必然要调用操作系统提供的接口。例如在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; }接下来是一些小实验:
上面的cw和cw1是w的子窗口,cw和cw1都是继承自QWidget的窗口类并且各自都有一个QLabel的子窗口。代码只对w调用了setVisible,在此之前cw和cw1已在w的孩子窗口链表中且w还未显示出来。按照前面的分析,第一次调用w.setVisible时,他除了将自己的窗口句柄创建并显示出来之外,还会递归将他的所有孩子窗口句柄创建出来并显示。这意味着它会显示,它的孩子会显示,它的孩子的孩子也会显示…实验证实了这个推理:
和test1比,test2做了一点小调整,我将cw1对象的创建(注意是对象,不是窗口句柄)放在了w.setVisible之后。这样的调整会带来什么变化呢?哦,那就是w和cw以及cw的所有孩子窗口都被创建和显示但是cw1以及他的孩子窗口都不会被创建和显示。为什么了?因为在w.setVisible调用时,cw1还不在w的孩子链表中。所以递归创建窗口的过程不会涉及cw1。实验结果如下:
这次,我将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(); //调用它创建窗口句柄 } ... }