ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

QWidget跨平台原因分析

2019-05-24 09:48:13  阅读:542  来源: 互联网

标签:const Qt create window 跨平台 result QWidget 原因


Qt是一个跨平台的C++库,目前无论是嵌入式操作系统UI开发,还是在Linux/windows PC级应用程序开发都占有非常庞大的用户群。既然说是跨平台,目前大约有两种方式,一种是以java/python为代表的解释执行,另一种是程序库的中间层实施跨平台,Qt做为C++界面库,选择的是后者。至于究竟是如何实现的正是本文所分析的。

这里选择对QWidget进行分析。至于为什么选择QWidget,而不选择其他控件,原因很简单,QWidget是Qt的基本控件之一,可以说是最重要的控件之一,基本上几乎所有的UI控件都是基于它的,对其进行分析也是理所当然的。

一、QWidget的创建

QWidget构造如下:

QWidget::QWidget(QWidget *parent, Qt::WindowFlags f)
    : QObject(*new QWidgetPrivate, 0), QPaintDevice()
{
    QT_TRY {
        d_func()->init(parent, f);
    } QT_CATCH(...) {
        QWidgetExceptionCleaner::cleanup(this, d_func());
        QT_RETHROW;
    }
}

这里进入到 QWidgetPrivate 的init函数中去看:

注意:代码中删除了部分不重要的代码
void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f)
{
    Q_Q(QWidget);
    Q_ASSERT(allWidgets);
    if (allWidgets)
        allWidgets->insert(q);
    q->data = &data;
    if (targetScreen >= 0) {
        topData()->initialScreenIndex = targetScreen;
        if (QWindow *window = q->windowHandle())
            window->setScreen(QGuiApplication::screens().value(targetScreen, nullptr));
    }
//默认是隐藏的
    q->setAttribute(Qt::WA_ContentsMarginsRespectsSafeArea);
    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();
    else if (parentWidget)
        q->setParent(parentWidget, data.window_flags);

//这里注意
    if (QApplicationPrivate::testAttribute(Qt::AA_ImmediateWidgetCreation)) 
        q->create();
    QEvent e(QEvent::Create);
    QApplication::sendEvent(q, &e);
    QApplication::postEvent(q, new QEvent(QEvent::PolishRequest));
}

这里需要注意的是,除了是QDesktop类型的窗口和窗口属性设置为Qt::AA_ImmediateWidgetCreation的窗口,这个窗口是不会立刻被创建的,至于何时创建,后文会有叙述。这里的CreateEvent只是发出了创建事件,该类并没有处理这个事件,所以目前来看只是父类处理该事件。针对QDesktop类型的窗口和窗口属性设置为Qt::AA_ImmediateWidgetCreation的窗口,我们不做详细分析,是因为该窗口除了是立刻被创建之外,其与普通窗口并没有区别,那么普通的QWidget又该如何被创建呢?

在QWidget的生命历程中,我们唯一还能够让它显示出来的方法就是SetVisable和show。事实上,show函数调用的代码就是setvisable(true),所以我们还是只能去找SetVisable.

注意:这里删除了部分不重要代码
void QWidget::setVisible(bool visible)
{
        Q_D(QWidget);
        QWidget *pw = parentWidget();
        if (!testAttribute(Qt::WA_WState_Created)
            && (isWindow() || pw->testAttribute(Qt::WA_WState_Created))) {
//这里又调用了熟悉的函数
            create();
        }

        if (d->layout)
            d->layout->activate();
        QEvent showToParentEvent(QEvent::ShowToParent);
        QApplication::sendEvent(this, &showToParentEvent);
    } 

        QEvent hideToParentEvent(QEvent::HideToParent);
        QApplication::sendEvent(this, &hideToParentEvent);
    }
}

进入到QWidget::create:

void QWidget::create(WId window, bool initializeWindow, bool destroyOldWindow)
{
    Q_D(QWidget);
    if ((type == Qt::Widget || type == Qt::SubWindow) && !parentWidget()) {
        type = Qt::Window;
        flags |= Qt::Window;
    }
//检查屏幕的环境变量
    static const bool paintOnScreenEnv = qEnvironmentVariableIntValue("QT_ONSCREEN_PAINT") > 0;
    if (paintOnScreenEnv)
        setAttribute(Qt::WA_PaintOnScreen);
//注意这里
    d->create_sys(window, initializeWindow, destroyOldWindow);
    d->setModal_sys();

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

二、QWidgetPrivate::create_sys

之前的分析显示进入到了create_sys,我们继续。

void QWindowPrivate::create(bool recursive, WId nativeHandle)
{
    if (q->parent())
        q->parent()->create();
//注意这里:
//如果按照正常的窗口创建过程,这里是一定会进来的,因为无论是创建QDesktop窗口还是普通
//QWidget窗口都是调用一样的调用一个默认参数的create()函数

    QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration();
    platformWindow = nativeHandle ? platformIntegration->createForeignWindow(q, nativeHandle)
        : platformIntegration->createPlatformWindow(q);

    QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceCreated);
    QGuiApplication::sendEvent(q, &e);
}

