现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

漫谈QWidget及其派生类(五)

2015-05-02 06:28 工业·编程 ⁄ 共 3611字 ⁄ 字号 暂无评论

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

相关阅读

    ----漫谈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()函数会依次接收到下面的...这些...事件...

事件type

事件type(对应前面的数字)

所处代码

此时将调用的函数

15

QEvent::Create

QWidget w;

 

203

QEvent::WinIdChange

w.show();

 

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

 

74

QEvent::PolishRequest

a.exec();

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;
作者:dbzhang800

给我留言

留言无头像?