Qt6 QML Book/Qt for MCUs/与C++的集成

Integrating with C++

与C++的集成

The C++

In order to demonstrate the connection between C++ and QML in Qt for MCUs, we will create a simple Counter singleton holding an integer value. Notice that we start from a struct and not a class. This is common practice in Qt Quick Ultralite.

为了演示MCUs在Qt中C++与QML之间的连接,我们将创建一个简单的计数器Counter,它包含一个整数值。请注意,我们从结构体而不是类开始。这是Qt Quick Ultralite中的常见做法。

The singleton will be used from a small UI as shown below.

单例将从如下所示的小UI中使用。

The Counter struct provides a property, value, as well as methods for changing the value, increase and decrease, as well as a reset method. It also provides a signal, hasBeenReset.

计数器Counter结构体提供了一个属性、值,以及更改值、增加和减少的方法,以及重置方法。它还提供了一个信号hasBeenReset。

#ifndef COUNTER_H
#define COUNTER_H

#include <qul/singleton.h>
#include <qul/property.h>
#include <qul/signal.h>

class Counter : public Qul::Singleton<Counter>
{
public:
    Counter();

    Qul::Property<int> value;

    void increase();
    void decrease();

    void reset();
    Qul::Signal<void(void)> hasBeenReset;

};

#endif // COUNTER_H

Coming from Qt, this looks odd. This is where Qt for MCUs shows the main differences. There is no QObject base class or Q_OBJECT macro, instead a new set of classes from the Qul is used. In this particular case, the base class is the Qul::Singleton class, creating a globally accessible singleton in the QML world. We also use the Qul::Signal class to create a signal and the Qul::Property class to create a property. All public, non-overloaded member functions are exposed to QML automatically.

来自Qt,这看起来很奇怪。这就是Qt for MCU显示主要差异的地方。没有QObject基类或Q_OBJECT宏,而是使用了来自Qul的一组新类。在这种特殊情况下,基类是Qul::Singleton类,它在QML世界中创建了一个全局可访问的Singleton。我们还使用Qul::Signal类创建信号,使用Qul::Property类创建属性。所有公共的、非重载的成员函数都会自动公开给QML。

TIP

To create an element that can be instatiated from QML, instead of a singleton, use the Qul::Object base class.

要创建可以从QML(而不是单例)使用的元素,请使用Qul::Object基类。

The struct is then exposed to QML using the CMake macro qul_target_generate_interfaces. Below you can see the CMakeLists.txt, based on the file generated by Qt Creator, with the counter.h and counter.cpp files added.

然后使用CMake宏qul_target_generate_interfaces将结构体暴露给QML。下面你可以看到CMakeLists.txt,基于Qt Creator生成的文件,带有counter.hcounter.cpp文件添加。

qul_target_generate_interfaces(cppintegration counter.h)

Now, let's continue with the implementation of the Counter struct. First up, for the value property, we use the value and setValue functions to access and modify the actual value. In our case, the property holds and int, but just as for the ordinary QML engine, types are mapped between C++ and QML .

​现在,让我们继续计数器Counter结构体的实现。首先,对于value属性,我们使用value和setValue函数来访问和修改实际值。在我们的例子中,属性保持和int,但是对于普通的QML引擎,类型是在C++和QML之间映射的。

This is used in the constructor, shown below, that sets the initial value to zero.

这在构造函数中使用,如下所示,该构造函数将初始值设置为零。

Counter::Counter()
{
    value.setValue(0);
}

The increase and decrease functions look similar. They use the getter and setter instead of interacting directly with the value.

增加和减少函数看起来相似。他们使用getter和setter,而不是直接与值交互。

void Counter::increase()
{
    value.setValue(value.value()+1);
}

void Counter::decrease()
{
    value.setValue(value.value()-1);
}

Counter also has a signal. The signal is represented by the Qul::Signal instance named hasReset. The signal takes a function signature as template argument, so to create a signal carrying an integer, create a Qul::Signal<void(int)>. In this case, the signal does not carry any values, so it is defined as a void(void). To emit the signal, we simply call it as if it was an ordiary function as shown in the reset function below.

计数器Counter也有信号。信号由名为hasReset的Qul::Signal实例表示。信号将函数签名作为模板参数,因此要创建一个带有整数的信号,请创建一个Qul::Signal<void(int)>。在这种情况下,信号不携带任何值,因此它被定义为void(void)。为了发出信号,我们简单地将其称为一个普通函数,如下面的reset函数所示。

