1. 搭建系统
前面介绍了利用Qt+mysql搭建系统,本文讲介绍利用QML+mysql框架搭建一个小型系统。系统有以下特点:
- 系统是异步处理,mysql操作在线程里,操作完后数据通过信号槽发送到页面展示
- 查询mysql百万数量级数据表,用TableView+自定义数据模型轻松展示
- QTableView加载自定义模型,展示百万条数据消耗比较少的内存(530MB左右内存)
1.1. 技术栈如下:
开发语言:C++
数据库:MySQL5.7
开发框架:Qt5.12
开发工具:Qt Creator 4.9.0 (Enterprise)
其他工具1:XAPP(集成apach和mysql)
其他工具2:Navicat 11.0.10
1.2. 系统的详细开发过程
1.2.1. 用Qt Creator 4.9.0创建项目
项目创建完成后运行如下图:
1.2.2. 创建资源文件
由于项目中需要用到图片,我们创建一个资源文件resource.qrc,如图
把图片添加到resource.qrc里面,
1.2.3. 添加项目需要的模块
由于系统里面需要访问mysql数据库,并需要显示图表,所以在工程文件pro里面需要添加模块。创建好项目后自带模块,如图:
添加数据库和图表模块,如图:
1.2.4. 创建项目文件和目录
项目目录结构如上图,文件和目录的说明如下:
**
CDataclass:Qt类,数据异步处理类,由页面触发,处理完成后通过信号发送处理结果给页面
Common:Qt类,单例类,注册元类型和初始化mysql数据库
CWorker:Qt类,在线程中运行,主要查询mysql
main.cpp:项目入口文件
login:QML文件,登录页面
MyProgressBar:QML文件,进度条
Threejs3DPage:QML文件,展示threejs功能
model3d:js文件,初始化threejs
ChartsPage:QML文件,展示Qt画图功能
LogPage:QML文件,日志页面
UserPage:QML文件,用户页面,用TableView展示百万数量及表格
main:QML文件,登录成功后跳转到的主页面,展示功能页面
**
下面我们来创建上面的列表类文件
1.2.4.1. 创建Common类
Common类是一个单例类,有以下功能:
- 负责初始化数据库
- 获取数据库实例
- 定义控件缩放函数
- 定义系统常量和数据结构
头文件
//定义数据处理结果状态码
enum RET_CODE {
RET_OK = 0, //成功
RET_DBERR_OPEN, //数据库查询打开失败
RET_DBERR_RUN, //SQL执行失败
RET_PARAMERR, //参数错误
RET_NOFUNC, //方法不存在
RET_NOWORKTYPE //处理类型不存在
};
//定义数据处理结果状态码对应的信息
extern QStringList RET_MSG;
//定义命令参数数据结构
typedef struct _CmdData {
QString func; //处理的函数名称
QMap<QString, QString> params; //参数列表
} CmdData;
//定义命令处理结果数据结构
typedef struct _RstData {
int retCode; //结果状态码
QString func; //处理的函数名称
QString msg; //结果信息
QVector< QVector<QString> > result; //处理结果数据,二位数组
} RstData;
// MYSQL数据库信息
typedef struct _MysqlInfo
{
int port;
QString host, name, usr, pwd;
} MysqlInfo;
class MyCommon : public QWidget
{
Q_OBJECT
public:
explicit MyCommon(QWidget *parent = nullptr);
~MyCommon();
static MyCommon *instance(); //定义单例类
static void InitDataBase(const MysqlInfo &dbInfo); //初始化数据库
static QSqlDatabase GetNewDatabase() //获取数据库实例
{
QSqlDatabase newDb;
if (QSqlDatabase::contains("mysql_1"))
{
int n = QSqlDatabase::connectionNames().size();
newDb = QSqlDatabase::cloneDatabase(mDatabase, QString("mysql_%1").arg(n));
}
else
{
newDb = QSqlDatabase::cloneDatabase(mDatabase, "mysql_1");
}
return newDb;
}
signals:
public slots:
private:
static float xScal, yScal;
static QRect mScreenRect;
static MyCommon *self;//单例模式
static QSqlDatabase mDatabase;
static MysqlInfo mDbInfo;
};
源文件
MyCommon *MyCommon::self = nullptr;
QSqlDatabase MyCommon::mDatabase;
MysqlInfo MyCommon::mDbInfo;
QStringList RET_MSG = QStringList() << "成功" << "数据库查询打开失败" << "SQL执行失败" << "参数错误" << "方法不存在" << "处理类型不存在";
MyCommon::MyCommon(QObject *parent) : QObject(parent)
{
//注册元类型:主要是在定义信号槽的时候,传递的参数类型不一定是QT所识别的
qRegisterMetaType<CmdData>("CmdData");
qRegisterMetaType<RstData>("RstData");
mDbInfo.port = 3306;
mDbInfo.host = "127.0.0.1";
mDbInfo.name = "test_db";
mDbInfo.usr = "root";
mDbInfo.pwd = "123456";
InitDataBase(mDbInfo);
}
MyCommon::~MyCommon()
{
if (self != nullptr)
{
delete self;
}
}
void MyCommon::InitDataBase(const MysqlInfo &dbInfo)
{
mDatabase = QSqlDatabase::addDatabase("QMYSQL");
mDatabase.setHostName(dbInfo.host);//设置主机地址
mDatabase.setPort(dbInfo.port); //设置端口
mDatabase.setDatabaseName(dbInfo.name); //设置数据库名称
mDatabase.setUserName(dbInfo.usr); //设置用户名
mDatabase.setPassword(dbInfo.pwd); //设置密码
}
1.2.4.2. 创建CDataClass类
CDataClass是Qt类,有以下功能:
- 定义多线程,把数据处理移到线程里面处理
- 定义命令处理函数,供页面调用
- 定义发送命令信号,把从页面接受的命令发送到线程里面
- 定义接收处理结果槽函数
- 定义发送处理结果信号,把从数据返回到页面
头文件
class CDataClass;
typedef void (CDataClass::*PTRFUN)(const CmdData &argcs); //函数指针,用于分发命令
class CDataClass : public QObject
{
Q_OBJECT
public:
explicit CDataClass(QObject *parent = nullptr);
~CDataClass()
{
mWorkerThread.quit();
mWorkerThread.wait();
}
Q_INVOKABLE void handleCmdDataQML(const QString &func, const QStringList &keys,
const QStringList &values); //供页面调用的命令函数,分发到具体的处理函数
Q_INVOKABLE void checkUserPwd(const CmdData &argcs); //验证输入的用户名和密码
Q_INVOKABLE void getUsersData(const CmdData &argcs); //查询用户信息
Q_INVOKABLE void addUsersData(const CmdData &argcs); //增加用户信息
Q_INVOKABLE void editUsersData(const CmdData &argcs); //编辑用户信息
Q_INVOKABLE void getLogsData(const CmdData &argcs); //查询日志信息
signals:
//把页面接受的命令,发送到线程里面的槽函数
void operate(const int type, const QString &func, const QString &cmd);
//把线程里面的处理结果返回给页面
void operateResult(const RstData &rstData);
//登录结果信号
void signalLoginResult(const bool &result);
//消息显示信号
void signalMeaasge(const QString &msg);
public slots:
void handleResults(const RstData &rstData); //接受线程里面处理结果
private:
QThread mWorkerThread; //定义处理线程
QMap<QString, PTRFUN> mFuncMap; //定义命令处理函数映射关系
};
源文件
CDataClass::CDataClass(QObject *parent) : QObject(parent)
{
Worker *worker = new Worker; //定义数据处理类
worker->moveToThread(&mWorkerThread); //把数据处理类移到线程
connect(&mWorkerThread, &QThread::finished, worker, &QObject::deleteLater);
//定义信号槽,把命令发送到线程里面的槽函数
connect(this, &CDataClass::operate, worker, &Worker::doWork);
//定义信号槽,接收线程里面发送的结果
connect(worker, &Worker::resultReady, this, &CDataClass::handleResults);
mWorkerThread.start(); //开启线程
//初始化命令处理函数映射关系
mFuncMap["checkUserPwd"] = &CDataClass::checkUserPwd;
mFuncMap["getUsersData"] = &CDataClass::getUsersData;
mFuncMap["addUsersData"] = &CDataClass::addUsersData;
mFuncMap["editUsersData"] = &CDataClass::editUsersData;
mFuncMap["getLogsData"] = &CDataClass::getLogsData;
}
void CDataClass::handleResults(const RstData &rstData)
{
if (rstData.func == "checkUserPwd")
{
if (rstData.result.size() > 0)
{
emit signalLoginResult(true);
}
else
{
emit signalLoginResult(false);
}
}
else
{
emit operateResult(rstData);
}
}
void CDataClass::handleCmdDataQML(const QString &func, const QStringList &keys,
const QStringList &values)
{
qDebug() << "[CDataClass::handleCmdDataQML]" << func << keys << values;
CmdData argcs;
argcs.func = func;
if (keys.size() != values.size())
{
return;
}
for (int var = 0; var < keys.size(); ++var)
{
argcs.params[keys[var]] = values[var];
}
handleCmdData(argcs);
}
void CDataClass::handleCmdData(const CmdData &argcs)
{
RstData rstData;
if (!mFuncMap.contains(argcs.func))
{
rstData.retCode = RET_NOFUNC;
rstData.msg = RET_MSG[rstData.retCode];
rstData.func = argcs.func;
emit signalMeaasge(rstData.msg);
return;
}
(this->*mFuncMap[argcs.func])(argcs);
}
1.2.4.3. 创建CWorker类
CWorker是Qt类,有以下功能:
- 定义mysql操作函数
- 定义接收页面命令槽函数
- 定义发送处理结果信号,把从数据返回到页面
//定义线程里面支持的处理数据的操作
enum WORK_TYPE {
WORK_DB_QUERY = 0, //数据库查询
WORK_DB_RUN //数据库更新(增、删、改)
};
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = nullptr);
void testAddData1(); //测试函数
signals:
void resultReady(const RstData &rstData); //返回处理结果信号
public slots:
void doWork(const int type, const QString &func, const QString &cmd); //接收页面命令槽函数
private:
QSqlDatabase mDatabase; //数据库操作对象
int RunSql(const QString &sqlStr); //执行sql语句,写入接口
int RunSql(const QString &prepare, const QMap<QString, QVariant> &values);
int RunSqlColRow(const QString &sqlStr, QVector< QVector<QString> > &result); //执行sql语句,查询接口, 返回二维数组[列][行]
int RunSqlRowCol(const QString &sqlStr, QVector< QVector<QString> > &result); //执行sql语句,查询接口, 返回二维数组[行][列]
};
1.2.4.4. 创建main.cpp
main.cpp是项目的入口文件,有以下功能:
- 注册数据类型,供信号槽传输
- 初始化单例类
- 在qml中设置属性,用于绑定信号槽,便于qml和c++通讯
- 加载入口qml文件
源文件
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
qmlRegisterType<CDataClass>("dataclass", 1, 0, "CDataClass"); // 注册类,Q_INVOKABLE方式调用c++里面函数
qmlRegisterType<UserTableModel>("UserTableModel", 0, 1, "UserTableModel");
qmlRegisterType<LogTableModel>("LogTableModel", 0, 1, "LogTableModel");
qmlRegisterType<MyTreeModel>("MyTreeModel", 0, 1, "MyTreeModel");
MyCommon::instance(); //初始化单例类
QQmlApplicationEngine engine;
CDataClass dataClass;
engine.rootContext()->setContextProperty("datacls", &dataClass); // 设置属性,接收c++对象信号
engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
1.2.4.5. 创建Login.qml页面
import QtQuick 2.11
import QtQuick.Window 2.11
import QtQuick.Controls 1.4
Rectangle {
id: loginpage
visible: true
width: 960
height: 540
z: 1000 //设置层级,盖住下层页面
property string username: 'admin'
property string password: 'admin'
signal signalStartProgressBar()
signal signalShowMsg(string title, string text)
Rectangle {
id: rectangle
x: 230
y: 100
width: 500
height: 340
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
border.color: "lightgray"
Text {
id: text1
x: 201
y: 8
width: 99
height: 29
text: qsTr("登录页面")
anchors.horizontalCenter: parent.horizontalCenter
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 22
}
Text {
id: text2
x: 78
y: 68
width: 67
height: 27
text: qsTr("账号:")
horizontalAlignment: Text.AlignRight
verticalAlignment: Text.AlignVCenter
font.pixelSize: 16
}
Text {
id: text3
x: 78
y: 107
width: 67
height: 27
text: qsTr("密码:")
horizontalAlignment: Text.AlignRight
font.pixelSize: 16
verticalAlignment: Text.AlignVCenter
}
TextInput {
id: textInputUser
x: 158
y: 68
width: 271
height: 27
font.pixelSize: 24
passwordCharacter: ""
readOnly: false
horizontalAlignment: Text.AlignLeft
cursorVisible: false
echoMode: TextInput.Normal
text: loginpage.username
BorderImage {
anchors.rightMargin: 0
anchors.leftMargin: -3
anchors.bottomMargin: 0
anchors.topMargin: 0
anchors.fill: parent
source: "qrc:/qml/images/blackborder.png"
border.left: 1; border.top: 1
border.right: 1; border.bottom: 1
}
}
TextInput {
id: textInputPwd
x: 158
y: 107
width: 271
height: 27
font.pixelSize: 24
passwordCharacter: ""
echoMode: TextInput.Password
text: loginpage.password
cursorVisible: false
horizontalAlignment: Text.AlignLeft
readOnly: false
BorderImage {
anchors.leftMargin: -3
anchors.fill: parent
border.top: 1
border.left: 1
source: "qrc:/qml/images/blackborder.png"
border.right: 1
border.bottom: 1
}
}
Button {
id: button
x: 174
y: 196
width: 152
height: 34
text: qsTr("登录")
anchors.horizontalCenter: parent.horizontalCenter
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: {
loginpage.username = textInputUser.text;
loginpage.password = textInputPwd.text;
if (loginpage.username == '' || loginpage.password == '') {
loginpage.signalShowMsg('提示', '请输入账号和密码!')
return;
}
datacls.handleCmdDataQML('checkUserPwd', ['user','pwd'], [loginpage.username, loginpage.password]);
loginpage.signalStartProgressBar();
}
}
}
}
Component.onCompleted: {
}
}
1.2.4.6. 创建MyProgressBar.qml进度条
import QtQuick 2.11
import QtQuick.Window 2.11
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.0
Rectangle {
id: rectangle
visible: true
width: 960
height: 540
color: "#00000000"
opacity: 1
function setProgressValue(value) {
progressBar.value = value;
}
function start() {
progressBar.value = 0;
timeid.start()
}
ProgressBar {
id: progressBar
width: 325
height: 23
opacity: 1
//indeterminate: true
maximumValue: 100
value: 0
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
Timer {
id: timeid
interval: 1000;
repeat: true;
running: false;
onTriggered: {console.log(progressBar.value)
if (progressBar.value < 100) {
progressBar.value += 1;
} else if (progressBar.value >= 100) {
progressBar.value = 100;
rectangle.visible = false;
timeid.stop();
}
}
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
}
}
1.2.4.7. 创建Threejs3DPage.qml
Threejs3DPage页面展示threejs功能,配合model3d.js使用
import QtQuick 2.11
import QtCanvas3D 1.0
import "model3d.js" as GLCode
Rectangle {
id: model3d
visible: true
width: 960
height: 540
border.color: mainwld.rectBdColor
Canvas3D {
id: cube
state: "image6"
property color backgroundColor: "#FCFCFC"
anchors.fill: parent
onBackgroundColorChanged: { GLCode.setBackgroundColor(cube.backgroundColor); }
onInitializeGL: {
GLCode.initializeGL(cube);
}
onPaintGL: {//页面循环执行
GLCode.paintGL(cube);
}
onResizeGL: {
GLCode.resizeGL(cube);
}
}
}
model3d.js文件
Qt.include("threejs/three.js")
var camera, scene, renderer, light;
var cube;
function initThree(canvas) {
renderer = new THREE.Canvas3DRenderer(
{ canvas: canvas, antialias: true, devicePixelRatio: canvas.devicePixelRatio });
renderer.setPixelRatio(canvas.devicePixelRatio);
renderer.setSize(canvas.width, canvas.height);
setBackgroundColor(canvas.backgroundColor);
}
function initCamera(canvas) {
camera = new THREE.PerspectiveCamera( 75, canvas.innerWidth/canvas.innerHeight, 0.1, 1000 );
camera.position.z = 5;
}
function initScene() {
scene = new THREE.Scene();
}
function initLight() {
light = new THREE.DirectionalLight(0xFF0000, 1.0, 0);
light.position.set(100, 100, 200);
scene.add(light);
}
function initObject() {
var geometry = new THREE.BoxGeometry(3,3,3);
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
cube = new THREE.Mesh( geometry, material );
scene.add( cube );
}
function animate() {
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
//cube.rotation.z += 0.01;
}
function initializeGL(canvas) {
initThree(canvas);
initCamera(canvas);
initScene();
initLight();
initObject();
}
function setBackgroundColor(backgroundColor) {
var str = ""+backgroundColor;
var color = parseInt(str.substring(1), 16);
if (renderer)
renderer.setClearColor(color);
}
function resizeGL(canvas) {
if (camera === undefined) return;
camera.aspect = canvas.width / canvas.height;
camera.updateProjectionMatrix();
renderer.setPixelRatio(canvas.devicePixelRatio);
renderer.setSize(canvas.width, canvas.height);
}
function paintGL(canvas) { //qml文件自动调用,刷新页面
animate();
renderer.render(scene, camera);
}
1.2.4.8. 创建UserPage.qml页面
UserPage是QML文件,该文件展示QML表格功能,用TableView和自定义模型实现
class UserTableModel : public QAbstractTableModel
{
Q_OBJECT
public:
UserTableModel(QObject *parent = nullptr)
{
mRoleList = QStringList() << "Id" << "Name" << "Nick" << "Mobile"
<< "Email" << "Depart" << "Post" << "Status";
mHandle = new CDataClass(parent);
connect(mHandle, &CDataClass::operateResult, this, &UserTableModel::handleResults);
}
~UserTableModel() override
{
if (mHandle != nullptr)
{
delete mHandle;
}
}
int rowCount(const QModelIndex & = QModelIndex()) const override
{
return mRow;
}
int columnCount(const QModelIndex & = QModelIndex()) const override
{
return mColumn;
}
QVariant data(const QModelIndex &index, int role) const override
{
if (mResult.size() > index.row())
{
if (mResult[0].size() > role)
{
return mResult[index.row()][role];
}
}
qDebug() << role << index.column() << index.row();
return "";
}
QHash<int, QByteArray> roleNames() const override
{
QHash<int, QByteArray> roleHash;
for (int var = 0; var < mRoleList.size(); ++var)
{
roleHash[var] = mRoleList[var].toUtf8();
}
qDebug() << roleHash;
return roleHash;
}
Q_INVOKABLE void clearData()
{
mResult.clear();
mRow = 0;
mColumn = 0;
}
Q_INVOKABLE void getData(const QStringList &keys, const QStringList &values)
{
qDebug() << "[UserTableModel::getData]" << keys << values;
CmdData argcs;
argcs.func = "getUsersData";
if (keys.size() != values.size())
{
return;
}
for (int var = 0; var < keys.size(); ++var)
{
argcs.params[keys[var]] = values[var];
}
mHandle->handleCmdData(argcs);
}
signals:
void signalRecvhandleResult();
public slots:
void handleResults(const RstData &rstData)
{
emit signalRecvhandleResult();
beginResetModel(); // 开始刷新模型
if (rstData.retCode == 0)
{
mResult = rstData.result;
mRow = mResult.size();
qDebug() << "mRow = " << mRow;
mColumn = mRow > 0 ? mResult[0].size() : 0;
qDebug() << "mColumn = " << mColumn;
}
else
{
clearData();
}
endResetModel(); // 结束刷新模型
}
private:
int mRow;
int mColumn;
QStringList mRoleList;
QVector< QVector<QString> > mResult;
CDataClass *mHandle;
};
QML文件
import QtQuick 2.11
import QtQuick.Window 2.11
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.0
import UserTableModel 0.1
Rectangle {
id: rectangle
visible: true
width: 960
height: 540
border.color: mainwld.rectBdColor
function clearTableData() {
tablemodelid.clearData();
}
Column {
id: column
spacing: mainwld.space
anchors.fill: parent
anchors.rightMargin: mainwld.space
anchors.leftMargin: mainwld.space
anchors.bottomMargin: mainwld.space
anchors.topMargin: mainwld.space
Rectangle {
id: recthead
//border.color: mainwld.rectBdColor
width: parent.width
height: 40
Rectangle {
border.color: mainwld.rectBdColor
width: 180
height: 30
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
TextInput {
id: textInput
font.pointSize: 20
anchors.fill: parent
onEditingFinished: {
if (textInput.focus) {
tablemodelid.getData(['search'], [textInput.text]);
progressBar.visible = true;
progressBar.start();
}
textInput.focus = false;
}
}
}
}
Rectangle {
id: rectcontent
y: recthead.height
width: parent.width
height: parent.height-recthead.height-mainwld.space
//border.color: mainwld.rectBdColor
TableView {
id: tableView
visible: true
anchors.fill: parent
TableViewColumn {
title: "序号"
role: "Id"
width: tableView.viewport.width*2/24
}
TableViewColumn {
title: "账号"
role: "Name"
width: tableView.viewport.width*3/24
}
TableViewColumn {
title: "员工姓名"
role: "Nick"
width: tableView.viewport.width*3/24
}
TableViewColumn {
title: "手机号码"
role: "Mobile"
width: tableView.viewport.width*3/24
}
TableViewColumn {
title: "邮箱"
role: "Email"
width: tableView.viewport.width*3/24
}
TableViewColumn {
title: "部门"
role: "Depart"
width: tableView.viewport.width*2/24
}
TableViewColumn {
title: "岗位"
role: "Post"
width: tableView.viewport.width*2/24
}
TableViewColumn {
title: "账号状态"
role: "Status"
width: tableView.viewport.width*2/24
}
TableViewColumn {
title: "操作"
role: "Operate1"
width: tableView.viewport.width*2/24
}
TableViewColumn {
title: "操作"
role: "Operate2"
width: tableView.viewport.width*2/24
}
model: UserTableModel {
id: tablemodelid
onSignalRecvhandleResult: {
progressBar.setProgressValue(100);
}
}
Component.onCompleted: {
tablemodelid.getData(['index','count'], ['0', '1000000']);
progressBar.visible = true;
progressBar.start();
}
}
}
}
}
1.2.4.9. 创建main.qml主页面
main是用户登录成功后跳转的主页面,有以下功能:
- 显示菜单导航栏
- 根据菜单显示不同页面
- 退出登录
import QtQuick 2.11
import QtQuick.Window 2.11
import QtQuick.Dialogs 1.3
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.0
import MyTreeModel 0.1
Window {
id: mainwld
visible: true
opacity: 1
width: 960
height: 540
minimumWidth: 960
minimumHeight: 540
title: windowTitle
property string windowTitle: ''
property string rectBdColor: 'lightgray'
property real space: 5
// 定义展示的页面,这里是动态创建页面,切换页面后,隐藏的页面会被销毁,减少内存占用
property var threejspage: null
property var qt3dpage: null
property var chartpage: null
property var userpage: null
property var logpage: null
// 定义登录页面
Login {
id: loginpage
anchors.fill: parent
visible: true
Component.onCompleted: {
loginpage.signalShowMsg.connect(showMsg) //连接消息信号
}
}
// 显示页面消息函数,所有页面的消息都是触发main页面的这个消息函数展示
function showMsg(title, text) {
messageDialog.title = title;
messageDialog.text = text;
messageDialog.visible = true;
}
//开启进度条函数
function handleStartProgressBar() {
progressBar.visible = true;
progressBar.start();
}
//处理登录结果槽函数
function handleSignalLoginResult(result)
{
progressBar.setProgressValue(100);
if (result)
{
loginpage.visible = false;
mainwld.windowTitle = 'QML演示系统';
}
else
{
showMsg('提示', '账号或密码不正确!');
}
}
//接收处理消息槽函数
function handleSignalMeaasge(msg)
{
progressBar.setProgressValue(100);
showMsg('提示', msg);
}
//通过target连接c++类发出的信号
Connections
{
//qml 连接 c++ 信号
target: datacls
onSignalLoginResult: handleSignalLoginResult(result)
onSignalMeaasge: handleSignalMeaasge(msg)
}
//定时器,更新页面上面时间
Timer {
id: timeUpdate
interval: 1000; running: true; repeat: true
onTriggered: {
//console.log(Qt.formatDateTime(new Date(), "dddd hh:mm:ss"), new Date().getDay());
labelTime.text = Qt.formatDateTime(new Date(), "dddd hh:mm:ss");
}
}
Rectangle {
id: rectangle
visible: true
//border.color: mainwld.rectBdColor
anchors.fill: parent
anchors.rightMargin: mainwld.space
anchors.leftMargin: mainwld.space
anchors.bottomMargin: mainwld.space
anchors.topMargin: mainwld.space
Row {
anchors.fill: parent
spacing: mainwld.space
Rectangle {//左侧区域
id: rectLeft
border.color: mainwld.rectBdColor
width: 200
height: parent.height
Rectangle {//上面头像和时间区域
id: rectAvatar
width: parent.width
height: rectHead.height * 2 + mainwld.space
border.color: mainwld.rectBdColor
BorderImage {
y: 11
width: 52
height: 53
anchors.horizontalCenterOffset: 0
anchors.horizontalCenter: parent.horizontalCenter
source: "images/head.png"
}
Label {
id: labelTime
x: 39
y: 79
width: 151
height: 22
text: qsTr("星期二 14:17:15")
anchors.horizontalCenterOffset: 0
font.pointSize: 12
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
anchors.horizontalCenter: parent.horizontalCenter
}
}
Rectangle {//下面导航栏区域
id: rectMenu
y: rectAvatar.height
width: parent.width
height: parent.height-rectAvatar.height
border.color: mainwld.rectBdColor
TreeView {
id: treeView
frameVisible: false
headerVisible: false
anchors.fill: parent
anchors.rightMargin: mainwld.space
anchors.leftMargin: mainwld.space
anchors.bottomMargin: mainwld.space
anchors.topMargin: mainwld.space
model: MyTreeModel {}
itemDelegate: Item {
Text {
anchors.verticalCenter: parent.verticalCenter
color: styleData.textColor
elide: styleData.elideMode
text: styleData.value
}
}
TableViewColumn {
title: "title"
role: "title"
width: treeView.width-2
}
onClicked: {
console.log('-----------', index, index.parent.row, index.row, index.value);
if (index.parent.row === -1) {
if (index.row === 0) {
}
} else if (index.parent.row === 1) {
if (index.row === 0) {
threejspage = createComponentMenu(rectContent, 'qrc:/qml/Threejs3DPage.qml', 'threejspage');
} else if (index.row === 1) {
qt3dpage = createComponentMenu(rectContent, 'qrc:/qml/Qt3DPage.qml', 'qt3dpage');
} else if (index.row === 2) {
chartpage = createComponentMenu(rectContent, 'qrc:/qml/ChartsPage.qml', 'chartpage');
}
} else if (index.parent.row === 2) {
if (index.row === 0) {
userpage = createComponentMenu(rectContent, 'qrc:/qml/UserPage.qml', 'userpage');
} else if (index.row === 1) {
logpage = createComponentMenu(rectContent, 'qrc:/qml/LogPage.qml', 'logpage');
}
}
}
}
}
}
Column {
spacing: mainwld.space
width: parent.width-rectLeft.width-mainwld.space
height: parent.height
Rectangle {
id: rectHead
border.color: mainwld.rectBdColor
width: parent.width
height: 55
Button {
id: btnModifyPwd
width: 75
height: 33
text: qsTr("修改密码")
anchors.verticalCenter: parent.verticalCenter
anchors.right: btnExit.left
anchors.rightMargin: mainwld.space
MouseArea {
anchors.fill: parent
onClicked: {
}
}
}
Button {
id: btnExit
width: 75
height: 33
text: qsTr("退出登录")
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: mainwld.space
MouseArea {
anchors.fill: parent
onClicked: {
loginpage.visible = true;
mainwld.windowTitle = '登录页面';
}
}
}
}
Rectangle {
id: rectContent
border.color: mainwld.rectBdColor
width: parent.width
height: parent.height-rectHead.height-mainwld.space
}
}
}
}
//定义弹窗消息
MessageDialog {
id: messageDialog
title: ""
text: ""
onAccepted: {
console.log("And of course you could only agree.")
visible = false
}
Component.onCompleted: visible = false
}
//定义进度条
MyProgressBar {
id: progressBar
z: 2000
visible: false
Component.onCompleted: {
loginpage.signalStartProgressBar.connect(handleStartProgressBar)
}
}
function createComponentMenu(parent, url, name) {
for (var i in parent.children) {
var item = parent.children[i];
//console.log(i, name, item, item.objectName, item.children);
item.destroy();
}
return Qt.createComponent(url).createObject(parent, {
'objectName': name,
'visible': true,
'anchors.fill': parent
});
}
Component.onCompleted: {
mainwld.windowTitle = '登录页面';
}
}
1.3. 源码文件
后台源码:https://download.csdn.net/download/yyt593891927/12680538
默认用户名:admin
默认密码:admin
1.4. 后记
本文完整讲述了利用QML+mysql搭建系统,后面还会持续更新QML方面的文章。