Declarative Widgets is a QML plugin that adds Qt Widgets support to QML. This means we can now easily mix the power of QML with the comfort of a Widgets UI on desktop.

1
2
3
4
5
6
7
8
9
10
11
import QtWidgets 1.0
 
MainWindow {
    width: 640
    height: 400
 
    Label {
        text: "Hello Declarative Widgets!"
        alignment: Qt.AlignHCenter | Qt.AlignVCenter
    }
}

Background

Declarative Widgets was born out of a breakfast discussion about how awesome it would be to use QML to describe QWidget-based scenes. If you have ever worked on a Qt Quick project and then switched back to creating a Widgets UI you will understand how frustrating it can be to write and maintain a complex UI in plain C++, or even create and modify UI files in Qt Designer.

The real power of QML, however, is in property bindings. Property bindings allow us to set the value of a property as an expression that is evaluated when ever a property involved in that expression changes. Take the following example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import QtWidgets 1.0
 
GroupBox {
    title: qsTr("New Contact: %1 %2").arg(firstName.text).arg(lastName.text)
 
    FormLayout {
        LineEdit {
            id: firstName
            FormLayout.label: qsTr("First Name")
        }
        LineEdit {
            id: lastName
            FormLayout.label: qsTr("Last Name")
        }
    }
}

The title property of the GroupBox is updated when the text property of either LineEdit changes. We could build this example in C++, but in QML we don’t need to write any boilerplate code to connect to signals or define slots. By using Declarative Widgets we don’t need to worry about writing our own UI components either; we can make use of all the existing widgets we developed warm, fuzzy feelings for over the years.

Implementation

To get an idea of how the Declarative Widgets plugin works, lets take a look at how QWidget is integrated into QML.

1
qmlRegisterExtendedType<QWidget, DeclarativeWidgetExtension>(uri, 1, 0, "Widget");

QWidgetneeds a few tweaks in order to integrate it into QML: there is no default property, the xywidth and height properties are read-only, and the geometry and visible properties do not have notify signals. Rather than modifying QWidget directly we can useqmlRegisterExtendedType to register an extension object which adds or overrides the properties we need.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class DeclarativeWidgetExtension : public DeclarativeObjectExtension
{
    Q_OBJECT
 
    // repeat property declarations, qmlRegisterExtendedType doesn't see the ones from base class
    Q_PROPERTY(QQmlListProperty<QObject> data READ data DESIGNABLE false CONSTANT)
 
    Q_PROPERTY(int x READ x WRITE setX NOTIFY posChanged)
    Q_PROPERTY(int y READ y WRITE setY NOTIFY posChanged)
    Q_PROPERTY(int width READ width WRITE setWidth NOTIFY sizeChanged)
    Q_PROPERTY(int height READ height WRITE setHeight NOTIFY sizeChanged)
    Q_PROPERTY(QRect geometry READ geometry WRITE setGeometry NOTIFY geometryChanged)
    Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged)
 
    Q_CLASSINFO("DefaultProperty", "data")
}

Our extension object, DeclarativeWidgetExtension, derives from DeclarativeObjectExtension which provides us with a default property. A default property is the property to which a value is assigned if an object is declared within another object’s definition without declaring it as a value for a particular property. In Qt Quick, the default property is used to construct the visual scene hierarchy, and we do the same with Declarative Widgets to create the QWidget hierarchy, calling QWidget::setParentQWidget::setLayout, or QWidget::addAction depending on the type of the declared object. Note that we have to redeclare the data property because qmlRegisterExtendedType doesn’t see the one from the base class.

To make the read-only properties writable, we override the existing property and provide a WRITE accessor function to make the appropriate change. Let’s take a look at the new x property:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
QWidget *DeclarativeWidgetExtension::extendedWidget() const
{
    QWidget *parentWidget = qobject_cast<QWidget*>(parent());
    Q_ASSERT(parentWidget);
    Q_UNUSED(parentWidget);
 
    return parentWidget;
}
 
int DeclarativeWidgetExtension::x() const
{
    return extendedWidget()->x();
}
 
