QML 和 Qt Quick拥有许多优点,但在某些情况下,也有一些点需要注意。本文将描述在Qt QML开发中的三个最佳实践方法。
一、自定义UI控制元素
现如今,流畅、现代、高端大气的UI是目前任何一个应用程序成功的关键,这一点,也是QML对设计人员或开发人员意义重大的地方。在实的应用程序开发中,Qt提供了最基本的UI控件,这些控件是创建一个流畅和现代的UI所必需的东西。因此,当在我们的实际工程开发中,应事先浏览一下Qt提供的控件,如果没有符合我们自己的需求效果的,我们才去创建自定义的控件。(如何创建自定义控件,后续小生会写一篇来描述其大致过程和思路)
我们可以通过以上“字母索引”寻找查看需要的QML类型。
二、将UI与逻辑分离
在我们实际应用程序开发过程中,为了创建可维护的应用程序,需要分离用户界面与业务逻辑。QML对UI的设计和开发有着较好的支持,主要有以下三点原因:
(1)QML属于声明性语言,它非常适合用于描述UI,因为UI元素之间往往呈现嵌套和级联的特性。
(2)在QML中,我们可以很容易使用JavaScript来响应事件。
(3)相对于C++来说,QML代码编写要简单许多。
C++作为一种强类型语言,最适合应用程序的逻辑开发。通常,这类代码会去执行复杂的计算或数据处理等任务,使用C++来实现会比QML更快。
综上,QML适合用于用户界面的开发,C++适合用于程序逻辑(大量计算和数据处理等)的开发,在实际使用中,我们可以按照这个规则进行开发。
三、在C++中与QML交互
Qt允许我们从C++操作QML,但是不建议这样做。为了解释原因,让我们看一个简单的例子。
(3-1)在C++中提取QML引用
这里假设我们正在开发一个设置页面,其QML代码如下:
import QtQuick 2.15
import QtQuick.Controls 2.15
Page {
Button {
text: qsTr("Restore")
}
}
这个时候,我们希望按钮在被单击时在C++中做一些事情。我们知道QML中的对象可以像在C++中一样发出更改信号,所以我们给按钮定义一个objectName,这样我们就可以从C++中找到QML中的按钮:
Button {
objectName: "restoreButton"
text: qsTr("Restore")
}
然后,在C++代码中,我们找到该对象并连接到它的更改信号:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QSettings>
class LogicHandle : public QObject
{
Q_OBJECT
public:
LogicHandle() {}
public slots:
void restoreDefaults() {
settings.setValue("loadLastProject", QVariant(false));
}
private:
QSettings settings;
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
LogicHandle backend;
QObject *rootObject = engine.rootObjects().first();
QObject *restoreDefaultsButton = rootObject->findChild<QObject*>("restoreButton");
QObject::connect(restoreDefaultsButton, SIGNAL(clicked()),
&backend, SLOT(restoreDefaults()));
return app.exec();
}
上述代码中,在C++中通过objectName
找到了QML中的按钮,然后使用connect()
函数进行按钮点击信号和槽函数的关联。那么,使用这种方式让QML与C++之间进行交互会有一个问题:
C++逻辑层依赖于QML表示层。
如果当我们改变了objectName,或者由于某些原因造成C++查找QML对象逻辑破坏,这时候我们需要修改的地方就变得非常的多(QML和C++都需要修改)且代码之间的变得高度耦合,这将是一件无趣且糟糕的事情!
因此,为了避免这种问题的出现,在QML与C++进行交互的时候,可以使用下文所述的方法:将C++类对象注册到QML中
。
(3-2)将C++类对象注册到QML中
重构QML比重构C++要容易得多,所以为了让维护变得轻松,我们应该尽量让C++类型不知道QML。这可以通过将对C++类型的引用“推”到QML中来实现:
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
LogicHandle backend;
QQmlApplicationEngine engine;
//将LogicHandle类的实例backend注册到QML上下文中
engine.rootContext()->setContextProperty("backend", &backend);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
然后在QML直接调用C++槽函数:
import QtQuick 2.15
import QtQuick.Controls 2.15
Page {
Button {
text: qsTr("Restore")
onClicked: backend.restoreDefaults()
}
}
在上述代码中,对于按钮的点击事件处理则放到了QML中,且在QML中通过注册的C++类访问到其槽函数。
使用这种方法,即使将来QML代码可能会需要重构,但是C++代码是不会受到任何影响的。
四、总结
本文似乎是更倾向于经验性总结的文章,描述了在QML开发中的三个最佳实践方式:
(1)在自定义QML控件之前我们应该做的事情——查找、检索、验证Qt官方提供的QML类型;
(2)UI和逻辑分离的开发方式:QML适合用于用户界面的开发,C++适合用于程序逻辑(大量计算和数据处理等)的开发;
(3)在C++中与QML交互,虽然该种方式不推荐使用,但也是值得了解的。推荐在QML中与C++交互
。
对于QML来说,经小生多个版本的使用对比得出一结论:强烈推荐使用Qt 5.13版本以上的QML,过低的QML版本就不要去用了。