漫谈QWidget及其派生类(五)

原文地址: https://blog.csdn.net/dbzhang800/article/details/6780282

和前面的 一二三四 没有什么连贯性,也没涉及QWidget的派生类,既然是漫谈,我忍了。本文内容:QWidget的创建

起点...


看看本文的代码,是不是很失望?这么简单的一个超级入门级小程序,能有什么可看的?

#include <QApplication>
#include <QWidget>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget w;
    w.show();
    return a.exec();
}

真的没什么可看的么...

从构造到显示,我们的QWidget的event()函数会依次接收到下面的...这些...事件...

QWidget w;

事件type 事件type(对应前面的数字) 此时将调用的函数
15 QEvent::Create

w.show();

事件type 事件type(对应前面的数字) 此时将调用的函数
203 QEvent::WinIdChange
75 QEvent::Polish QStyle::polish()
13 QEvent::Move QWidget::moveEvent()
14 QEvent::Resize QWidget::resizeEvent()
17 QEvent::Show QWidget::showEvent()
24 QEvent::WindowActivate
99 QEvent::ActivationChange
26 QEvent::ShowToParent

a.exec();

事件type 事件type(对应前面的数字) 此时将调用的函数
74 QEvent::PolishRequest QWidget::ensurePolished()
77 QEvent::UpdateRequest QWidgetBackingStore::sync()
12 QEvent::Paint QWidget::paintEvent()

(注:上表是Qt4.7/WinXP下的结果,不同平台下结果会稍有不同,你只需了解这点就好)

如何切入?


如何讨论这个问题,真的很头痛,找不到合适的切入点...

恩,考虑个问题吧?我们知道:

X11下 Windows 下
创建一个窗口 XCreateWindow() CreateWindow()
显示一个窗口 XMapWindow() ShowWindow()

自然而然,我们很容易将

  • 这两个和前面的 QEvent::Create 和 QEvent::Show 对应起来
  • 也很容易将他们和 QWidget w; 和 w.show(); 对应起来

但是,我要说的是,这个结论是错误的;创建和显示都是在QWidget::show()内完成的!

有什么用?


了解这些细节有什么用,我直接用不就好了,干嘛要考虑这些乱七八糟的东西呢?

不少人可能遇到过这种问题:

  • 在构造函数内获取QWidget的size()和几何尺寸,获取的总是不对

恩,原因就是:构造函数结束时,窗口其实还没有创建,窗口尺寸尚未正式被设置。

废话少说,简单看看源码片段(注,代码中忽略了很多的细节,希望不会对大家造成误导):

构造函数


构造函数中,初始化了大量的参数(我们不关心这些,也不列出了)。我们只看看感兴趣的attribute和事件

void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f)
{
...
    q->setAttribute(Qt::WA_PendingMoveEvent);
    q->setAttribute(Qt::WA_PendingResizeEvent);
    QEvent e(QEvent::Create);
    QApplication::sendEvent(q, &e);
    QApplication::postEvent(q, new QEvent(QEvent::PolishRequest));
}
  • 两个带Pending的attribute,暂时不谈,稍后我们还会看到它。
  • QEvent::Create 事件,也是我们前面列出的第一个事件!
  • QEvent::PolishRequest事件,注意,这儿采用的postEvent,要到exec()启动以后才会被处理。

$show()


我们都知道,show()、hide()、setHidden()都是setVisible的马甲,所以:

  • create()
void QWidget::setVisible(bool visible)
{
...
        QWidget *pw = parentWidget();
        if (!testAttribute(Qt::WA_WState_Created)
            && (isWindow() || pw->testAttribute(Qt::WA_WState_Created))) {
            create();
        }

恩,QWidget::create()在这儿被调用,借助QWidgetPrivate::create_sys使得窗口被创建。此时QEvent::WinIdChange事件通过sendEvent被派发。

  • ensurePolished()
        // polish if necessary
        ensurePolished();

前面提到了,第一调用该函数时,QEvent::Polish事件将通过sendEvent被派发

  • QLayout::activate()
  // activate our layout before we and our children become visible
        if (d->layout)
            d->layout->activate();

这将通知所有的layout,你们目前的尺寸无效,用到是要重新生成!(额,我们这儿例子中其实没有用到layout)

注意:QLayout是顶级layout负责制,如果你对被嵌套的layout调用activate,最终调用的还是顶级layout(一级一级的向上找)的相应成员。

  • adjustSize()
    if (!wasResized
            && (isWindow() || !parentWidget()->d_func()->layout))  {
            if (isWindow()) {
                adjustSize();
                if (windowState() != initialWindowState)
                    setWindowState(initialWindowState);
            } else {
                adjustSize();
            }

窗口的尺寸是在这儿被调整到合适大小的

  • show_helper()
        if (isWindow() || parentWidget()->isVisible()) {
            d->show_helper();

对于show()来说,这是最核心的东西了。

在该函数内,它会检查有无Pending的move或resize的属性。对我们这个,显然是有的。于是 QEvent::Move和QEvent::Resize事件通过sendEvent()被派发

然后派发QEvent::Show事件,随后调用QWidgetPrivate::show_sys()来显示窗口

  • 派发QEvent::ShowToParent
        QEvent showToParentEvent(QEvent::ShowToParent);
        QApplication::sendEvent(this, &showToParentEvent);

QApplication::exec()


事件循环,这部分就不介绍。先前通过postEvent()派发的事件以及系统的Spontaneous事件,此时都开始被处理...

PolishRequest


尽管不在此处调用,我们还是看一下PolishRequest将如何被处理:

  • QEvent::PolishRequest
    case QEvent::PolishRequest:
        ensurePolished();

如果此时尚未polished,ensurePolished将:给自己发送QEvent::Polish事件,同时递归调用子类的ensurePolished()函数

  • QEvent::Polish
    case QEvent::Polish: {
        style()->polish(this);
        setAttribute(Qt::WA_WState_Polished)

不过在我们这个例子中,在进入事件循环之前,ensurePolished() 已经被调用多次了。所以此处不会在派发QEvent::Polish事件。

Paint


在X11下,系统的Spontaneous事件 Expose 最终将被转换QEvent::Paint

int QApplication::x11ProcessEvent(XEvent* event)
{
    Q_D(QApplication);
...
    switch (event->type) {
    case GraphicsExpose:
    case Expose:                                // paint event
        widget->translatePaintEvent(event);
        break;
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,919评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,567评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,316评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,294评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,318评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,245评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,120评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,964评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,376评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,592评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,764评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,460评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,070评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,697评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,846评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,819评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,665评论 2 354

推荐阅读更多精彩内容