← 所有文章

MySQL on Qt (3):SQL的MVC结构基础知识总结

更新于 2021.12.11

文章目录

MySQL on Qt (3): SQL的MVC结构基础知识总结

写在前面:

大一下学期的 CPP 大作业“自主订餐系统”可真是颇费了一些力气,每天晚睡早起撸代码,课都没听,很多知识都是现学现用。

为了避免当时学到的东西给全部忘光光(`_>`,我这脑子啊。。。),所以我把当时大作业的一些核心部分(一些零碎的小细节,能记就记)给记录一下,一方面是复习,一方面也方便以后用到的时候查阅。

这篇文章是大作业 MySQL + SQLite 操作相关部分的第三章,主要讲一下 Qt 中 SQL 的 MVC 结构的基础知识,包括一部分的 MVC 原理介绍和基础使用。

什么是 MVC 结构

关于 MVC 结构的具体介绍,可以看一个名为《Qt中文文档》的 Github 项目中 Qt 关于 MVC 的官方文档的中文翻译版(这里打个广告=。=):

模型/视图 编程

https://www.cryfeifei.cn/2020/08/08/qt-zhong-wen-wen-dang-mo-xing-shi-tu-jian-jie/

也可以看上一篇我转载自网络的文章:

Qt Model/View(模型/视图)结构(无师自通)

http://www.skykeyjoker.com/2020/07/23/model-view/

以上两篇网络文章我都转载到了我的博客中。

这里我就不再讲太多太细的知识和原理,只是简单概括一下。

GUI 应用程序的一个很重要的功能是由用户在界面上编辑和修改数据,典型的如数据库应用程序。数据库应用程序中,用户在界面上执行各种操作,实际上是修改了界面组件所关联的数据库内的数据。

Model/View(模型/视图)结构是 Qt 中用界面组件显示与编辑数据的一种结构,视图(View)是显示和编辑数据的界面组件,模型(Model)是视图与原始数据之间的接口。

将界面组件与所编辑的数据分离开来,又通过数据源的方式连接起来,是处理界面与数据的一种较好的方式。Qt 使用 Model/View 结构来处理这种关系,Model/View 的基本结构如图 1 所示。

Model/View基本结构

其中各部分的功能如下:

  • 数据(Data)是实际的数据,如数据库的一个数据表或SQL查询结果,内存中的一个 StringList,或磁盘文件结构等。
  • 视图或视图组件(View)是屏幕上的界面组件,视图从数据模型获得每个数据项的模型索引(model index),通过模型索引获取数据,然后为界面组件提供显示数据。Qt 提供一些现成的数据视图组件,如 QListView、QTreeView 和 QTableView 等。
  • 模型或数据模型(Model)与实际数据通信,并为视图组件提供数据接口。它从原始数据提取需要的内容,用于视图组件进行显示和编辑。Qt 中有一些预定义的数据模型,如 QStringListModel 可作为 StringList 的数据模型,QSqlTableModel 可以作为数据库中一个数据表的数据模型。

开启 MVC 之旅

讲完又臭又长的理论,我们就要进入令人激动的实践啦。

外甥打灯笼,这次我也是写了一个 Demo 来演示具体的应用。

demo

与以往不同,这次会先把全部工程代码贴出来再分块讲。

sqlmvcdemo.h:

#ifndef SQLMVCDEMO_H
#define SQLMVCDEMO_H

#include <QWidget>
#include <QDebug>

#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLineEdit>
#include <QLabel>

#include <QTableView>
#include <QHeaderView>
#include <QSqlTableModel>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlRecord>


QT_BEGIN_NAMESPACE
namespace Ui { class SQLMVCDemo; }
QT_END_NAMESPACE

class SQLMVCDemo : public QWidget
{
    Q_OBJECT

public:
    SQLMVCDemo(QWidget *parent = nullptr);
    ~SQLMVCDemo();

    QVBoxLayout *mainLay;

    // Sql model/view
    QSqlTableModel *_tableModel;
    QTableView *_tableView;

    bool connectToDb();

private:
    Ui::SQLMVCDemo *ui;

    QSqlDatabase _db = QSqlDatabase::addDatabase("QMYSQL");
};
#endif // SQLMVCDEMO_H


sqlmvcdemo.cpp:

#include "sqlmvcdemo.h"
#include "ui_sqlmvcdemo.h"

SQLMVCDemo::SQLMVCDemo(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::SQLMVCDemo)
{
    ui->setupUi(this);

    // Instance
    mainLay = new QVBoxLayout(this);
    _tableView = new QTableView;
    _tableModel = new QSqlTableModel(nullptr,_db); // Construct from allocated Database

    // UI
    mainLay->addWidget(_tableView);

    // Connect to Db
    if(connectToDb())
        qDebug()<<"Connected sucessfully.";
    else
    {
        QSqlError error = _db.lastError();

        qDebug()<<"Connected failed.";
        qDebug()<<error.text();
    }

    //Model
    _tableModel->setTable("user"); // Set table
    _tableModel->select();  // Must ** SELECT ** first!
    //_tableModel->setEditStrategy(QSqlTableModel::OnManualSubmit);  // Manual submit

    //View
    _tableView->setModel(_tableModel);

    /* Init Hearders
    _tableView->setColumnCount(5);
    _tableView->setHorizontalHeader(QStringList()<<"ID"<<"Name");
    */

    // Set width of cols
    _tableView->setColumnWidth(0,100);
    _tableView->setColumnWidth(1,180);
    _tableView->setColumnWidth(2,100);
    _tableView->setColumnWidth(3,220);
    _tableView->setColumnWidth(4,100);

    // Resize with stretch
    _tableView->horizontalHeader()->setSectionResizeMode(3,QHeaderView::Stretch);

    // Color varies in rows
    _tableView->setAlternatingRowColors(true);

    /* Some propertis of view
    _tableView->setSelectionBehavior(QAbstractItemView::SelectRows);  // SelectRows
    _tableView->setSelectionMode(QAbstractItemView::SingleSelection); // SingleSelection
    _tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);  // NoEditTriggers
    */
}

