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的事件机制打下了基础。

原文地址:https://www.cnblogs.com/lfsblack/p/5279016.html