Qt5利用tableview和继承QSqlQueryModel实时修改及显示数据库内容


1、前言

有这样一个需求,我们需要将数据库内容显示到界面中,然后在界面上双击表格就可以修改数据库内容并更新到数据库文件中,修改完成后显示到界面中的内容和数据库文件中的内容一致。利用Qt5自带的例子分析后发现可以根据我们的数据库表格定制SqlQueryModel来实现对应的数据操作SqlQueryModel,然后关联该SqlQueryModel和tableview即可。

基本思路:

  • 设计数据库表格并确认
  • 创建数据库操作并封装接口,获取数据库操作句柄
  • 定制数据库操作Model
  • 创建tableview并设置和绑定定制的Model

2、测试过程及代码实例

(1)、创建一个简单的widget项目

这个不多说了,创建项目,ui选择QWidget即可:

在这里插入图片描述

(2)、在ui设计师中直接放置一个tableview并简单布局

在这里插入图片描述

(3)、设计简单的表格

这里设计一个简单的表格:

学生编号INTEGER primary key 学生性别vchar(4) 学生姓名vchar(100) 学生班级vchar(100)
1 张三 一班
2 李四 一班

以此,我们创建100个同学进行测试。

(4)、项目pro中添加sql支持

在这里插入图片描述

(5)、创建数据库及表格

这里我们使用纯c方式实现并将相关表格名称等定义为宏,便于后续可能存在修改字段名等。

定义dboptions.cpp和dboptions.h:

#ifndef DBOPTIONS_H
#define DBOPTIONS_H

#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <QDebug>

#define DB_CONNECTION_NAME "stu_db_connect"  //数据库连接名称

#define DB_NAME  "stu.db"  //数据库名称

#define STU_INFO_TABLE  "stu_info" //学生信息表
#define STU_NUM     "num"          //学生编号
#define STU_SEX     "sex"          //学生性别
#define STU_NAME    "name"         //学生名称
#define STU_CLASS   "classname"    //学生班级

bool connectDB();

bool initStuTable();

#endif // DBOPTIONS_H
#include "dboptions.h"

QSqlDatabase stuDBOP;  //数据库操作句柄

bool connectDB() {
    //判断该数据库连接是否已存在,存在则返回数据库操作句柄,不存在则创建并设置数据库名称
    if (QSqlDatabase::contains(DB_CONNECTION_NAME))
    {
        stuDBOP = QSqlDatabase::database(DB_CONNECTION_NAME);
    }
    else
    {
        stuDBOP = QSqlDatabase::addDatabase("QSQLITE", DB_CONNECTION_NAME);
        stuDBOP.setDatabaseName(DB_NAME);
    }

    //连接数据库
    if(!stuDBOP.open())
    {
        qCritical()<< "file:" << __FILE__ <<"line:"<< __LINE__ << stuDBOP.lastError();
        return false;
    }
    qInfo()<< "file:" << __FILE__ << "line:" << __LINE__ << "连接并打开配置数据库成功";
    return true;
}

bool initStuTable() {
    QSqlQuery query(stuDBOP);
    QStringList tables = stuDBOP.tables();

    if(tables.contains(STU_INFO_TABLE, Qt::CaseInsensitive))
    {
        qInfo()<<"file:"<<__FILE__<<"line:"<<__LINE__<<"学生信息表已存在";
        return true;
    }

    QString sql = QObject::tr("create table %1 (%2 INTEGER primary key, %3 varchar(4), %4 varchar(100), %5 var(100))")
                  .arg(STU_INFO_TABLE)
                  .arg(STU_NUM, STU_SEX, STU_NAME, STU_CLASS);
    qDebug()<<"file:"<<__FILE__<<"line:"<<__LINE__<<sql;
    if(!query.exec(sql))
    {
        qCritical()<<"file:"<<__FILE__<<"line:"<<__LINE__<<"create error"<<stuDBOP.lastError();
        return false;
    }
    qInfo()<<"file:"<<__FILE__<<"line:"<<__LINE__<<"学生信息表创建完成";

    sql.clear();
    sql = QString("insert into %1 (%2, %3, %4, %5) values (?, ?, ?, ?)")
                        .arg(STU_INFO_TABLE, STU_NUM, STU_SEX, STU_NAME, STU_CLASS);

    if(!query.prepare(sql))
    {
        qCritical()<<"file:"<<__FILE__<<"line:"<<__LINE__<<query.lastError();
        return false;
    }
    for(int i = 1; i <= 100; i++) {
        query.addBindValue(i);
        query.addBindValue("男");
        query.addBindValue("张三");
        query.addBindValue("一班");
        if(!query.exec())
        {
            qCritical()<<"file:"<<__FILE__<<"line:"<<__LINE__<<query.lastError();
            return false;
        }
    }
    qInfo()<<"file:"<<__FILE__<<"line:"<<__LINE__<<"初始化班级表完成";

    return true;
}
(6)、继承QSqlQueryModel写StuSqlQueryModel定制数据表操作模型

创建StuSqlQueryModel类继承QSqlQueryModel。

#ifndef STUSQLQUERYMODEL_H
#define STUSQLQUERYMODEL_H

#include <QSqlQueryModel>