SQLMVCDemo::~SQLMVCDemo()
{
    delete ui;
}

bool SQLMVCDemo::connectToDb()
{
    _db.setHostName("");
    _db.setPort(3306);
    _db.setDatabaseName("");
    _db.setUserName("");
    _db.setPassword("");

    bool ret = _db.open();

    return ret;
}

Model/View 相关的类

demo 头文件sqlmvcdemo.h比较重要的部分是头文件include部分,这涉及了 Qt 中 MVC 相关的类。

Qt 中 MVC 结构相关类列表如下:

Qt中模型类的层次结构

视图相关类的层次结构图

从图里列出的类名基本上就能推测出我们要用到哪些类:

#include <QTableView>
#include <QHeaderView>
#include <QSqlTableModel>

其中 QHeaderView 类比较特殊,看起来与我们要使用的 SQL 毫不相关对不对。但其实它作用很大,到后面用到的时候再来说一说。


SQL Model/View 绑定

正如前面所说,Model 作为数据模型为操作数据提供了相关接口,因而在使相关 Model 时我们要绑定到具体的数据上。在我们这次使用的 SQL MVC 模型中,就是要把 QSqlTableModel 与 QSqlDatabase 对象绑定。所以便有了如下实例化代码:

_tableModel = new QSqlTableModel(nullptr,_db); // Construct from allocated Database

这样待绑定的数据库连接后,QSqlTableModel对象就可以对数据库进行相关操作了。

这里要注意一下QSqlTableModel模型指定一个表后,还要执行一下QSqlTableModel::select()函数才能选中表中的数据。

//Model
_tableModel->setTable("user"); // Set table
_tableModel->select();  // Must ** SELECT ** first!

关于数据模型我们还要考虑一个小细节:用户提交策略

一般情况下,用户完成数据编辑后,会有两种提交策略:

  • 自动提交(默认),即编辑完数据( dataChanged() )后,数据模型自动提交。
  • 手动提交,即编辑完数据后,需手动调用提交函数方能提交用户数据修改。

若要设置为手动模式,我们需要对 Model 进行如下设置:

_tableModel->setEditStrategy(QSqlTableModel::OnManualSubmit); // Manual submit

并在相应位置手动调用该函数提交:

_tableModel->submitAll()

前面也说过,View 是视图结构,与数据模型绑定,为用户操作提供相应的接口,并将用户操作具体应用到数据模型,数据模型再将操作实际应用到数据中。在我们这次使用的 SQL MVC 模型中,就是要把 QTableView 与 QSqlTableModel绑定。所以便有以下代码:

_tableView->setModel(_tableModel);

对 View 控件的设置,主要包括以下方面:

  • 表头设置
  • 编辑策略
  • 代理

View 表头设置

先来讲一下 View 控件表头的设置。

还记得前面提到的QHeaderView类嘛?这里对表头的各种设置,就必须引用该类。

首先是设置表头,这里以水平表头为例:

// Init Hearders
_tableView->setColumnCount(5);
_tableView->setHorizontalHeader(QStringList()<<"ID"<<"Name");

然后我们肯定会遇到要设置某一列宽度的情况:

// Set width of cols
_tableView->setColumnWidth(0,100);
_tableView->setColumnWidth(1,180);
_tableView->setColumnWidth(2,100);
_tableView->setColumnWidth(3,220);
_tableView->setColumnWidth(4,100);

当然,我们还能设置某一列的宽度随着界面缩放比例而改变(建议开启该选项,这样才能让表格占满 View 控件,美观度++):

// Resize with stretch
_tableView->horizontalHeader()->setSectionResizeMode(3,QHeaderView::Stretch);

再来设置一下让数据按行交替现实不同的颜色:

// Color varies in rows
_tableView->setAlternatingRowColors(true);

编辑策略

按行选中而不是按单元格选中:

_tableView->setSelectionBehavior(QAbstractItemView::SelectRows);  // SelectRows

单选:

_tableView->setSelectionMode(QAbstractItemView::SingleSelection); // SingleSelection

只读:

_tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);  // NoEditTriggers

代理

例如本 Demo 中的 Gender 项,我想编辑的时候出现一个“男生|女生”的下拉框而不是仅仅出现一个0或1的编辑框,这个时候我们就需要使用代理。

代理这里先卖一个关子。

好吧其实不是卖关子,而是太长了实在是不想在这一章讲了。。。

代理是一个很庞大的部分,我打算在下一章讲一些皮毛的东西。


到这里本章的内容基本上就结束了,下一章我会讲一下代理最为皮毛的一些原理和应用。

Qt Model/View(模型/视图)结构(无师自通)

http://www.skykeyjoker.com/2020/07/23/model-view/