您的位置:首页 > 编程语言 > Qt开发

QT分析之QPushButton的初始化

2016-03-15 12:54 567 查看
原文地址:http://blog.163.com/net_worm/blog/static/127702419201001003326522/

在简单的QT程序的第二行,声明了一个QPushButton的对象。先简单看看其初始化过程。

QPushButton的类继承关系为:

QPushButton :public QAbstractButton :pubic QWidget :public QObject, public QPaintDevice


QPushButton的构造:

QPushButton::QPushButton(const QString &text, QWidget *parent)
: QAbstractButton(*new QPushButtonPrivate, parent)
{
Q_D(QPushButton);  // 声明并获得QPushButtonPrivate函数指针d
setText(text);    // 设置按钮的名字
d->init();   // 调用QPushButtonPrivate::init(),其实只是重新设定排布间隔
}


新生成的QPushButtonPrivate对象传递给QAbstractButton之后,发生了什么事呢?

QAbstractButton::QAbstractButton(QAbstractButtonPrivate &dd, QWidget *parent)
: QWidget(dd, parent, 0)
{
Q_D(QAbstractButton);  // 声明并获得QAbstractButtonPrivate函数指针d
d->init();   // 调用QAbstractButtonPrivate::init()
}


QAbstractButtonPrivate::init()做了什么呢?其实只是调用了QPushButton的几个设定函数。

继续看QWidget的初始化过程。

QWidget::QWidget(QWidgetPrivate &dd, QWidget* parent, Qt::WindowFlags f)
: QObject(dd, 0), QPaintDevice()
{
d_func()->init(parent, f);
}


其中d_func()是宏定义Q_DECLARE_PRIVATE(QWidget)中定义的,获取QWidgetPrivate指针的函数。有点奇怪的是,这里怎么没有用Q_D宏定义,与之前的风格有点不同。

QWidgetPrivate::init()里做了什么动作呢?(关键语句用颜色标记)

void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f)
{
Q_Q(QWidget);
if (qApp->type() == QApplication::Tty)
qFatal("QWidget: Cannot create a QWidget when no GUI is being used");

Q_ASSERT(uncreatedWidgets);
uncreatedWidgets->insert(q);

QWidget *desktopWidget = 0;
if (parentWidget && parentWidget->windowType() == Qt::Desktop) {
desktopWidget = parentWidget;
parentWidget = 0;
}

q->data = &data;

if (!q->parent()) {
Q_ASSERT_X(q->thread() == qApp->thread(), "QWidget",
"Widgets must be created in the GUI thread.");
}

data.fstrut_dirty = true;

data.winid = 0;
data.widget_attributes = 0;
data.window_flags = f;
data.window_state = 0;
data.focus_policy = 0;
data.context_menu_policy = Qt::DefaultContextMenu;
data.window_modality = Qt::NonModal;

data.sizehint_forced = 0;
data.is_closing = 0;
data.in_show = 0;
data.in_set_window_state = 0;
data.in_destructor = false;

// Widgets with Qt::MSWindowsOwnDC (typically QGLWidget) must have a window handle.
if (f & Qt::MSWindowsOwnDC)
q->setAttribute(Qt::WA_NativeWindow);

q->setAttribute(Qt::WA_QuitOnClose); // might be cleared in adjustQuitOnCloseAttribute()
adjustQuitOnCloseAttribute();

q->setAttribute(Qt::WA_WState_Hidden);

//give potential windows a bigger "pre-initial" size; create_sys() will give them a new size later
data.crect = parentWidget ? QRect(0,0,100,30) : QRect(0,0,640,480);

focus_next = focus_prev = q;

if ((f & Qt::WindowType_Mask) == Qt::Desktop)
q->create();  // 调用了QWidget::create()
else if (parentWidget)
q->setParent(parentWidget, data.window_flags);
else {
adjustFlags(data.window_flags, q);
resolveLayoutDirection();
// opaque system background?
const QBrush &background = q->palette().brush(QPalette::Window);
setOpaque(q->isWindow() && background.style() != Qt::NoBrush && background.isOpaque());
}
data.fnt = QFont(data.fnt, q);

q->setAttribute(Qt::WA_PendingMoveEvent);
q->setAttribute(Qt::WA_PendingResizeEvent);

if (++QWidgetPrivate::instanceCounter > QWidgetPrivate::maxInstances)
QWidgetPrivate::maxInstances = QWidgetPrivate::instanceCounter;

if (QApplicationPrivate::app_compile_version < 0x040200
|| QApplicationPrivate::testAttribute(Qt::AA_ImmediateWidgetCreation))
q->create();

// 下面的三行,产生并发送了Create事件
QEvent e(QEvent::Create);
QApplication::sendEvent(q, &e);
QApplication::postEvent(q, new QEvent(QEvent::PolishRequest));

extraPaintEngine = 0;
}


