1. 搭建系统
前面介绍了利用python+vue搭建全栈系统:
移动端篇:H5+搭建移动端应用
前端篇:Vue2.0搭建PC前端
后台篇:Flask搭建系统后台
项目线上地址:项目访问链接,账号:admin 密码:admin
本文讲介绍另外一种技术,利用Qt框架搭建一个小型系统。系统有以下特点:

系统是异步处理,mysql操作在线程里,操作完后数据通过信号槽发送到页面展示
查询mysql百万数量级数据表,用QTableView+自定义数据模型轻松展示
QTableView加载自定义模型,展示百万条数据消耗比较少的内存(600MB左右内存,用Qt自带的QStandardItemModel模型消耗2635MB内存)
页面里面控件会随窗口自动缩放


  1. 登录页面
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

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. 创建项目文件和目录

在这里插入图片描述
项目目录结构如上图,文件和目录的说明如下:
C3DModels:Qt界面设计类,继承CBaseWidget类,展示3D模型
CBaseWidget:Qt类,作为其他页面的基类,实现页面缩放、页面进度条功能
CDataclass:Qt类,数据异步处理类,由页面触发,处理完成后通过信号发送处理结果给页面
CHome:Qt界面设计类,继承CBaseWidget类,登录成功后跳转到的主页面,展示功能页面
CLogin:Qt界面设计类,继承CBaseWidget类,登录页面
CLogs:Qt界面设计类,继承CBaseWidget类,日志页面
Common:Qt类,单例类,注册元类型和初始化mysql数据库
CQCharts:Qt界面设计类,继承CBaseWidget类,展示Qt画图页面
CUsers:Qt界面设计类,继承CBaseWidget类,用户页面,用QTableView展示百万数量及表格
CWorker:Qt类,在线程中运行,主要查询mysql
main.cpp:项目入口文件

 

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

1.2.4.1. 创建Common类
Common类是一个单例类,有以下功能:

负责初始化数据库
获取数据库实例
定义控件缩放函数
定义系统常量和数据结构
 

//定义组件缩放的基准
#define STD_WIDTH 1920  //组件缩放的基准宽度
#define STD_HIGHT 1080  //组件缩放的基准高度

//定义数据处理结果状态码
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 void scalWidget(QWidget *widget, float xc = 1, float yc = 1); //缩放组件函数

    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;
};

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();
    }

    QVariant testGetData(int count); //测试函数
    void handleCmdData(const CmdData &argcs); //供页面调用的命令函数,分发到具体的处理函数
    void checkUserPwd(const CmdData &argcs); //验证输入的用户名和密码
    void getUsersData(const CmdData &argcs); //查询用户信息
    void addUsersData(const CmdData &argcs); //增加用户信息
    void editUsersData(const CmdData &argcs); //编辑用户信息
    void getLogsData(const CmdData &argcs); //查询日志信息

signals:
	//把页面接受的命令,发送到线程里面的槽函数
    void operate(const int type, const QString &func, const QString &cmd);
    //把线程里面的处理结果返回给页面
    void operateResult(const RstData &rstData);

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;
}

 

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. 创建CBaseWidget类

CBaseWidget是Qt类,作为其他页面的基类,有以下功能:

  • 作为其他页面的基类
  • 实现组件随页面缩放
  • 定义CDataClass对象,发送命令和接收处理结果

 

class CBaseWidget : public QWidget
{
    Q_OBJECT
public:
    explicit CBaseWidget(QWidget *parent = nullptr);
    ~CBaseWidget();

    void initWidgets(QWidget *pageWidget, bool scaleFlag = true);
    void resizeWidget();
    void resizeEvent(QResizeEvent *event);
    void handleCmdData(const CmdData &argcs);
    void handleCmdDataList(const QVector<CmdData> &argcsList);

signals:
    void operateResult(const RstData &rstData);

public slots:
    void handleResults(const RstData &rstData);

private:
    QMap<QString, QRect> mWidRectMap;
    CDataClass *mHandle;
    DialogProcessBar *mDlgProcess;
    bool mScaleFlag;
    QVector<CmdData> mArgcsVec;

};

1.2.4.5. 创建CLogin类

