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. DOM节点的创建、插入、删除、查找、替换

    在前端开发中,js与html联系最紧密的莫过于对DOM的操作了,本文为大家分享一些DOM节点的基本操作. 一.创建DOM节点 使用的命令是 var oDiv = document.createElem ...

  2. 120.VS调试技巧

    设置断点调试 在一行代码的左侧点击即可设置断点,按F5(调试->开始调试)即可运行到第一个端点处暂停 逐语句调试 按F11(调试->逐语句)即可开始一步一步执行 逐过程调试 按F10(调试 ...

  3. AC自动机 hdu2222

    #include <iostream> using namespace std; struct Node{ Node *next[]; Node* fail; int count; Nod ...

  4. OpenCV —— 图像处理

    使用图像结构中所定义的高层处理方法(图形和视觉范畴)来完成特定任务 平滑处理   cvSmooth 处理后图像与输入图像的大小相同(不用考虑边缘) 中值滤波  CV_MEDIAN 不支持 in pla ...

  5. lastlog---显示系统中所有用户最近一次登录信息。

    lastlog命令用于显示系统中所有用户最近一次登录信息. lastlog文件在每次有用户登录时被查询.可以使用lastlog命令检查某特定用户上次登录的时间,并格式化输出上次登录日志/var/log ...

  6. cocos2d-x 一些3效果的类及创建參数

    CCShaky3D::create(时间,晃动网格大小,晃动范围,Z轴是否晃动); //创建一个3D晃动的效果 CCShakyTiles3D::create(时间,晃动网格大小,晃动范围,Z轴是否晃动 ...

  7. Qt程序调试之Q_ASSERT断言(它是一个宏,接受布尔值,当其中的布尔值为真时,便什么也不做)

    在使用Qt开发大型软件时,难免要调试程序,以确保程序内的运算结果符合我们的预期.在不符合预期结果时,就直接将程序断下,以便我们修改. 这就用到了Qt中的调试断言 - Q_ASSERT. 用一个小例子来 ...

  8. 重建一些被PHP7废弃的函数,

    <?php if(!function_exists('ereg')) { function ereg($pattern, $subject, &$matches = []) { retu ...

  9. BZOJ 1009 GT考试 (AC自动机 + 矩阵乘法加速dp)

    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=1009 题意: 准考证号为\(n\)位数\(X_1X_2....X_n(0<=X_ ...

  10. Pairs Forming LCM

    题目: B - Pairs Forming LCM Time Limit:2000MS     Memory Limit:32768KB Description Find the result of ...