void Counter::reset()
{
    std::cout << "Resetting from " << value.value() << std::endl;
    value.setValue(0);
    hasReset();
}

The QML

The QML code produces the simple user interface shown below.

QML代码生成如下所示的简单用户界面。

We will look at the UI in three parts. First, the basic structure, and bindings to Counter.value:

我们将从三个部分来看UI。首先是基本结构,以及绑定Counter.value

import QtQuick

Rectangle {
    width: 480
    height: 272

    Column {
        // Left buttons goes here
    }

    Column {
        // Right buttons goes here
    }

    Text {
        anchors.centerIn: parent
        text: Counter.value;
    }
}

As you can tell, the Text element's text property is bound to the Counter.value as in all QML.

可以看出,文本元素的Text属性绑定到Counter.value

Now, let's look at the left side buttons. These are used to invoke the C++ methods provided via the Counter singleton. The PlainButton is a QML element that we use to create these simple buttons. It let's you set the text, background color and a handler for the clicked signal. As you can tell, each button calls the corresponding method on the Counter singleton.

现在,让我们看看左边的按钮。这些被用来调用通过C++计数器单独提供的C++方法。PlainButton是我们用来创建这些简单按钮的QML元素。它让你为点击的信号设置文本、背景色和处理程序。正如您所知,每个按钮都会在计数器单例上调用相应的方法。

Column {
    x: 10
    y: 10
    spacing: 5
    PlainButton {
        text: "+"
        onClicked: Counter.increase()
    }
    PlainButton {
        text: "reset"
        onClicked: Counter.reset()
    }
    PlainButton {
        text: "-"
        onClicked: Counter.decrease()
    }
}

The buttons on the right modify the Counter.value directly from QML. This is possible to do, but invisible to C++. There is no simple way for C++ to monitor if a property has changed, so if a C++ reaction is needed, it is recommended to use a setter method, rather than directly modifying the property value.

右边的按钮可以修改Counter.value直接来自QML。这是可能的,但是C++是不可见的。没有一种简单的方法来监视C++是否改变了一个属性,因此如果需要一个C++反应,建议使用setter方法,而不是直接修改属性值。

Column {
    x: 350
    y: 10
    spacing: 5
    PlainButton {
        color: "orange"
        text: "++"
        onClicked: Counter.value += 5;
    }
    PlainButton {
        color: "orange"
        text: "100"
        onClicked: Counter.value = 100;
    }
    PlainButton {
        color: "orange"
        text: "--"
        onClicked: Counter.value -= 5;
    }
}

This shows how to provide a singleton from C++ and how to make function calls, emit signals, and share state (properties) between C++ and QML.

这说明了如何从C++提供单例,以及如何在C++和QML之间进行函数调用、发出信号、共享状态(属性)。

Revisiting the CMake file

重新访问CMake文件

The CMakeLists.txt file may look familiar to you, but there are some tips and tricks that we need to discuss.

CMakeLists.txt文件可能看起来很熟悉,但有一些技巧和窍门我们需要讨论。

First of all, in order to expose a C++ class to QML, use the qul_target_generate_interfaces, e.g:

首先,为了将C++类暴露给QML,使用qul_target_generate_interfaces接口,例如:

qul_target_generate_interfaces(cppintegration counter.h)

The other half, the QML files, are added using the qul_target_qml_sources macro. If you have multiple QML files, simply list them one by one as shown below:

另一半QML文件是使用qul_target_qml_sources宏添加的。如果您有多个QML文件,只需按如下所示逐个列出即可:

qul_target_qml_sources(cppintegration cppintegration.qml PlainButton.qml)

Another interesting aspect is that we are building a C++ project without writing a main function. This is taken care of by the app_target_default_main macro that adds a reference main implementation to the project. You can of course replace this with a custom main function if you need more control.

另一个有趣的方面是,我们正在构建一个C++项目,而不写一个main函数。这由app_target_default_main宏负责,该宏向项目中添加了一个参考主实现。当然,如果需要更多控制,可以用自定义的main函数替换它。

app_target_default_main(cppintegration cppintegration)

Finally, the libraries linked to are not the standard Qt ones, but the Qul:: ones, e.g:

最后,链接到的库不是标准的Qt库,而是Qul::,例如:

target_link_libraries(cppintegration
    Qul::QuickUltralite
    Qul::QuickUltralitePlatform)

Links

链接

Further reading at qt.io:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值