CBaseWidget是Qt界面设计类,继承CBaseWidget类,有以下功能:

  • 用户登录页面
    namespace Ui {
    class CLoginForm;
    }
    
    class CLoginForm : public CBaseWidget
    {
        Q_OBJECT
    
    public:
        explicit CLoginForm(CBaseWidget *parent = nullptr);
        ~CLoginForm();
    
    private slots:
        void handleMainWidExit(); //退出登录槽函数
        void on_pbtLogin_clicked(); //登录槽函数
        void handleResults(const RstData &rstData); //接收线程处理结果槽函数
    
    private:
        Ui::CLoginForm *ui;
    
        CHomeForm *mMainWid; //定义主窗口
    };
    

    1.2.4.6. 创建CHome类

    CHome是Qt界面设计类,继承CBaseWidget类,有以下功能:

  • 显示菜单导航栏
  • 根据菜单显示不同页面
  • 退出登录
    namespace Ui {
    class CHomeForm;
    }
    
    class CHomeForm : public CBaseWidget
    {
        Q_OBJECT
    
    public:
        explicit CHomeForm(CBaseWidget *parent = nullptr);
        ~CHomeForm();
    
        void initMenu();
    
    signals:
        void signalExit();
    
    private slots:
        void on_pbtChangePwd_clicked();
        void on_pushExit_clicked();
        void handleResults(const RstData &rstData);
        void handleMenuClicked(const QModelIndex &index);
        void handleTimerUpdate();
    
    private:
        Ui::CHomeForm *ui;
    
        QTimer *mTimerUpdate;
        QStringList mMenuData1;
        QVector<QStringList> mMenuData2;
    
        C3DModelsForm *m3DModels;
        CQChartsForm *mCharts;
        CUsersForm *mUsers;
        CLogsForm *mLogs;
    };
    

    构造函数初始化,初始化展示页面对象指针,析构时删除页面对象指针

    CHomeForm::CHomeForm(CBaseWidget *parent) :
        CBaseWidget(parent),
        ui(new Ui::CHomeForm)
    {
        ui->setupUi(this);
    
        m3DModels = new C3DModelsForm();
        m3DModels->setParent(this->ui->frameContent);
        m3DModels->hide();
        mCharts = new CQChartsForm();
        mCharts->setParent(this->ui->frameContent);
        mCharts->hide();
        mUsers = new CUsersForm();
        mUsers->setParent(this->ui->frameContent);
        mUsers->hide();
        mLogs = new CLogsForm();
        mLogs->setParent(this->ui->frameContent);
        mLogs->hide();
        this->initWidgets(this);
    
        initMenu();
        mTimerUpdate = new QTimer(this);
        connect(mTimerUpdate, &QTimer::timeout, this, &CHomeForm::handleTimerUpdate);
        mTimerUpdate->start(1000);
        handleTimerUpdate();
    
        connect(this, &CHomeForm::operateResult, this, &CHomeForm::handleResults);
    }
    
    CHomeForm::~CHomeForm()
    {
        delete ui;
    
        if (mTimerUpdate != nullptr)
        {
            delete mTimerUpdate;
        }
    
        if (m3DModels != nullptr)
        {
            delete m3DModels;
        }
    
        if (mCharts != nullptr)
        {
            delete mCharts;
        }
    
        if (mUsers != nullptr)
        {
            delete mUsers;
        }
    
        if (mLogs != nullptr)
        {
            delete mLogs;
        }
    }
    

    初始化菜单导航栏

void CHomeForm::initMenu()
{
    QStandardItemModel *model = new QStandardItemModel(ui->treeViewMenu);//创建模型
    ui->treeViewMenu->setModel(model);//导入模型

    model->setHorizontalHeaderLabels(QStringList()<<QStringLiteral("菜单导航"));
    mMenuData1 << "首页" << "业务菜单" << "系统设置";
    mMenuData2.push_back(QStringList());
    mMenuData2.push_back(QStringList() << "3D模型" << "画图展示" << "模型3");
    mMenuData2.push_back(QStringList() << "用户管理" << "系统日志");
    qDebug() << mMenuData1 << mMenuData2;

    for (int i = 0; i < mMenuData1.size(); ++i)
    {
        model->setItem(i,0,new QStandardItem(mMenuData1[i]));
        for (int j = 0; j < mMenuData2[i].size(); ++j)
        {
            model->item(i)->appendRow(new QStandardItem(mMenuData2[i][j]));
        }
    }

    connect(ui->treeViewMenu, &QTreeView::clicked, this, &CHomeForm::handleMenuClicked);
    ui->labelTitle->setText(mMenuData1[0]);
}

 页面导航跳转

