QML+mysql搭建系统

1. 搭建系统

前面介绍了利用Qt+mysql搭建系统,本文讲介绍利用QML+mysql框架搭建一个小型系统。系统有以下特点:

  1. 系统是异步处理,mysql操作在线程里,操作完后数据通过信号槽发送到页面展示
  2. 查询mysql百万数量级数据表,用TableView+自定义数据模型轻松展示
  3. 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文件,登录成功后跳转到的主页面,展示功能页面
**

页面操作
处理结果
页面操作
处理结果
QML页面
CDataClass
Worker

下面我们来创建上面的列表类文件

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方面的文章。

©️2020 CSDN 皮肤主题: 1024 设计师:上身试试 返回首页