class StuSqlQueryModel : public QSqlQueryModel
{
    Q_OBJECT

public:
    StuSqlQueryModel(QObject *parent = 0);

    Qt::ItemFlags flags(const QModelIndex &index) const override;
    bool setData(const QModelIndex &index, const QVariant &value, int role) override;

    void refresh();
private:
    bool setStuName(int id, QString name);
    bool setStuSex(int id, QString sex);
    bool setStuClass(int id, QString className);
};

#endif // STUSQLQUERYMODEL_H
#include "stusqlquerymodel.h"
#include "dboptions.h"

extern QSqlDatabase stuDBOP;

StuSqlQueryModel::StuSqlQueryModel(QObject *parent) : QSqlQueryModel(parent)
{

}

Qt::ItemFlags StuSqlQueryModel::flags(
        const QModelIndex &index) const
{
    Qt::ItemFlags flags = QSqlQueryModel::flags(index);
    if (index.column() == 1 || index.column() == 2 || index.column() == 3)
        flags |= Qt::ItemIsEditable;
    return flags;
}

bool StuSqlQueryModel::setData(const QModelIndex &index, const QVariant &value, int /* role */)
{
    if (index.column() < 1) {
        return false;
    }

    QModelIndex primaryKeyIndex = QSqlQueryModel::index(index.row(), 0);
    int id = data(primaryKeyIndex).toInt();

    bool ok;
    if (index.column() == 1)
    {
        ok = setStuSex(id, value.toString());
    }
    else if(index.column() == 2)
    {
        ok = setStuName(id, value.toString());
    }
    else if(index.column() == 3)
    {
        ok = setStuClass(id, value.toString());
    }

    refresh();
    endResetModel();
    return ok;
}

void StuSqlQueryModel::refresh()
{
    setQuery(QString("select * from %1").arg(STU_INFO_TABLE), stuDBOP);
    setHeaderData(1, Qt::Horizontal, "学生性别");
    setHeaderData(2, Qt::Horizontal, "学生姓名");
    setHeaderData(3, Qt::Horizontal, "学生班级");
}

bool StuSqlQueryModel::setStuName(int id, QString name)
{
    QSqlQuery query(stuDBOP);
    QString sql = QString("update %1 set %2 = ? where %3 = ?").arg(STU_INFO_TABLE, STU_NAME, STU_NUM);
    query.prepare(sql);
    query.addBindValue(name);
    query.addBindValue(id);
    return query.exec();
}

bool StuSqlQueryModel::setStuSex(int id, QString sex)
{
    QSqlQuery query(stuDBOP);
    QString sql = QString("update %1 set %2 = ? where %3 = ?").arg(STU_INFO_TABLE, STU_SEX, STU_NUM);
    query.prepare(sql);
    query.addBindValue(sex);
    query.addBindValue(id);
    return query.exec();
}

bool StuSqlQueryModel::setStuClass(int id, QString className)
{
    QSqlQuery query(stuDBOP);
    QString sql = QString("update %1 set %2 = ? where %3 = ?").arg(STU_INFO_TABLE, STU_CLASS, STU_NUM);
    query.prepare(sql);
    query.addBindValue(className);
    query.addBindValue(id);
    return query.exec();
}
(7)、绑定tableview与model
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include "stusqlquerymodel.h"

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private:
    Ui::Widget *ui;

    StuSqlQueryModel *stuSqlQueryModel;
};

#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
#include "dboptions.h"

extern QSqlDatabase stuDBOP;

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

    if(connectDB()) {
        initStuTable();
    }

    stuSqlQueryModel = new StuSqlQueryModel(ui->tableView);
    stuSqlQueryModel->refresh();

    ui->tableView->setModel(stuSqlQueryModel);
    ui->tableView->hideColumn(0);
    ui->tableView->resizeColumnsToContents();
    ui->tableView->verticalHeader()->hide();
    ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
}

Widget::~Widget()
{
    stuDBOP.close();
    delete stuSqlQueryModel;
    delete ui;
}

调用:

#include "widget.h"
#include "ui_widget.h"
#include "dboptions.h"

extern QSqlDatabase stuDBOP;

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

    if(connectDB()) {
        initStuTable();
    }

    stuSqlQueryModel = new StuSqlQueryModel(ui->tableView);
    stuSqlQueryModel->refresh();

    ui->tableView->setModel(stuSqlQueryModel);
    ui->tableView->hideColumn(0);
    ui->tableView->resizeColumnsToContents();
    ui->tableView->verticalHeader()->hide();
    ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
}

Widget::~Widget()
{
    stuDBOP.close();
    delete stuSqlQueryModel;
    delete ui;
}

3、结果

在这里插入图片描述
代码工程上传到CSDN资源(https://download.csdn.net/download/weixin_39510813/16396144)了,最近需要一些积分,其实基本上上面主要的代码都有了。

4、坑点

坑点:参考的Qt的示例代码中定制Model时在setData接口实现时中每次会先调用clear清除数据,然后低下生效后相当于重新创建了Model,导致每次Tableview都会刷新,如果数据多有滚动条的情况下会导致在tableview中改动数据后滚动条会自动跳到开头,这个找了很久,直到大量调试后在setData接口实现中去掉了clear调用才好。
在这里插入图片描述

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