void CHomeForm::handleMenuClicked(const QModelIndex &index)
{
    QString menuName = index.data().toString();
    qDebug() << "index = " << index.row() << index.parent().row() << menuName;
    int row = index.parent().row() == -1 ? index.row() : index.parent().row();
    if (index.parent().row() == -1 && mMenuData2[row].size() > 0)
    {
        return;
    }

    ui->labelTitle->setText(QString("%1 / %2").arg(mMenuData1[row]).arg(menuName));

    if (menuName == "首页")
    {
        m3DModels->hidePage();
        mCharts->hidePage();
        mUsers->hidePage();
        mLogs->hidePage();
    }
    else if (menuName == "3D模型")
    {
        m3DModels->showPage();
        mCharts->hidePage();
        mUsers->hidePage();
        mLogs->hidePage();
    }
    else if (menuName == "画图展示")
    {
        m3DModels->hidePage();
        mCharts->showPage();
        mUsers->hidePage();
        mLogs->hidePage();
    }
    else if (menuName == "模型3")
    {
        m3DModels->hidePage();
        mCharts->hidePage();
        mUsers->hidePage();
        mLogs->hidePage();
    }
    else if (menuName == "用户管理")
    {
        m3DModels->hidePage();
        mCharts->hidePage();
        mUsers->showPage();
        mLogs->hidePage();
    }
    else if (menuName == "系统日志")
    {
        m3DModels->hidePage();
        mCharts->hidePage();
        mUsers->hidePage();
        mLogs->showPage();
    }
    resizeWidget(); //页面大小调整后,其他隐藏的页面大小也要调整
}

 

1.2.4.7. 创建CQCharts类

CQCharts是Qt界面设计类,继承CBaseWidget类,该类展示Qt图表功能,项目中使用该功能需要在pro文件中增加:QT += charts

//定义画图的数据结构
typedef QPair<QPointF, QString> Data;
typedef QList<Data> DataList;
typedef QList<DataList> DataTable;

namespace Ui {
class CQChartsForm;
}

class CQChartsForm : public CBaseWidget
{
    Q_OBJECT

public:
    explicit CQChartsForm(CBaseWidget *parent = nullptr);
    ~CQChartsForm();

    void showPage(); //显示页面
    void hidePage(); //隐藏页面
    void resizeView(); //改变图表大小
    //随机生成画图数据的函数
    DataTable generateRandomData(int listCount, int valueMax, int valueCount) const;
    //创建柱状图
    QChart *createBarChart(int valueCount) const;
    //创建折线图
    QChart *createLineChart() const;

private slots:
    void handleResults(const RstData &rstData); //接收线程返回数据的槽函数

private:
    int m_listCount;
    int m_valueMax;
    int m_valueCount;
    QList<QChartView *> m_charts;
    DataTable m_dataTable;
    QChartView *mLineView; //折线图
    QChartView *mbarView; //柱状图

    Ui::CQChartsForm *ui;
};

 

1.2.4.8. 创建CUsers类

CUsers是Qt界面设计类,继承CBaseWidget类,该类展示Qt表格功能,用户QTableView和自定义模型实现,用自定义模型时在pro文件中添加DEFINES += USER_MODEL
定义QTableView数据模型

class UserTableModel : public QAbstractTableModel
{
    Q_OBJECT

public:
    UserTableModel(const QStringList &hreadList, QObject *parent = nullptr)
    {
        Q_UNUSED(parent)
        mRoleList = hreadList; //表头数据
    }