void DeclarativeWidgetExtension::setX(int value)
{
    QWidget *widget = extendedWidget();
 
    if (value == widget->x())
      return;
 
    QRect geometry = widget->geometry();
    geometry.moveLeft(value);
    widget->setGeometry(geometry);
}

The READ accessor function simply calls the original READaccessor function on the extended type. However, QWidget does not have an existing setX function so we have to update the x property using QWidget::setGeometry.

Keen observers will notice that we haven’t emitted any of the NOTIFY signals that we declared. This is because widgets respond to events delivered to them by Qt as a result of things that have happened either within the application or as a result of outside activity that the application needs to know about. In order to hook into this system, our extension object installs itself as an event filter on the object we are extending. An event filter receives all the events for the target object before the target does, allowing us to observe and react to the events as required.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
bool DeclarativeWidgetExtension::eventFilter(QObject *watched, QEvent *event)
{
    Q_ASSERT(watched == parent());
    Q_UNUSED(watched);
 
    switch (event->type())
    {
    case QEvent::Move:
        emit posChanged();
        emit geometryChanged();
        break;
 
    case QEvent::Resize:
        emit sizeChanged();
        emit geometryChanged();
        break;
 
    case QEvent::Show:
    case QEvent::Hide:
        emit visibleChanged(isVisible());
        break;
 
    default:
        break;
    }
 
    return false;
}

In our event filter we simply emit the NOTIFY signals when we receive the appropriate event. In our x property example we receive a QEvent::Move event as a result of our call to QWidget::setGeometry. This is where we emit posChanged.

The geometry and visibleproperties that we overrode to add a NOTIFY signal to simply call the original QWidgetREAD and WRITE accessor functions. Then, in the event filter we emit the new signals when we receive the appropriate event.

What about QQuickWidget or QWebEngineView?

There are no additional limitations to using QQuickWidget with Declarative Widgets. One of the use cases we came up with for using Declarative Widgets is as a stepping stone to porting existing Qt Widgets applications to Qt Quick. The first step of the port would be to isolate the business logic and replicate the existing UI using Declarative Widgets (we even wrote a tool to generate QML files from .ui files). You could then replace chunks of the UI with QtQuick components displayed in QQuickWidgets.

To see QQuickWidget or QWebEngineView in action take a look through our examples on GitHub.

How do I get it?

The Declarative Widgets source code is available on GitHub: https://github.com/KDAB/DeclarativeWidgets

If you like Declarative Widgets please consider contributing to the project. Adding Qt Widgets support to QML is a large task and whilst we have done most of the ground work there are surely features we have missed. If there are features you need and you are unable to contribute, please get in touch and we will see what we can do about implementing them for you.

https://www.kdab.com/declarative-widgets/

Declarative Widgets is a QML plugin that adds Qt Widgets support to QML的更多相关文章

  1. Qt Widgets、QML、Qt Quick的区别

    Qt Widgets.QML.Qt Quick的区别 简述 看了之前关于 QML 的一些介绍,很多人难免会有一些疑惑: Q1:QML 和 Qt Quick 之间有什么区别? Q2:QtQuick 1. ...

  2. QT5中的pro文件中为何要加入"QT += widgets"

    在pro文件里写"QT+=widgets"表示引入QtWidget这个module,qmake在生成makefile的时候,会设置好include path 和 lib path, ...

  3. 【QT相关】Qt Widgets Module

    Qt Widgets Module:提供了一些列UI元素. 使用: //头文件包含 #include <QtWidgets> //链接模式,在.pro文件中添加行: QT += widge ...

  4. 由基于qml,c++的串口调试工具浅谈qml与c++混合编程

    最近在做一个基于sim900 的串口通信工具,基于qml和c++来实现. 首先,对于串口,qt有自带的QSerialPort,可以实现同步,和异步通信,qt creator也有自带的例子,本例子是从其 ...

  5. Qt Quick编程(1)——QML的核心部分ECMAScript

    说道QML,不得不先说一下ECMAScript: ECMAScript语言的标准是由Netscape.Sun.微软.Borland等公司基于JavaScript和JScript锤炼.定义出来的. EC ...

  6. Qt4项目迁移到Qt5问题:greaterThan(QT_MAJOR_VERSION, 4): QT += widgets .

    文章来源:http://blog.csdn.net/ccf19881030/article/details/18220447 问题一:错误:C1083: 无法打开包括文件:"QApplica ...

  7. Qt界面UI之QML初见(学习笔记四)

    拖了大半年,今天终于有更新了...我自己都不好意思,最近太忙了! 今天讲一下:QML语法 一 概述 QML是一种专门用于构建用户界面的编程语言,它允许用户构建高性能,具有流畅特效的可视化应用程序,QM ...

  8. 【Qt官方例程学习笔记】Getting Started Programming with Qt Widgets

    创建一个QApplication对象,用于管理应用程序资源,它对于任何使用了Qt Widgets的程序都必要的.对于没有使用Qt Widgets 的GUI应用,可以使用QGuiApplication代 ...

  9. Qt在线讲座之QML脚本书写规范

    时间:2016年3月1日晚7:30 在线讲座:http://qtdream.com主页处就可以收看直播(详见主页提示) 參与对象:对Qt跨平台开发框架感兴趣的朋友们.当然了,假设你是大牛.也可以旁听一 ...

