【qml入门】在qml项目上加入用户登录qml页面(包含源码)
前言
参照QT项目做一个qml项目并不复杂,先从“在主框架上加一个页面”开始吧,方便初学者构建自己的qml项目。本人也有分享类似的QT项目博文(借鉴此博文的风格来分享):
【QT入门到晋级】拿别人的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.12Window {id: loginWindowwidth: 300height: 200title: "用户登录"modality: Qt.ApplicationModalflags: Qt.DialogColumn {anchors.centerIn: parentspacing: 15width: parent.width * 0.8TextField {id: usernamewidth: parent.widthplaceholderText: "用户名"}TextField {id: passwordwidth: parent.widthplaceholderText: "密码"echoMode: TextInput.Password}Row {spacing: 10anchors.horizontalCenter: parent.horizontalCenterButton {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.12Window {id:mainWindowwidth: 640height: 480visible: truetitle: qsTr("Hello World")//在窗口完成初始化后执行Component.onCompleted: {loginwin.visible = true}//登录窗口LoginWindow{//文件名称:直接引用组件(静态加载)id:loginwinvisible: false}
}
方式二:动态加载
import QtQuick 2.12
import QtQuick.Window 2.12Window {id:mainWindowwidth: 640height: 480visible: truetitle: 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;elsereturn 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: loginWindowwidth: 300height: 200title: "用户登录"modality: Qt.ApplicationModalflags: Qt.DialogPasswordValidator {id: validator}Column {anchors.centerIn: parentspacing: 15width: parent.width * 0.8TextField {id: usernamewidth: parent.widthplaceholderText: "用户名"}TextField {id: passwordwidth: parent.widthplaceholderText: "密码"echoMode: TextInput.Password}Row {spacing: 10anchors.horizontalCenter: parent.horizontalCenterButton {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()//关闭主窗口}}}}
}