    ~UserTableModel() override
    {

    }
	//重载函数,自定义模型需要定义,返回表格行数
    int rowCount(const QModelIndex & = QModelIndex()) const override
    {
        return mRow;
    }
	//重载函数,自定义模型需要定义,返回表格列数
    int columnCount(const QModelIndex & = QModelIndex()) const override
    {
        return mColumn;
    }
	//重载函数,自定义模型需要定义,设置表格表头
    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override
    {
        if(role == Qt::DisplayRole && orientation == Qt::Horizontal)
        {
            //qDebug() << "[headerData]" << section << mRoleList.size();
            if (section < mRoleList.size())
            {
                return mRoleList[section];
            }
        }

        return QAbstractTableModel::headerData(section, orientation, role);
    }
	//重载函数,自定义模型需要定义,设置表格数据
    QVariant data(const QModelIndex &index, int role) const override
    {
        if(!index.isValid())
        {
            return QVariant();
        }

        switch (role)
        {
        case Qt::TextColorRole:
            return QColor(Qt::black);
        case Qt::TextAlignmentRole:
            return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
        case Qt::DisplayRole:
        {
            if (mResult.size() > index.row())
            {
                if (mResult[0].size() > index.column())
                {
                    if (index.column() == 7) //状态
                    {
                        return mResult[index.row()][index.column()] == "1" ? "有效" : "无效";
                    }
                    else if (index.column() == 8) //编辑
                    {
                        return "编辑";
                    }
                    else if (index.column() == 9) //删除
                    {
                        return "删除";
                    }
                    else
                    {
                        return mResult[index.row()][index.column()];
                    }
                }
            }
            //qDebug() << role << index.column() << index.row();

            return QVariant();
        }
        case Qt::CheckStateRole:
        {
            return QVariant(); // 返回勾选框,如果返回QVariant()就没有勾选框
        }
        default:
            return QVariant();
        }
    }
	//自定义函数,返回表格行数据
    QVector<QString> getRowData(int row)
    {
        if (row < 0 || row >= mResult.size())
        {
            QVector<QString> result(0);
            return result;
        }

        return mResult[row];
    }
	//自定义函数,清除表格数据
    void clear()
    {
        mResult.clear();
        mRow = 0;
        mColumn = 0;
    }

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
        {
            clear();
        }
        endResetModel(); // 结束刷新模型
    }

private:
    int mRow;
    int mColumn;
    QStringList mRoleList;
    QVector< QVector<QString> > mResult;

};

定义用户页面类

namespace Ui {
class CUsersForm;
}

class CUsersForm : public CBaseWidget
{
    Q_OBJECT

public:
    explicit CUsersForm(CBaseWidget *parent = nullptr);
    ~CUsersForm();

    void showPage(); //显示页面,发送查询数据命令
    void hidePage(); //隐藏页面,清除表格数据
    void setTableHead(); //设置表格表头函数
    void updateTableData(const QVector< QVector<QString> > &result); //更新表格函数

private slots:
    void handleResults(const RstData &rstData);//接收线程处理结果槽函数
    void handleEditBtnClicked(); //行编辑槽函数
    void handleDelBtnClicked(); //行删除槽函数
    void handleTableClicked(const QModelIndex &index); //表格单元格点击槽函数

    void on_lineEditSearch_editingFinished();//模糊搜索槽函数

    void on_pbtAddUsers_clicked();

private:
    Ui::CUsersForm *ui;

    AddDialog *mAddDlg; //编辑用户信息弹框

};

 构造函数

CUsersForm::CUsersForm(CBaseWidget *parent) :
    CBaseWidget(parent),
    ui(new Ui::CUsersForm)
{
    ui->setupUi(this);
    this->initWidgets(this, false); //初始化后,页面组件跟随页面缩放

    setTableHead(); //设置表头
    connect(this, &CLogsForm::operateResult, this, &CLogsForm::handleResults);//连接基类中的信号,获取页面命令处理结果
    connect(ui->tabViewUsers, &QTableView::clicked, this, &CUsersForm::handleTableClicked);

    mAddDlg = new AddDialog();
    connect(mAddDlg, &AddDialog::operateResult, this, &CUsersForm::handleResults);
}

显示页面

void CUsersForm::showPage()
{
	QVector<CmdData> argcsVec;
    int count = 1000000;
    for (int var = 0; var < 1; ++var)
    {
        CmdData argcs;
        argcs.func = "getUsersData";
        argcs.params["index"] = QString::number(var*count);
        argcs.params["count"] = QString::number(count);
        argcsVec.push_back(argcs);
    }
    handleCmdDataList(argcsVec);
    this->show();
}

隐藏页面

void CUsersForm::hidePage()
{
#ifdef USER_MODEL
    ((UserTableModel*)(ui->tabViewUsers->model()))->clear();
#else
    ((QStandardItemModel*)(ui->tabViewUsers->model()))->clear();
#endif
    this->hide();
}

设置表头