看看QWidget::create()的实现:

void QWidget::create(WId window, bool initializeWindow, bool destroyOldWindow)
{
Q_D(QWidget);
if (testAttribute(Qt::WA_WState_Created) && window == 0 && internalWinId())
return;

if (d->data.in_destructor)
return;

Qt::WindowType type = windowType();
Qt::WindowFlags &flags = data->window_flags;

if ((type == Qt::Widget || type == Qt::SubWindow) && !parentWidget()) {
type = Qt::Window;
flags |= Qt::Window;
}

if (QWidget *parent = parentWidget()) {
if (type & Qt::Window) {
if (!parent->testAttribute(Qt::WA_WState_Created))
parent->createWinId();
} else if (testAttribute(Qt::WA_NativeWindow) && !parent->internalWinId()
&& !testAttribute(Qt::WA_DontCreateNativeAncestors)) {
// We're about to create a native child widget that doesn't have a native parent;
// enforce a native handle for the parent unless the Qt::WA_DontCreateNativeAncestors
// attribute is set.
d->createWinId(window);
// Nothing more to do.
Q_ASSERT(testAttribute(Qt::WA_WState_Created));
Q_ASSERT(internalWinId());
return;
}
}

static int paintOnScreenEnv = -1;
if (paintOnScreenEnv == -1)
paintOnScreenEnv = qgetenv("QT_ONSCREEN_PAINT").toInt() > 0 ? 1 : 0;
if (paintOnScreenEnv == 1)
setAttribute(Qt::WA_PaintOnScreen);

if (QApplicationPrivate::testAttribute(Qt::AA_NativeWindows))
setAttribute(Qt::WA_NativeWindow);

#ifdef ALIEN_DEBUG
qDebug() << "QWidget::create:" << this << "parent:" << parentWidget()
<< "Alien?" << !testAttribute(Qt::WA_NativeWindow);
#endif

// Unregister the dropsite (if already registered) before we
// re-create the widget with a native window.
if (testAttribute(Qt::WA_WState_Created) && !internalWinId() && testAttribute(Qt::WA_NativeWindow)
&& d->extra && d->extra->dropTarget) {
d->registerDropSite(false);
}

d->updateIsOpaque();

setAttribute(Qt::WA_WState_Created);                        // set created flag
d->create_sys(window, initializeWindow, destroyOldWindow);

// a real toplevel window needs a backing store
if (isWindow()) {
delete d->topData()->backingStore;
// QWidgetBackingStore will check this variable, hence it must be 0
d->topData()->backingStore = 0;
if (hasBackingStoreSupport())
d->topData()->backingStore = new QWidgetBackingStore(this);
}

d->setModal_sys();

if (!isWindow() && parentWidget() && parentWidget()->testAttribute(Qt::WA_DropSiteRegistered))
setAttribute(Qt::WA_DropSiteRegistered, true);

// need to force the resting of the icon after changing parents
if (testAttribute(Qt::WA_SetWindowIcon))
d->setWindowIcon_sys(true);
if (isWindow() && !d->topData()->iconText.isEmpty())
d->setWindowIconText_helper(d->topData()->iconText);
if (windowType() != Qt::Desktop) {
d->updateSystemBackground();

if (isWindow() && !testAttribute(Qt::WA_SetWindowIcon))
d->setWindowIcon_sys();
}
}