随机推荐

  1. Mahout的推荐系统

    Mahout的推荐系统 什么是推荐系统 为什使用推荐系统 推荐系统中的算法 什么是推荐系统 为什么使用推荐系统? 促进厂商商品销售,帮助用户找到想要的商品 推荐系统无处不在,体现在生活的各个方面 图书 ...

  2. 适合ASP.NET Web API使用的场景

    富客户端web应用程序:ASP.NET Web API适合大量使用AJAX调用的富客户端应用程序,如Silverlight应用程序,基于Adobe Flash的应用程序或单页应用程序(SPA)等. 本 ...

  3. 51Nod 1006 最长公共子序列Lcs问题 模板题

    给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). 比如两个串为:   abcicba abdkscab   ab是两个串的子序列,abc也是,abca也是,其中abca是这两个 ...

  4. spring之AOP(转)

    Spring之AOP篇: AOP框架是Spring的一个重要组成部分.但是Spring IOC 并不依赖于AOP,这就意味着你有权力选择是否使用AOP,AOP作为Spring IOC容器的一个补充,使 ...

  5. gdal读写图像分块处理

    转自赵文原文 gdal读写图像分块处理(精华版) Review: 用gdal,感觉还不如直接用C++底层函数对遥感数据进行处理.因为gdal进行太多封装,如果你仅仅只是Geotif等格式进行处理,IO ...

  6. 【iOS与EV3混合机器人编程系列之中的一个】iOS要干嘛?EV3能够更酷!

    乐高Mindstorm EV3智能机器人(下面简称EV3)自从在2013年的CES(Consumer Electronics Show美国消费电子展)上展出之后,就吸引了全球广大机器人爱好者的眼球!E ...

  7. svn: Can&#39;t convert string from &#39;UTF-8&#39; to native encoding 解决的方法

    今天在down代码时遇到了例如以下问题: [xxx@xxx ~]$ svn co https://xxxxxxxxxxxxx svn: Can't convert string from 'UTF-8 ...

  8. 通过NFS、FTP、HTTP三种方法安装Redhat Linux (高清版)

          本节教程讲述了通过在Red Hat Linux服务器端假设NSF Server来进行Linux系统安装的过程,并详细介绍了如何制作网络启动盘的细节.演示直观,讲解通俗易懂,特别适合初学者学 ...

  9. BZOJ4372: 烁烁的游戏(动态点分治)

    Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠.题意:给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠.烁烁他每次会跳到一个节点u,把周围与他距离不超过d的节点各吸引出w只皮皮 ...

  10. 全新linux中通过编译方式安装nginx

    先去官网下载linux.tar.gz包 http://nginx.org/en/download.html   传到linxu中 解压tar包 在软件包nginx-1.15.9目录下对NGINX进行配 ...