void CUsersForm::setTableHead()
{
#ifdef USER_MODEL
    if (ui->tabViewUsers->model() == nullptr)
    {
        QStringList head;
        head << "序号"  << "账号" << "员工姓名" << "手机号码" << "邮箱" << "部门" << "岗位" << "账号状态" << "操作" << "操作";
        UserTableModel *usrTabModel = new UserTableModel(head);
        ui->tabViewUsers->setModel(usrTabModel);
    }

    ui->tabViewUsers->setStyleSheet("QHeaderView::section { background:green; color:white;min-height:3em;}");
    ui->tabViewUsers->setGridStyle(Qt::SolidLine);
    ui->tabViewUsers->horizontalHeader()->setStretchLastSection(true);
    ui->tabViewUsers->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    ui->tabViewUsers->verticalHeader()->setVisible(false);
    ui->tabViewUsers->setEditTriggers(QAbstractItemView::NoEditTriggers);
#else
    if (ui->tabViewUsers->model() == nullptr)
    {
        QStandardItemModel* model = new QStandardItemModel(ui->tabViewUsers);
        ui->tabViewUsers->setModel(model);
    }

    QStandardItemModel* model = ((QStandardItemModel*)ui->tabViewUsers->model());
    QStringList head;
    head << "序号"  << "账号" << "员工姓名" << "手机号码" << "邮箱" << "部门" << "岗位" << "账号状态" << "操作" << "操作";
    model->setHorizontalHeaderLabels(head);
    ui->tabViewUsers->setStyleSheet("QHeaderView::section { background:green; color:white;min-height:3em;}");
    ui->tabViewUsers->setGridStyle(Qt::SolidLine);
    ui->tabViewUsers->horizontalHeader()->setStretchLastSection(true);
    ui->tabViewUsers->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    ui->tabViewUsers->verticalHeader()->setVisible(false);
    ui->tabViewUsers->setEditTriggers(QAbstractItemView::NoEditTriggers);
#endif
}

显示数据

void CUsersForm::updateTableData(const QVector<QVector<QString> > &result)
{
#ifdef USER_MODEL
    UserTableModel *usrTabModel = ((UserTableModel*)(ui->tabViewUsers->model()));
    RstData rstData;
    rstData.retCode = 0;
    rstData.result = result;
    usrTabModel->handleResults(rstData);
#else
    QStandardItemModel* model = ((QStandardItemModel*)ui->tabViewUsers->model());
    setTableHead();
    if (result.size() == 0) return;
    int start = model->rowCount();
    int col = result[0].size();
    int row = result.size();
    QIcon iconEdit(":/new/prefix1/images/edit.png");
    QIcon iconDel(":/new/prefix1/images/edit_remove.png");

    for (int i = 0; i<row; ++i)
    {
        QList<QStandardItem*> list;
        for (int j = 0; j<col; ++j)
        {
            if (j == col - 3)
            {
                list << new QStandardItem(result[i][j] == "1" ? "有效" : "无效");
            }
            else if (j == col - 2)
            {
                list << new QStandardItem(iconEdit, "编辑");
            }
            else if (j == col - 1)
            {
                list << new QStandardItem(iconDel, "删除");
            }
            else
            {
                list << new QStandardItem(result[i][j]);
            }
        }
        model->insertRow(i + start, list);
    }
#endif
}

接收线程处理数据的槽函数

void CUsersForm::handleResults(const RstData &rstData)
{
    qDebug() << "CUsersForm::handleResults" << rstData.func << rstData.retCode << rstData.msg;
    if (rstData.retCode != RET_OK)
    {
        QMessageBox::warning(this, "提示", rstData.msg);
        return;
    }

    if (rstData.func == "getUsersData") // 处理查询用户数据
    {
        qDebug() << "CUsersForm::handleResults"  << rstData.result.size();
        updateTableData(rstData.result);
    }
    else if (rstData.func == "addUsersData") // 处理增加用户数据后更新表格
    {
        showPage();
    }
    else if (rstData.func == "editUsersData")
    {
        QMessageBox::warning(this, "提示", rstData.msg);
        mAddDlg->hide();
        showPage();
    }
}

使用自定义模型消耗的内存,如图:

在pro文件中注释自定义模型条件编译,#DEFINES += USER_MODEL,重新编译运行,就可以查看用Qt标准模型QStandardItemModel花费的内存,如图:

我们对比发现:使用自定义模型消耗610MB内存,使用QStandardItemModel花费2628MB内存,相差了4.3倍,所以为追求性能,最好实现自定义模型。