这里QWidgetPrivate::create_sys()定义在QWidget_win.cpp里。

void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyOldWindow)
{
Q_Q(QWidget);
static int sw = -1, sh = -1;

Qt::WindowType type = q->windowType();
Qt::WindowFlags flags = data.window_flags;

bool topLevel = (flags & Qt::Window);
bool popup = (type == Qt::Popup);
bool dialog = (type == Qt::Dialog
|| type == Qt::Sheet
|| (flags & Qt::MSWindowsFixedSizeDialogHint));
bool desktop = (type == Qt::Desktop);
bool tool = (type == Qt::Tool || type == Qt::Drawer);

HINSTANCE appinst  = qWinAppInst();
HWND parentw, destroyw = 0;
WId id;

QString windowClassName = qt_reg_winclass(q);

if (!window)                                // always initialize
initializeWindow = true;

if (popup)
flags |= Qt::WindowStaysOnTopHint; // a popup stays on top

if (sw < 0) {                                // get the (primary) screen size
sw = GetSystemMetrics(SM_CXSCREEN);
sh = GetSystemMetrics(SM_CYSCREEN);
}

if (desktop && !q->testAttribute(Qt::WA_DontShowOnScreen)) {                                // desktop widget
popup = false;                                // force this flags off
if (QSysInfo::WindowsVersion != QSysInfo::WV_NT && QSysInfo::WindowsVersion != QSysInfo::WV_95)
data.crect.setRect(GetSystemMetrics(76 /* SM_XVIRTUALSCREEN  */), GetSystemMetrics(77 /* SM_YVIRTUALSCREEN  */),
GetSystemMetrics(78 /* SM_CXVIRTUALSCREEN */), GetSystemMetrics(79 /* SM_CYVIRTUALSCREEN */));
else
data.crect.setRect(0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
}

parentw = q->parentWidget() ? q->parentWidget()->effectiveWinId() : 0;

#ifdef UNICODE
QString title;
const TCHAR *ttitle = 0;
#endif
QByteArray title95;
int style = WS_CHILD;
int exsty = 0;

if (window) {
style = GetWindowLongA(window, GWL_STYLE);
if (!style)
qErrnoWarning("QWidget::create: GetWindowLong failed");
topLevel = false; // #### needed for some IE plugins??
} else if (popup || (type == Qt::ToolTip) || (type == Qt::SplashScreen)) {
style = WS_POPUP;
} else if (topLevel && !desktop) {
if (flags & Qt::FramelessWindowHint)
style = WS_POPUP;                // no border
else if (flags & Qt::WindowTitleHint)
style = WS_OVERLAPPED;
else
style = 0;
}
if (!desktop) {
// if (!testAttribute(Qt::WA_PaintUnclipped))
// ### Commented out for now as it causes some problems, but
// this should be correct anyway, so dig some more into this
#ifndef Q_FLATTEN_EXPOSE
style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN ;
#endif
if (topLevel) {
if ((type == Qt::Window || dialog || tool)) {
if (!(flags & Qt::FramelessWindowHint)) {
if (!(flags & Qt::MSWindowsFixedSizeDialogHint)) {
style |= WS_THICKFRAME;
if(!(flags &
( Qt::WindowSystemMenuHint
| Qt::WindowTitleHint
| Qt::WindowMinMaxButtonsHint
| Qt::WindowCloseButtonHint
| Qt::WindowContextHelpButtonHint)))
style |= WS_POPUP;
} else {
style |= WS_POPUP | WS_DLGFRAME;
}
}
if (flags & Qt::WindowTitleHint)
style |= WS_CAPTION;
if (flags & Qt::WindowSystemMenuHint)
style |= WS_SYSMENU;
if (flags & Qt::WindowMinimizeButtonHint)
style |= WS_MINIMIZEBOX;
if (shouldShowMaximizeButton())
style |= WS_MAXIMIZEBOX;
if (tool)
exsty |= WS_EX_TOOLWINDOW;
if (flags & Qt::WindowContextHelpButtonHint)
exsty |= WS_EX_CONTEXTHELP;
} else {
exsty |= WS_EX_TOOLWINDOW;
}
}
}

if (flags & Qt::WindowTitleHint) {
QT_WA({
title = q->isWindow() ? qAppName() : q->objectName();
ttitle = (TCHAR*)title.utf16();
} , {
title95 = q->isWindow() ? qAppName().toLocal8Bit() : q->objectName().toLatin1();
});
}

// The Qt::WA_WState_Created flag is checked by translateConfigEvent() in
// qapplication_win.cpp. We switch it off temporarily to avoid move
// and resize events during creationt
q->setAttribute(Qt::WA_WState_Created, false);

if (window) {                                // override the old window
if (destroyOldWindow)
destroyw = data.winid;
id = window;
setWinId(window);
LONG res = SetWindowLongA(window, GWL_STYLE, style);
if (!res)
qErrnoWarning("QWidget::create: Failed to set window style");
#ifdef _WIN64
res = SetWindowLongPtrA( window, GWLP_WNDPROC, (LONG_PTR)QtWndProc );
#else
res = SetWindowLongA( window, GWL_WNDPROC, (LONG)QtWndProc );
#endif
if (!res)
qErrnoWarning("QWidget::create: Failed to set window procedure");
} else if (desktop) {                        // desktop widget
id = GetDesktopWindow();
//         QWidget *otherDesktop = QWidget::find(id);        // is there another desktop?
//         if (otherDesktop && otherDesktop->testWFlags(Qt::WPaintDesktop)) {
//             otherDesktop->d_func()->setWinId(0);        // remove id from widget mapper
//             d->setWinId(id);                     // make sure otherDesktop is
//             otherDesktop->d_func()->setWinId(id);       //   found first
//         } else {
setWinId(id);
//         }
} else if (topLevel) {                       // create top-level widget
if (popup)
parentw = 0;

const bool wasMoved = q->testAttribute(Qt::WA_Moved);
int x = wasMoved ? data.crect.left() : CW_USEDEFAULT;
int y = wasMoved ? data.crect.top() : CW_USEDEFAULT;
int w = CW_USEDEFAULT;
int h = CW_USEDEFAULT;

// Adjust for framestrut when needed
RECT rect = {0,0,0,0};
bool isVisibleOnScreen = !q->testAttribute(Qt::WA_DontShowOnScreen);
if (isVisibleOnScreen && AdjustWindowRectEx(&rect, style & ~WS_OVERLAPPED, FALSE, exsty)) {
QTLWExtra *td = maybeTopData();
if (wasMoved && (td && !td->posFromMove)) {
x = data.crect.x() + rect.left;
y = data.crect.y() + rect.top;
}

if (q->testAttribute(Qt::WA_Resized)) {
w = data.crect.width() + (rect.right - rect.left);
h = data.crect.height() + (rect.bottom - rect.top);
}
}
//update position & initial size of POPUP window
if (isVisibleOnScreen && topLevel && initializeWindow && (style & WS_POPUP)) {
if (!q->testAttribute(Qt::WA_Resized)) {
w = sw/2;
h = 4*sh/10;
}
if (!wasMoved) {
x = sw/2 - w/2;
y = sh/2 - h/2;
}
}

QT_WA({
const TCHAR *cname = (TCHAR*)windowClassName.utf16();
id = CreateWindowEx(exsty, cname, ttitle, style,
x, y, w, h,
parentw, 0, appinst, 0);
} , {
id = CreateWindowExA(exsty, windowClassName.toLatin1(), title95, style,
x, y, w, h,
parentw, 0, appinst, 0);
});
if (!id)
qErrnoWarning("QWidget::create: Failed to create window");
setWinId(id);
if ((flags & Qt::WindowStaysOnTopHint) || (type == Qt::ToolTip)) {
SetWindowPos(id, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
if (flags & Qt::WindowStaysOnBottomHint)
qWarning() << "QWidget: Incompatible window flags: the window can't be on top and on bottom at the same time";
} else if (flags & Qt::WindowStaysOnBottomHint)
SetWindowPos(id, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
winUpdateIsOpaque();
} else if (q->testAttribute(Qt::WA_NativeWindow) || paintOnScreen()) { // create child widget
QT_WA({
const TCHAR *cname = (TCHAR*)windowClassName.utf16();
id = CreateWindowEx(exsty, cname, ttitle, style,
data.crect.left(), data.crect.top(), data.crect.width(), data.crect.height(),
parentw, NULL, appinst, NULL);
} , {
id = CreateWindowExA(exsty, windowClassName.toLatin1(), title95, style,
data.crect.left(), data.crect.top(), data.crect.width(), data.crect.height(),
parentw, NULL, appinst, NULL);
});
if (!id)
qErrnoWarning("QWidget::create: Failed to create window");
SetWindowPos(id, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
setWinId(id);
}

if (desktop) {
q->setAttribute(Qt::WA_WState_Visible);
} else if (topLevel && !q->testAttribute(Qt::WA_DontShowOnScreen)) {
RECT  cr;
GetClientRect(id, &cr);
// one cannot trust cr.left and cr.top, use a correction POINT instead
POINT pt;
pt.x = 0;
pt.y = 0;
ClientToScreen(id, &pt);

if (data.crect.width() == 0 || data.crect.height() == 0) {
data.crect = QRect(pt.x, pt.y, data.crect.width(), data.crect.height());
} else {
data.crect = QRect(QPoint(pt.x, pt.y),
QPoint(pt.x + cr.right - 1, pt.y + cr.bottom - 1));
}

if (data.fstrut_dirty) {
// be nice to activeqt
updateFrameStrut();
}
}

q->setAttribute(Qt::WA_WState_Created);                // accept move/resize events
hd = 0;                                        // no display context

if (window) {                                // got window from outside
if (IsWindowVisible(window))
q->setAttribute(Qt::WA_WState_Visible);
else
q->setAttribute(Qt::WA_WState_Visible, false);
}

if (extra && !extra->mask.isEmpty())
setMask_sys(extra->mask);

#if defined(QT_NON_COMMERCIAL)
QT_NC_WIDGET_CREATE
#endif

if (q->hasFocus() && q->testAttribute(Qt::WA_InputMethodEnabled))
q->inputContext()->setFocusWidget(q);

if (destroyw) {
DestroyWindow(destroyw);
}

if (q != qt_tablet_widget && QWidgetPrivate::mapper)
qt_tablet_init();

if (q->testAttribute(Qt::WA_DropSiteRegistered))
registerDropSite(true);

if (maybeTopData() && maybeTopData()->opacity != 255)
q->setWindowOpacity(maybeTopData()->opacity/255.);

if (topLevel && (data.crect.width() == 0 || data.crect.height() == 0)) {
q->setAttribute(Qt::WA_OutsideWSRange, true);
}

if (!topLevel && q->testAttribute(Qt::WA_NativeWindow) && q->testAttribute(Qt::WA_Mapped)) {
Q_ASSERT(q->internalWinId());
ShowWindow(q->internalWinId(), SW_SHOW);
}
}


这里调用了qt_reg_winclass()(在QApplication_win.cpp里定义),查看其代码就是RegisterWindows,把window窗口的消息处理设定为:QtWndProc。QObject的初始化没有什么新意,参看QApplication得初始化。

到目前为止的初始化分析,为下一步我们分析Windows消息传递,也就是QT的事件机制打下了基础。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: