前言
参照QT项目做一个qml项目并不复杂,先从"在主框架上加一个页面"开始吧,方便初学者构建自己的qml项目。本人也有分享类似的QT项目博文(借鉴此博文的风格来分享):
一、创建一个qml项目






完成以上傻瓜式的操作之后,我们在qtcreator可以看到3个能打开的文件
.pro文件,与qt项目相比,有如下变化

main.cpp文件,与qt项目相比,有如下变化

qt的main(),是直接调用w.show();来显示界面,而qml的main(),需要通过以下5步来显示界面
- 初始化Qt GUI应用程序框架
- 创建QML引擎用于界面渲染
- 设置QML主界面文件路径(使用Qt资源系统)
- 添加对象创建失败的异常处理
- 异步加载QML界面文件
自带的main.qml

显示一个窗口标题为"hello word"的空白页面
二、增加一个登录页面LoginWindow



以上操作完成之后,在qtcreator中可以看到多出了一个内容为空白的LoginWindow.qml文件

这里提供一个登录页面的代码
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Window 2.12
Window {
id: loginWindow
width: 300
height: 200
title: "用户登录"
modality: Qt.ApplicationModal
flags: Qt.Dialog
Column {
anchors.centerIn: parent
spacing: 15
width: parent.width * 0.8
TextField {
id: username
width: parent.width
placeholderText: "用户名"
}
TextField {
id: password
width: parent.width
placeholderText: "密码"
echoMode: TextInput.Password
}
Row {
spacing: 10
anchors.horizontalCenter: parent.horizontalCenter
Button {
text: "登录"
onClicked: {
if(username.text === "admin" && password.text === "123456") {
console.log("登录成功")
loginWindow.close()//关闭自己的窗口
} else {
console.log("登录失败")
}
}
}
Button {
text: "退出"
onClicked: {
//退出方式一:强制关闭所有窗口,资源完全释放
Qt.quit()//强制终止整个Qt应用程序进程
//退出方式二:关闭两个窗口,资源有可能未完全释放
//loginWindow.close()//关闭自己的窗口
//mainWindow.close()//关闭主窗口
}
}
}
}
}
注意了,这里有一个坑,如果在刚创建项目时,你点击过运行项目。然后再添加LoginWindow.qml文件的话,会报以下错误:
qmlcache_loader.cpp:-1: error: undefined reference to `QmlCacheGeneratedCode::_0x5f__login_qml::qmlData'
这是qml的资源缓存没有包含新增加的login.qml导致,点击【构建】->【重新构建项目】之后,再点击运行项目就正常了(这个现象,你可以理解为,当你只修改.h头文件时,执行make,make会反馈"没有任何变化",清掉缓存,重新编译就正常了)。
三、main.qml加载登录页面LoginWindow
注意一点,以上增加LoginWindow.qml文件时,它与main.qml是在同一个目录下,方便引用,以下提供两种引用方式:
方式一:静态加载
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
id:mainWindow
width: 640
height: 480
visible: true
title: qsTr("Hello World")
//在窗口完成初始化后执行
Component.onCompleted: {
loginwin.visible = true
}
//登录窗口
LoginWindow{//文件名称:直接引用组件(静态加载)
id:loginwin
visible: false
}
}
方式二:动态加载
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
id:mainWindow
width: 640
height: 480
visible: true
title: qsTr("Hello World")
//在窗口完成初始化后执行
Component.onCompleted: {
var component = Qt.createComponent("LoginWindow.qml")
if (component.status === Component.Ready) {
var loginWindow = component.createObject(mainWindow)
loginWindow.show()
}
}
}
| 场景 | 推荐方式 |
|---|---|
| 始终显示的界面元素 | 静态加载 |
| 按需加载的页面 | 动态加载 |
| 资源密集型组件 | 动态加载 |
| 简单的子组件 | 静态加载 |
| 需要频繁切换的视图 | 动态加载 |
| 应用核心组件 | 静态加载 |
四、登录页面在后端实时验证信息
在【三、main.qml加载登录页面LoginWindow】中提供了在qml上静态方式校验用户名和密码的代码,这只是为了让【三】能正常编译,实际项目中,是要动态方式校验的,以下展示创建一个后端的CheckPwd类进行校验,并于页面互通。
1、创建新类



添加过程跟QT项目一样,
2、声明及定义页面调用的函数
创建完成之后,在头文件checkpwd.h中加入页面引入的函数,代码如下
#ifndef CHECKPWD_H
#define CHECKPWD_H
#include <QObject>
#include <QVariant> //新增头文件,前后端传参/结果
class CheckPwd : public QObject
{
Q_OBJECT
public:
explicit CheckPwd(QObject *parent = nullptr);
//声明给qml调用的函数
Q_INVOKABLE bool logincheck(const QVariant usrname,const QVariant passwd);
signals:
};
#endif // CHECKPWD_H
要点有两个:
- 使用Q_INVOKABLE宏声明为qml调用的函数;
- 使用QVariant传参或者返回结果给qml,QVariant是Qt框架中提供的通用数据类型容器。QVariant的作用是:MVD(Model-View-Delegate)模式中Model<->Delegate之间传递数据的容器载体。
==============以下是关系图,内容来源于AI=====================

以下函数的定义代码,把QVariant转换成QString之后,其他就是常规的QT代码开发(以下代码只做简单的判断逻辑)
bool CheckPwd::logincheck(const QVariant usrname,const QVariant passwd){
QString name=usrname.toString();
QString pwd=passwd.toString();
if(name== "admin" && pwd == "123456")
return true;
else
return false;
}
3、将C++类注册到QML系统
main.cpp中创建QML引擎并指定加载的qml页面之后才能显示该页面。qml页面要使用后端的C++类,也要进行注册,代码如下
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "checkpwd.h" //-------------新增的头文件
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);//创建Qt GUI应用程序实例,管理主事件循环和应用程序资源
QQmlApplicationEngine engine;//创建QML引擎,用于加载和运行QML界面文件
const QUrl url(QStringLiteral("qrc:/main.qml"));//定义QML主界面文件的路径(qrc资源系统中的main.qml)
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,//连接引擎的objectCreated信号
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection); //使用队列连接确保异步执行
//将C++类注册到QML系统
//CheckPwd的类的名称,必须与checkpwd.h类中的名称完全一致
//CheckPwdObj是自主命名(名称随意),在LoginWindow.qml页面中引用
//PasswordValidator引用的类型名称
qmlRegisterType<CheckPwd>("CheckPwdObj",1,0,"PasswordValidator");//-------新增的注册类
//加载以上指定的main.qml文件
engine.load(url);
return app.exec();
}
4、qml引入C++类的函数
在LoginWindow.qml中增加3个要素:
-
头部通过import进行引用:import CheckPwdObj 1.0,对应关系如下

-
类型引用
PasswordValidator { id: validator } -
C++函数引用
Button { text: "登录" onClicked: { var pwdck = validator.logincheck(username.text,password.text); if(pwdck===true){ console.log("登录成功") loginWindow.close()//关闭自己的窗口 } else { console.log("登录失败") } } }完成以上3步骤即可实现引用C++类函数,LoginWindow.qml完整代码如下
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Window 2.12import CheckPwdObj 1.0
Window {
id: loginWindow
width: 300
height: 200
title: "用户登录"
modality: Qt.ApplicationModal
flags: Qt.DialogPasswordValidator { id: validator } Column { anchors.centerIn: parent spacing: 15 width: parent.width * 0.8 TextField { id: username width: parent.width placeholderText: "用户名" } TextField { id: password width: parent.width placeholderText: "密码" echoMode: TextInput.Password } Row { spacing: 10 anchors.horizontalCenter: parent.horizontalCenter Button { text: "登录" onClicked: { var pwdck = validator.logincheck(username.text,password.text); if(pwdck===true){ console.log("登录成功") loginWindow.close()//关闭自己的窗口 } else { console.log("登录失败") } /*if(username.text === "admin" && password.text === "123456") { console.log("登录成功") loginWindow.close()//关闭自己的窗口 } else { console.log("登录失败") }*/ } } Button { text: "退出" onClicked: { //退出方式一:强制关闭所有窗口,资源完全释放 Qt.quit()//强制终止整个Qt应用程序进程 //退出方式二:关闭两个窗口,资源有可能未完全释放 //loginWindow.close()//关闭自己的窗口 //mainWindow.close()//关闭主窗口 } } } }}