1.2.4.9. 创建CLogs类
CLogs是Qt界面设计类,继承CBaseWidget类,该类展示Qt表格功能,用户QTableView和QStandardItemModel实现
 

namespace Ui {
class CLogsForm;
}

class CLogsForm : public CBaseWidget
{
    Q_OBJECT

public:
    explicit CLogsForm(CBaseWidget *parent = nullptr);
    ~CLogsForm();

    void showPage(); //显示页面,发送查询数据命令
    void hidePage(); //隐藏页面,清除表格数据
    void setTableHead(); //设置表格表头函数
    void updateTableData(const QVector< QVector<QString> > &result); //更新表格函数

private slots:
    void handleResults(const RstData &rstData); //接收线程处理结果槽函数

    void on_lineEditSearch_editingFinished(); //模糊搜索槽函数

private:
    Ui::CLogsForm *ui;

};

构造函数初始化

CLogsForm::CLogsForm(CBaseWidget *parent) :
    CBaseWidget(parent),
    ui(new Ui::CLogsForm)
{
    ui->setupUi(this);
    this->initWidgets(this, false); //初始化后,页面组件跟随页面缩放

    setTableHead(); //设置表头
    connect(this, &CLogsForm::operateResult, this, &CLogsForm::handleResults);//连接基类中的信号,获取页面命令处理结果
}

显示页面

void CLogsForm::showPage()
{
	CmdData argcs;
    argcs.func = "getLogsData"; //命令处理函数,查询日志信息
    argcs.params["index"] = QString::number(0); //命令参数1
    argcs.params["count"] = QString::number(1000000); //命令参数2
    handleCmdData(argcs);
    this->show();
}

隐藏页面

void CLogsForm::hidePage()
{
    ((QStandardItemModel*)ui->tabWidLogs->model())->clear();
    this->hide();
}

设置表头

void CLogsForm::setTableHead()
{
    if (ui->tabWidLogs->model() == nullptr)
    {
        QStandardItemModel* model = new QStandardItemModel(ui->tabWidLogs);
        ui->tabWidLogs->setModel(model);
    }

    QStandardItemModel* model = ((QStandardItemModel*)ui->tabWidLogs->model());
    model->setHorizontalHeaderLabels(QStringList() << "序号"   << "用户"  << "IP" << "模块" << "动作" << "内容" << "操作时间");
    ui->tabWidLogs->setStyleSheet("QHeaderView::section { background:green; color:white;min-height:3em;}");
    ui->tabWidLogs->setGridStyle(Qt::SolidLine);
    ui->tabWidLogs->horizontalHeader()->setStretchLastSection(true);
    ui->tabWidLogs->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    ui->tabWidLogs->verticalHeader()->setVisible(false);
}

显示数据

void CLogsForm::updateTableData(const QVector<QVector<QString> > &result)
{
    QStandardItemModel* model = ((QStandardItemModel*)ui->tabWidLogs->model());
    setTableHead();
    if (result.size() == 0) return;
    int start = model->rowCount();
    int col = result[0].size();
    int row = result.size();
    for (int i = 0; i<row; ++i)
    {
        QList<QStandardItem*> list;
        for (int j = 0; j<col; ++j)
        {
            list << new QStandardItem(j == 0 ? QString::number(i+1+start) : result[i][j]);
        }
        model->insertRow(i + start, list);
    }
    //qDebug() << model->rowCount() << model->columnCount();
}

接收线程处理数据的槽函数

void CLogsForm::handleResults(const RstData &rstData)
{
    qDebug() << "CLogsForm::handleResults" << rstData.func << rstData.retCode << rstData.msg;
    if (rstData.retCode != RET_OK)
    {
        QMessageBox::warning(this, "提示", rstData.msg);
        return;
    }

    if (rstData.func == "getLogsData") //处理用户登录结果
    {
        qDebug() << "CLogsForm::handleResults"  << rstData.result.size();
        updateTableData(rstData.result);
    }
}

1.3. 源码文件
后台源码:https://download.csdn.net/download/yyt593891927/12676387
资源解压密码:dingba
默认用户名:admin
默认密码:admin

1.4. 后记
本文完整讲述了利用Qt+mysql搭建系统,下一章介绍用qml+mysql搭建系统。
————————————————
版权声明:本文为CSDN博主「丁爸」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yyt593891927/article/details/107712154

Logo

前往低代码交流专区

更多推荐