Qt Quick provides an option for reading and writing SQLite databases using the module “QtQuick.LocalStorage”. These databases are user-specific and QML-specific, but accessible to all QML applications. They are stored in the Databases subdirectory of QQmlEngine::offlineStoragePath(), currently as SQLite databases. Database connections are automatically closed during Javascript garbage collection.
In this blog post I am going to demonstrate how we can write a simple user based login application using QML which uses LocalStorage to store the user credentials. This demo application has following features.
- New User Registration
- User Login
- Authentication and error handling
- Password retrieval
This application is spitted into multiple screens and uses StackView show the screens. Following are the list of screens which we will be using in this demo application.
- LogInPage
- RegisterScreen
- PasswordResetPage
- UserInfoPage
The icons which you can see in this application are created by using “Font Awesome” font. I have used the FontLoader from QML for this purpose. And this font object is used for all the Text items to show their respective icons using Unicode strings.
FontLoader { id: fontAwesome name: "fontawesome" source: "qrc:/fontawesome-webfont.ttf" }
In this application I have used a PopUp item to show all the warnings and error messages of this application. This popup is placed at the bottom of the screen. I have also introduced a Timer along this popup to close it automatically after 2 seconds.
//Popup to show messages or warnings on the bottom position of the screen Popup { id: popup property alias popMessage: message.text background: Rectangle { implicitWidth: rootWindow.width implicitHeight: 60 color: "#b44" } y: rootWindow.height modal: true focus: true closePolicy: Popup.CloseOnPressOutside Text { id: message anchors.centerIn: parent font.pointSize: 12 color: "#ffffff" } onOpened: popupClose.start() } // Popup will be closed automatically in 2 seconds after its opened Timer { id: popupClose interval: 1000 onTriggered: popup.close() }
I have used a JavScript file “backend.js” to validate the user credentials for login and registration.
In my “main.qml” file I have used a StackView as a container and it loads “LogInPage.qml” as the initial page. And here I also initializes the Database and create a table to store user details. Instance of this database will be used across the application to store/retrieve user data.
Following are the main operation which I am performing on the database.
- Create database and create a new table for user credentials
- Insert user credentials in database
- Retrieve password for the given user for authentication
- Retrieve password for the given user and hint for password retrieval
Below is my application’s main.qml file.
main.qml:
import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.1 import QtQuick.LocalStorage 2.0 import "backend.js" as Backend ApplicationWindow { id: rootWindow visible: true width: 420 height: 680 title: qsTr("Login Demo") flags: Qt.FramelessWindowHint property color backGroundColor : "#394454" property color mainAppColor: "#6fda9c" //"#FA6B65" property color mainTextCOlor: "#f0f0f0" property var dataBase FontLoader { id: fontAwesome name: "fontawesome" source: "qrc:/fontawesome-webfont.ttf" } // Main stackview StackView{ id: stackView focus: true anchors.fill: parent } // After loading show initial Login Page Component.onCompleted: { stackView.push("qrc:/LogInPage.qml") //initial page dataBase = userDataBase() console.log(dataBase.version) } //Popup to show messages or warnings on the bottom postion of the screen Popup { id: popup property alias popMessage: message.text background: Rectangle { implicitWidth: rootWindow.width implicitHeight: 60 color: "#b44" } y: rootWindow.height modal: true focus: true closePolicy: Popup.CloseOnPressOutside Text { id: message anchors.centerIn: parent font.pointSize: 12 color: "#ffffff" } onOpened: popupClose.start() } // Popup will be closed automatically in 2 seconds after its opened Timer { id: popupClose interval: 1000 onTriggered: popup.close() } // Create and initialize the database function userDataBase() { var db = LocalStorage.openDatabaseSync("UserLoginApp", "1.0", "Login example!", 1000000); db.transaction(function(tx) { tx.executeSql('CREATE TABLE IF NOT EXISTS UserDetails(username TEXT, password TEXT, hint TEXT)'); }) return db; } // Register New user function registerNewUser(uname, pword, pword2, hint) { var ret = Backend.validateRegisterCredentials(uname, pword, pword2, hint) var message = "" switch(ret) { case 0: message = "Valid details!" break; case 1: message = "Missing credentials!" break; case 2: message = "Password does not match!" break; } if(0 !== ret) { popup.popMessage = message popup.open() return } dataBase.transaction(function(tx) { var results = tx.executeSql('SELECT password FROM UserDetails WHERE username=?;', uname); if(results.rows.length !== 0) { popup.popMessage = "User already exist!" popup.open() return } tx.executeSql('INSERT INTO UserDetails VALUES(?, ?, ?)', [ uname, pword, hint ]); showUserInfo(uname) // goto user info page }) } // Login users function loginUser(uname, pword) { var ret = Backend.validateUserCredentials(uname, pword) var message = "" if(ret) { message = "Missing credentials!" popup.popMessage = message popup.open() return } dataBase.transaction(function(tx) { var results = tx.executeSql('SELECT password FROM UserDetails WHERE username=?;', uname); if(results.rows.length === 0) { message = "User not registered!" popup.popMessage = message popup.open() } else if(results.rows.item(0).password !== pword) { message = "Invalid credentials!" popup.popMessage = message popup.open() } else { console.log("Login Success!") showUserInfo(uname) } }) } // Retrieve password using password hint function retrievePassword(uname, phint) { var ret = Backend.validateUserCredentials(uname, phint) var message = "" var pword = "" if(ret) { message = "Missing credentials!" popup.popMessage = message popup.open() return "" } dataBase.transaction(function(tx) { var results = tx.executeSql('SELECT password FROM UserDetails WHERE username=? AND hint=?;', [uname, phint]); if(results.rows.length === 0) { message = "User not found!" popup.popMessage = message popup.open() } else { pword = results.rows.item(0).password } }) return pword } // Show UserInfo page function showUserInfo(uname) { stackView.replace("qrc:/UserInfoPage.qml", {"userName": uname}) } // Logout and show login page function logoutSession() { stackView.replace("qrc:/LogInPage.qml") } // Show Password reset page function forgotPassword() { stackView.replace("qrc:/PasswordResetPage.qml") } }
Below you can see the screenshots from this application.
Source code:
Leave a comment