QT分析之QPushButton的初始化
原文地址:http://blog.163.com/net_worm/blog/static/127702419201001003326522/
在简单的QT程序的第二行,声明了一个QPushButton的对象。先简单看看其初始化过程。
QPushButton的类继承关系为:
1 QPushButton :public QAbstractButton :pubic QWidget :public QObject, public QPaintDevice
QPushButton的构造:
1 QPushButton::QPushButton(const QString &text, QWidget *parent) 2 : QAbstractButton(*new QPushButtonPrivate, parent) 3 { 4 Q_D(QPushButton); // 声明并获得QPushButtonPrivate函数指针d 5 setText(text); // 设置按钮的名字 6 d->init(); // 调用QPushButtonPrivate::init(),其实只是重新设定排布间隔 7 }
新生成的QPushButtonPrivate对象传递给QAbstractButton之后,发生了什么事呢?
1 QAbstractButton::QAbstractButton(QAbstractButtonPrivate &dd, QWidget *parent) 2 : QWidget(dd, parent, 0) 3 { 4 Q_D(QAbstractButton); // 声明并获得QAbstractButtonPrivate函数指针d 5 d->init(); // 调用QAbstractButtonPrivate::init() 6 }
QAbstractButtonPrivate::init()做了什么呢?其实只是调用了QPushButton的几个设定函数。
继续看QWidget的初始化过程。
1 QWidget::QWidget(QWidgetPrivate &dd, QWidget* parent, Qt::WindowFlags f) 2 : QObject(dd, 0), QPaintDevice() 3 { 4 d_func()->init(parent, f); 5 }
其中d_func()是宏定义Q_DECLARE_PRIVATE(QWidget)中定义的,获取QWidgetPrivate指针的函数。有点奇怪的是,这里怎么没有用Q_D宏定义,与之前的风格有点不同。
QWidgetPrivate::init()里做了什么动作呢?(关键语句用颜色标记)
1 void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) 2 { 3 Q_Q(QWidget); 4 if (qApp->type() == QApplication::Tty) 5 qFatal("QWidget: Cannot create a QWidget when no GUI is being used"); 6 7 Q_ASSERT(uncreatedWidgets); 8 uncreatedWidgets->insert(q); 9 10 QWidget *desktopWidget = 0; 11 if (parentWidget && parentWidget->windowType() == Qt::Desktop) { 12 desktopWidget = parentWidget; 13 parentWidget = 0; 14 } 15 16 q->data = &data; 17 18 if (!q->parent()) { 19 Q_ASSERT_X(q->thread() == qApp->thread(), "QWidget", 20 "Widgets must be created in the GUI thread."); 21 } 22 23 data.fstrut_dirty = true; 24 25 data.winid = 0; 26 data.widget_attributes = 0; 27 data.window_flags = f; 28 data.window_state = 0; 29 data.focus_policy = 0; 30 data.context_menu_policy = Qt::DefaultContextMenu; 31 data.window_modality = Qt::NonModal; 32 33 data.sizehint_forced = 0; 34 data.is_closing = 0; 35 data.in_show = 0; 36 data.in_set_window_state = 0; 37 data.in_destructor = false; 38 39 // Widgets with Qt::MSWindowsOwnDC (typically QGLWidget) must have a window handle. 40 if (f & Qt::MSWindowsOwnDC) 41 q->setAttribute(Qt::WA_NativeWindow); 42 43 q->setAttribute(Qt::WA_QuitOnClose); // might be cleared in adjustQuitOnCloseAttribute() 44 adjustQuitOnCloseAttribute(); 45 46 q->setAttribute(Qt::WA_WState_Hidden); 47 48 //give potential windows a bigger "pre-initial" size; create_sys() will give them a new size later 49 data.crect = parentWidget ? QRect(0,0,100,30) : QRect(0,0,640,480); 50 51 focus_next = focus_prev = q; 52 53 if ((f & Qt::WindowType_Mask) == Qt::Desktop) 54 q->create(); // 调用了QWidget::create() 55 else if (parentWidget) 56 q->setParent(parentWidget, data.window_flags); 57 else { 58 adjustFlags(data.window_flags, q); 59 resolveLayoutDirection(); 60 // opaque system background? 61 const QBrush &background = q->palette().brush(QPalette::Window); 62 setOpaque(q->isWindow() && background.style() != Qt::NoBrush && background.isOpaque()); 63 } 64 data.fnt = QFont(data.fnt, q); 65 66 q->setAttribute(Qt::WA_PendingMoveEvent); 67 q->setAttribute(Qt::WA_PendingResizeEvent); 68 69 if (++QWidgetPrivate::instanceCounter > QWidgetPrivate::maxInstances) 70 QWidgetPrivate::maxInstances = QWidgetPrivate::instanceCounter; 71 72 if (QApplicationPrivate::app_compile_version < 0x040200 73 || QApplicationPrivate::testAttribute(Qt::AA_ImmediateWidgetCreation)) 74 q->create(); 75 76 // 下面的三行,产生并发送了Create事件 77 QEvent e(QEvent::Create); 78 QApplication::sendEvent(q, &e); 79 QApplication::postEvent(q, new QEvent(QEvent::PolishRequest)); 80 81 extraPaintEngine = 0; 82 }
看看QWidget::create()的实现:
1 void QWidget::create(WId window, bool initializeWindow, bool destroyOldWindow) 2 { 3 Q_D(QWidget); 4 if (testAttribute(Qt::WA_WState_Created) && window == 0 && internalWinId()) 5 return; 6 7 if (d->data.in_destructor) 8 return; 9 10 Qt::WindowType type = windowType(); 11 Qt::WindowFlags &flags = data->window_flags; 12 13 if ((type == Qt::Widget || type == Qt::SubWindow) && !parentWidget()) { 14 type = Qt::Window; 15 flags |= Qt::Window; 16 } 17 18 if (QWidget *parent = parentWidget()) { 19 if (type & Qt::Window) { 20 if (!parent->testAttribute(Qt::WA_WState_Created)) 21 parent->createWinId(); 22 } else if (testAttribute(Qt::WA_NativeWindow) && !parent->internalWinId() 23 && !testAttribute(Qt::WA_DontCreateNativeAncestors)) { 24 // We're about to create a native child widget that doesn't have a native parent; 25 // enforce a native handle for the parent unless the Qt::WA_DontCreateNativeAncestors 26 // attribute is set. 27 d->createWinId(window); 28 // Nothing more to do. 29 Q_ASSERT(testAttribute(Qt::WA_WState_Created)); 30 Q_ASSERT(internalWinId()); 31 return; 32 } 33 } 34 35 static int paintOnScreenEnv = -1; 36 if (paintOnScreenEnv == -1) 37 paintOnScreenEnv = qgetenv("QT_ONSCREEN_PAINT").toInt() > 0 ? 1 : 0; 38 if (paintOnScreenEnv == 1) 39 setAttribute(Qt::WA_PaintOnScreen); 40 41 if (QApplicationPrivate::testAttribute(Qt::AA_NativeWindows)) 42 setAttribute(Qt::WA_NativeWindow); 43 44 #ifdef ALIEN_DEBUG 45 qDebug() << "QWidget::create:" << this << "parent:" << parentWidget() 46 << "Alien?" << !testAttribute(Qt::WA_NativeWindow); 47 #endif 48 49 // Unregister the dropsite (if already registered) before we 50 // re-create the widget with a native window. 51 if (testAttribute(Qt::WA_WState_Created) && !internalWinId() && testAttribute(Qt::WA_NativeWindow) 52 && d->extra && d->extra->dropTarget) { 53 d->registerDropSite(false); 54 } 55 56 d->updateIsOpaque(); 57 58 setAttribute(Qt::WA_WState_Created); // set created flag 59 d->create_sys(window, initializeWindow, destroyOldWindow); 60 61 // a real toplevel window needs a backing store 62 if (isWindow()) { 63 delete d->topData()->backingStore; 64 // QWidgetBackingStore will check this variable, hence it must be 0 65 d->topData()->backingStore = 0; 66 if (hasBackingStoreSupport()) 67 d->topData()->backingStore = new QWidgetBackingStore(this); 68 } 69 70 d->setModal_sys(); 71 72 if (!isWindow() && parentWidget() && parentWidget()->testAttribute(Qt::WA_DropSiteRegistered)) 73 setAttribute(Qt::WA_DropSiteRegistered, true); 74 75 // need to force the resting of the icon after changing parents 76 if (testAttribute(Qt::WA_SetWindowIcon)) 77 d->setWindowIcon_sys(true); 78 if (isWindow() && !d->topData()->iconText.isEmpty()) 79 d->setWindowIconText_helper(d->topData()->iconText); 80 if (windowType() != Qt::Desktop) { 81 d->updateSystemBackground(); 82 83 if (isWindow() && !testAttribute(Qt::WA_SetWindowIcon)) 84 d->setWindowIcon_sys(); 85 } 86 }
这里QWidgetPrivate::create_sys()定义在QWidget_win.cpp里。
1 void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyOldWindow) 2 { 3 Q_Q(QWidget); 4 static int sw = -1, sh = -1; 5 6 Qt::WindowType type = q->windowType(); 7 Qt::WindowFlags flags = data.window_flags; 8 9 bool topLevel = (flags & Qt::Window); 10 bool popup = (type == Qt::Popup); 11 bool dialog = (type == Qt::Dialog 12 || type == Qt::Sheet 13 || (flags & Qt::MSWindowsFixedSizeDialogHint)); 14 bool desktop = (type == Qt::Desktop); 15 bool tool = (type == Qt::Tool || type == Qt::Drawer); 16 17 HINSTANCE appinst = qWinAppInst(); 18 HWND parentw, destroyw = 0; 19 WId id; 20 21 QString windowClassName = qt_reg_winclass(q); 22 23 if (!window) // always initialize 24 initializeWindow = true; 25 26 if (popup) 27 flags |= Qt::WindowStaysOnTopHint; // a popup stays on top 28 29 if (sw < 0) { // get the (primary) screen size 30 sw = GetSystemMetrics(SM_CXSCREEN); 31 sh = GetSystemMetrics(SM_CYSCREEN); 32 } 33 34 if (desktop && !q->testAttribute(Qt::WA_DontShowOnScreen)) { // desktop widget 35 popup = false; // force this flags off 36 if (QSysInfo::WindowsVersion != QSysInfo::WV_NT && QSysInfo::WindowsVersion != QSysInfo::WV_95) 37 data.crect.setRect(GetSystemMetrics(76 /* SM_XVIRTUALSCREEN */), GetSystemMetrics(77 /* SM_YVIRTUALSCREEN */), 38 GetSystemMetrics(78 /* SM_CXVIRTUALSCREEN */), GetSystemMetrics(79 /* SM_CYVIRTUALSCREEN */)); 39 else 40 data.crect.setRect(0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)); 41 } 42 43 parentw = q->parentWidget() ? q->parentWidget()->effectiveWinId() : 0; 44 45 #ifdef UNICODE 46 QString title; 47 const TCHAR *ttitle = 0; 48 #endif 49 QByteArray title95; 50 int style = WS_CHILD; 51 int exsty = 0; 52 53 if (window) { 54 style = GetWindowLongA(window, GWL_STYLE); 55 if (!style) 56 qErrnoWarning("QWidget::create: GetWindowLong failed"); 57 topLevel = false; // #### needed for some IE plugins?? 58 } else if (popup || (type == Qt::ToolTip) || (type == Qt::SplashScreen)) { 59 style = WS_POPUP; 60 } else if (topLevel && !desktop) { 61 if (flags & Qt::FramelessWindowHint) 62 style = WS_POPUP; // no border 63 else if (flags & Qt::WindowTitleHint) 64 style = WS_OVERLAPPED; 65 else 66 style = 0; 67 } 68 if (!desktop) { 69 // if (!testAttribute(Qt::WA_PaintUnclipped)) 70 // ### Commented out for now as it causes some problems, but 71 // this should be correct anyway, so dig some more into this 72 #ifndef Q_FLATTEN_EXPOSE 73 style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN ; 74 #endif 75 if (topLevel) { 76 if ((type == Qt::Window || dialog || tool)) { 77 if (!(flags & Qt::FramelessWindowHint)) { 78 if (!(flags & Qt::MSWindowsFixedSizeDialogHint)) { 79 style |= WS_THICKFRAME; 80 if(!(flags & 81 ( Qt::WindowSystemMenuHint 82 | Qt::WindowTitleHint 83 | Qt::WindowMinMaxButtonsHint 84 | Qt::WindowCloseButtonHint 85 | Qt::WindowContextHelpButtonHint))) 86 style |= WS_POPUP; 87 } else { 88 style |= WS_POPUP | WS_DLGFRAME; 89 } 90 } 91 if (flags & Qt::WindowTitleHint) 92 style |= WS_CAPTION; 93 if (flags & Qt::WindowSystemMenuHint) 94 style |= WS_SYSMENU; 95 if (flags & Qt::WindowMinimizeButtonHint) 96 style |= WS_MINIMIZEBOX; 97 if (shouldShowMaximizeButton()) 98 style |= WS_MAXIMIZEBOX; 99 if (tool) 100 exsty |= WS_EX_TOOLWINDOW; 101 if (flags & Qt::WindowContextHelpButtonHint) 102 exsty |= WS_EX_CONTEXTHELP; 103 } else { 104 exsty |= WS_EX_TOOLWINDOW; 105 } 106 } 107 } 108 109 if (flags & Qt::WindowTitleHint) { 110 QT_WA({ 111 title = q->isWindow() ? qAppName() : q->objectName(); 112 ttitle = (TCHAR*)title.utf16(); 113 } , { 114 title95 = q->isWindow() ? qAppName().toLocal8Bit() : q->objectName().toLatin1(); 115 }); 116 } 117 118 // The Qt::WA_WState_Created flag is checked by translateConfigEvent() in 119 // qapplication_win.cpp. We switch it off temporarily to avoid move 120 // and resize events during creationt 121 q->setAttribute(Qt::WA_WState_Created, false); 122 123 if (window) { // override the old window 124 if (destroyOldWindow) 125 destroyw = data.winid; 126 id = window; 127 setWinId(window); 128 LONG res = SetWindowLongA(window, GWL_STYLE, style); 129 if (!res) 130 qErrnoWarning("QWidget::create: Failed to set window style"); 131 #ifdef _WIN64 132 res = SetWindowLongPtrA( window, GWLP_WNDPROC, (LONG_PTR)QtWndProc ); 133 #else 134 res = SetWindowLongA( window, GWL_WNDPROC, (LONG)QtWndProc ); 135 #endif 136 if (!res) 137 qErrnoWarning("QWidget::create: Failed to set window procedure"); 138 } else if (desktop) { // desktop widget 139 id = GetDesktopWindow(); 140 // QWidget *otherDesktop = QWidget::find(id); // is there another desktop? 141 // if (otherDesktop && otherDesktop->testWFlags(Qt::WPaintDesktop)) { 142 // otherDesktop->d_func()->setWinId(0); // remove id from widget mapper 143 // d->setWinId(id); // make sure otherDesktop is 144 // otherDesktop->d_func()->setWinId(id); // found first 145 // } else { 146 setWinId(id); 147 // } 148 } else if (topLevel) { // create top-level widget 149 if (popup) 150 parentw = 0; 151 152 const bool wasMoved = q->testAttribute(Qt::WA_Moved); 153 int x = wasMoved ? data.crect.left() : CW_USEDEFAULT; 154 int y = wasMoved ? data.crect.top() : CW_USEDEFAULT; 155 int w = CW_USEDEFAULT; 156 int h = CW_USEDEFAULT; 157 158 // Adjust for framestrut when needed 159 RECT rect = {0,0,0,0}; 160 bool isVisibleOnScreen = !q->testAttribute(Qt::WA_DontShowOnScreen); 161 if (isVisibleOnScreen && AdjustWindowRectEx(&rect, style & ~WS_OVERLAPPED, FALSE, exsty)) { 162 QTLWExtra *td = maybeTopData(); 163 if (wasMoved && (td && !td->posFromMove)) { 164 x = data.crect.x() + rect.left; 165 y = data.crect.y() + rect.top; 166 } 167 168 if (q->testAttribute(Qt::WA_Resized)) { 169 w = data.crect.width() + (rect.right - rect.left); 170 h = data.crect.height() + (rect.bottom - rect.top); 171 } 172 } 173 //update position & initial size of POPUP window 174 if (isVisibleOnScreen && topLevel && initializeWindow && (style & WS_POPUP)) { 175 if (!q->testAttribute(Qt::WA_Resized)) { 176 w = sw/2; 177 h = 4*sh/10; 178 } 179 if (!wasMoved) { 180 x = sw/2 - w/2; 181 y = sh/2 - h/2; 182 } 183 } 184 185 QT_WA({ 186 const TCHAR *cname = (TCHAR*)windowClassName.utf16(); 187 id = CreateWindowEx(exsty, cname, ttitle, style, 188 x, y, w, h, 189 parentw, 0, appinst, 0); 190 } , { 191 id = CreateWindowExA(exsty, windowClassName.toLatin1(), title95, style, 192 x, y, w, h, 193 parentw, 0, appinst, 0); 194 }); 195 if (!id) 196 qErrnoWarning("QWidget::create: Failed to create window"); 197 setWinId(id); 198 if ((flags & Qt::WindowStaysOnTopHint) || (type == Qt::ToolTip)) { 199 SetWindowPos(id, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); 200 if (flags & Qt::WindowStaysOnBottomHint) 201 qWarning() << "QWidget: Incompatible window flags: the window can't be on top and on bottom at the same time"; 202 } else if (flags & Qt::WindowStaysOnBottomHint) 203 SetWindowPos(id, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); 204 winUpdateIsOpaque(); 205 } else if (q->testAttribute(Qt::WA_NativeWindow) || paintOnScreen()) { // create child widget 206 QT_WA({ 207 const TCHAR *cname = (TCHAR*)windowClassName.utf16(); 208 id = CreateWindowEx(exsty, cname, ttitle, style, 209 data.crect.left(), data.crect.top(), data.crect.width(), data.crect.height(), 210 parentw, NULL, appinst, NULL); 211 } , { 212 id = CreateWindowExA(exsty, windowClassName.toLatin1(), title95, style, 213 data.crect.left(), data.crect.top(), data.crect.width(), data.crect.height(), 214 parentw, NULL, appinst, NULL); 215 }); 216 if (!id) 217 qErrnoWarning("QWidget::create: Failed to create window"); 218 SetWindowPos(id, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); 219 setWinId(id); 220 } 221 222 if (desktop) { 223 q->setAttribute(Qt::WA_WState_Visible); 224 } else if (topLevel && !q->testAttribute(Qt::WA_DontShowOnScreen)) { 225 RECT cr; 226 GetClientRect(id, &cr); 227 // one cannot trust cr.left and cr.top, use a correction POINT instead 228 POINT pt; 229 pt.x = 0; 230 pt.y = 0; 231 ClientToScreen(id, &pt); 232 233 if (data.crect.width() == 0 || data.crect.height() == 0) { 234 data.crect = QRect(pt.x, pt.y, data.crect.width(), data.crect.height()); 235 } else { 236 data.crect = QRect(QPoint(pt.x, pt.y), 237 QPoint(pt.x + cr.right - 1, pt.y + cr.bottom - 1)); 238 } 239 240 if (data.fstrut_dirty) { 241 // be nice to activeqt 242 updateFrameStrut(); 243 } 244 } 245 246 q->setAttribute(Qt::WA_WState_Created); // accept move/resize events 247 hd = 0; // no display context 248 249 if (window) { // got window from outside 250 if (IsWindowVisible(window)) 251 q->setAttribute(Qt::WA_WState_Visible); 252 else 253 q->setAttribute(Qt::WA_WState_Visible, false); 254 } 255 256 if (extra && !extra->mask.isEmpty()) 257 setMask_sys(extra->mask); 258 259 #if defined(QT_NON_COMMERCIAL) 260 QT_NC_WIDGET_CREATE 261 #endif 262 263 if (q->hasFocus() && q->testAttribute(Qt::WA_InputMethodEnabled)) 264 q->inputContext()->setFocusWidget(q); 265 266 if (destroyw) { 267 DestroyWindow(destroyw); 268 } 269 270 if (q != qt_tablet_widget && QWidgetPrivate::mapper) 271 qt_tablet_init(); 272 273 if (q->testAttribute(Qt::WA_DropSiteRegistered)) 274 registerDropSite(true); 275 276 if (maybeTopData() && maybeTopData()->opacity != 255) 277 q->setWindowOpacity(maybeTopData()->opacity/255.); 278 279 if (topLevel && (data.crect.width() == 0 || data.crect.height() == 0)) { 280 q->setAttribute(Qt::WA_OutsideWSRange, true); 281 } 282 283 if (!topLevel && q->testAttribute(Qt::WA_NativeWindow) && q->testAttribute(Qt::WA_Mapped)) { 284 Q_ASSERT(q->internalWinId()); 285 ShowWindow(q->internalWinId(), SW_SHOW); 286 } 287 }
这里调用了qt_reg_winclass()(在QApplication_win.cpp里定义),查看其代码就是RegisterWindows,把window窗口的消息处理设定为:QtWndProc。QObject的初始化没有什么新意,参看QApplication得初始化。
到目前为止的初始化分析,为下一步我们分析Windows消息传递,也就是QT的事件机制打下了基础。
有一种落差是,你配不上自己的野心,也辜负了所受的苦难