代码中首先调用了一个对外的create,然而这里并没有什么有价值的货,继续向下,我们看到了什么?没错,createPlatformWindow !

这里的nativeHandle ===0,这里我用了 恒 !等 !于 !成功找到突破口,继续向下。

看到这里就很兴奋了,看名字就知道是跟平台相关的的一个虚类,大胆猜想这个类的存在就是支持跨平台的关键所在!动手一试,果然如此:

这是windows平台的

这是linux平台的:

安卓和BSD的已经贴出来了,还有很多,此处就不一一列举了。

三、QWindowsIntegration::createPlatformWindow

这次我选择windows的创建过程,linux的下次分析。

QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) const
{
//这里的QWindowsDesktopWindow实际上上最后还是调用到QPlatformWindow这里来,所以用不着担心太多
    if (window->type() == Qt::Desktop) {
        QWindowsDesktopWindow *result = new QWindowsDesktopWindow(window);
        return result;
    }

    QWindowsWindowData requested;
    requested.geometry = QHighDpi::toNativePixels(window->geometry(), window);
//注意这里
    QWindowsWindowData obtained =
        QWindowsWindowData::create(window, requested,
                                   QWindowsWindow::formatWindowTitle(window->title()));

    QWindowsWindow *result = createPlatformWindowHelper(window, obtained);

    if (QWindowsMenuBar *menuBarToBeInstalled = QWindowsMenuBar::menuBarOf(window))
        menuBarToBeInstalled->install(result);

    return result;
}

进入到QWindowsWindowData::create继续查看:

QWindowsWindowData
    QWindowsWindowData::create(const QWindow *w,
                                       const QWindowsWindowData &parameters,
                                       const QString &title)
{
    WindowCreationData creationData;
    creationData.fromWindow(w, parameters.flags);
    QWindowsWindowData result = creationData.create(w, parameters, title);
    // Force WM_NCCALCSIZE (with wParam=1) via SWP_FRAMECHANGED for custom margin.
    creationData.initialize(w, result.hwnd, !parameters.customMargins.isNull(), 1);
    return result;
}

这里很简洁,没啥说的,继续向下,进入到

QWindowsWindowData result = creationData.create(w, parameters, title);
struct WindowCreationData
{
    typedef QWindowsWindowData WindowData;
    enum Flags { ForceChild = 0x1, ForceTopLevel = 0x2 };

    WindowCreationData() : parentHandle(0), type(Qt::Widget), style(0), exStyle(0),
        topLevel(false), popup(false), dialog(false),
        tool(false), embedded(false), hasAlpha(false) {}

    void fromWindow(const QWindow *w, const Qt::WindowFlags flags, unsigned creationFlags = 0);
    inline WindowData create(const QWindow *w, const WindowData &data, QString title) const;
    inline void applyWindowFlags(HWND hwnd) const;
    void initialize(const QWindow *w, HWND h, bool frameChange, qreal opacityLevel) const;

    Qt::WindowFlags flags;
};

点开create函数:

QWindowsWindowData
    WindowCreationData::create(const QWindow *w, const WindowData &data, QString title) const
{
    result.flags = flags;

    const HINSTANCE appinst = (HINSTANCE)GetModuleHandle(0);

    const QString windowClassName = QWindowsContext::instance()->registerWindowClass(w);

    const QRect rect = QPlatformWindow::initialGeometry(w, data.geometry, defaultWindowWidth, defaultWindowHeight);

    if (title.isEmpty() && (result.flags & Qt::WindowTitleHint))
        title = topLevel ? qAppName() : w->objectName();

    const wchar_t *titleUtf16 = reinterpret_cast<const wchar_t *>(title.utf16());
    const wchar_t *classNameUtf16 = reinterpret_cast<const wchar_t *>(windowClassName.utf16());

    const QWindowCreationContextPtr context(new QWindowCreationContext(w, data.geometry, rect, data.customMargins, style, exStyle));
    QWindowsContext::instance()->setWindowCreationContext(context);



    result.hwnd = CreateWindowEx(exStyle, classNameUtf16, titleUtf16,
                                 style,
                                 context->frameX, context->frameY,
                                 context->frameWidth, context->frameHeight,
                                 parentHandle, NULL, appinst, NULL);

    if (!result.hwnd) {
        qErrnoWarning("%s: CreateWindowEx failed", __FUNCTION__);
        return result;
    }

}

注意这句:

    const QString windowClassName = QWindowsContext::instance()->registerWindowClass(w);

和这句:

    result.hwnd = CreateWindowEx(exStyle, classNameUtf16, titleUtf16,
                                 style,
                                 context->frameX, context->frameY,
                                 context->frameWidth, context->frameHeight,
                                 parentHandle, NULL, appinst, NULL);

这里就已经很明了了,继续向下没啥意义了,这篇就到此为止了

标签:const,Qt,create,window,跨平台,result,QWidget,原因
来源: https://blog.csdn.net/k_wang_/article/details/90484374

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有