「 QT 避坑指南 」远离那些躺过无数次的坑
整理 | 猿胖子
出品 | 猿武场(ID:apesarena)
QT已经封装了常用的数据库驱动,我们可以很方便对数据库操作,那么先来看看基于 SQLite 数据库操作的实例吧
首先,完成基本的界面布局创建
在项目 pro 文件中添加 SQL 模块支持,如下
1QT =coregui 2 3#在此处添加SQL模块支持 4QT =sql 5 6greaterThan(QT_MAJOR_VERSION,4):QT =widgets 7 8CONFIG =c 11 910#YoucanmakeyourcodefailtocompileifitusesdeprecatedAPIs.11#Inordertodoso,uncommentthefollowingline.12#DEFINES =QT_DISABLE_DEPRECATED_BEFORE=0x060000#disablesalltheAPIsdeprecatedbeforeQt6.0.01314SOURCES =\15custommodel.cpp\16main.cpp\17mainwindow.cpp1819HEADERS =\20custommodel.h\21mainwindow.h
接下来,简单创建个交互界面
1/** 2*初始化UI布局 3**/ 4voidMainWindow::initUI() 5{ 6QWidget*main=newQWidget; 7this->setCentralWidget(main); 8QVBoxLayout*layout=newQVBoxLayout(main); 910QTableView*view=newQTableView;11view->setObjectName(“view”);12QHBoxLayout*menu_layout=newQHBoxLayout;13QPushButton*add_btn=newQPushButton(“新增”);14menu_layout->addWidget(add_btn);15QPushButton*modify_btn=newQPushButton(“修改”);16menu_layout->addWidget(modify_btn);17QPushButton*del_btn=newQPushButton(“删除”);18menu_layout->addWidget(del_btn);1920QVBoxLayout*content_layout=newQVBoxLayout;21QLineEdit*id_edit=newQLineEdit;22id_edit->setObjectName(“id_edit”);23id_edit->setPlaceholderText(“序号”);24id_edit->setEnabled(false);25content_layout->addWidget(id_edit);26QLineEdit*name_edit=newQLineEdit;27name_edit->setObjectName(“name_edit”);28name_edit->setPlaceholderText(“姓名”);29content_layout->addWidget(name_edit);30QLineEdit*power_edit=newQLineEdit;31power_edit->setObjectName(“power_edit”);32power_edit->setPlaceholderText(“战力”);33content_layout->addWidget(power_edit);34QLineEdit*create_edit=newQLineEdit;35create_edit->setObjectName(“create_edit”);36create_edit->setPlaceholderText(“创建时间”);37create_edit->setEnabled(false);38content_layout->addWidget(create_edit);39QLineEdit*update_edit=newQLineEdit;40update_edit->setObjectName(“update_edit”);41update_edit->setPlaceholderText(“更新时间”);42update_edit->setEnabled(false);43content_layout->addWidget(update_edit);4445layout->addLayout(menu_layout);46layout->addLayout(content_layout);47layout->addWidget(view);48}
完成这些,我们就看到运行起来的效果
添加 SQLite 数据库并创建表
我们在 mainwindow.h 中新增一个 initDB 函数,用于初始化SQLite 连接、创建用户表和插入一些默认数据
1#ifndefMAINWINDOW_H 2#defineMAINWINDOW_H 3 4#include<QMainWindow> 5#include<QSqlDatabase> 6#include<QTableView> 7 8classMainWindow:publicQMainWindow 9{10Q_OBJECT1112public:13MainWindow(QWidget*parent=nullptr);14~MainWindow();1516private:17QSqlDatabasedb;18private:19voidinitUI();20boolinitDB();
注意引用头文件QSsqlDatabase
1/** 2*初始化数据库 3*创建表并插入初始数据 4**/ 5boolMainWindow::initDB() 6{ 7boolresult=false; 8 9//创建SQLite连接10db=QSqlDatabase::addDatabase(“QSQLITE”);11//设置数据库文件路径12db.setDatabaseName(QString(“%1/apesarena.db”).arg(QDir::currentPath()));1314//打开数据库连接15db.open();16if(!exists(“tbl_users”)){17//创建用户表18QSqlQueryquery(db);19boolresult=query.exec(“CREATETABLEtbl_users(idINTEGERPRIMARYKEYAUTOINCREMENT,nameVARCHAR(20),”20″powerINT,create_dateTIMESTAMPDEFAULT(datetime(‘now’,’localtime’)),”21″update_dateTIMESTAMPDEFAULT(datetime(‘now’,’localtime’)))”);22if(!result)23returnresult;24query.clear();2526//批量插入测试数据27QVariantListval_name;28val_name<<“C”<<“C “<<“QT”;29QVariantListval_power;30val_power<<3000<<2000<<1000;3132query.prepare(“INSERTINTOtbl_usersVALUES(NULL,:name,:power,(datetime(‘now’,’localtime’)),(datetime(‘now’,’localtime’)))”);33query.bindValue(“:power”,val_power);34query.bindValue(“:name”,val_name);35result=query.execBatch();36}37returnresult;38}
注意SQLite主键自增
这里有个小坑,如果你在创建语句中主键使用的是INT而非INTEGER,那么恭喜你 SQL语句会执行成功,但主键并不会自增。这里要实现自增除了写明AUTOINCREMENT 外不能是INT 一定的写INTEGER。
1//主键ID自增2//必须是INTEGER34idINTEGERPRIMARYKEYAUTOINCREMENT56//完整创建语句7query.exec(“CREATETABLEtbl_users(idINTEGERPRIMARYKEYAUTOINCREMENT,nameVARCHAR(20),”8″powerINT,create_dateTIMESTAMPDEFAULT(datetime(‘now’,’localtime’)),”9″update_dateTIMESTAMPDEFAULT(datetime(‘now’,’localtime’)))”);
将数据从库中读取并绑定到 QTableView
我们使用QTableModel读取数据库,使用也是非常简单
1//QSqlTableModel 2 3QSqlTableModel*model=newQSqlTableModel(this,db); 4model->setEditStrategy(QSqlTableModel::OnManualSubmit); 5 6//设置访问tbl_users表 7model->setTable(“tbl_users”); 8//查询tbl_users表所以数据 9model->select();1011//model绑定到QTableView显示12findChild<QTableView*>(“view”)->setModel(model);
这就 OK 了, 在 QSqlTableModel 中呢还可以设置排序和过滤条件,调用对应的方法就可以了,非常简单易用
1//设置排序按第二列升序,参数1指定第几列,参数2升序或降序2model->setSort(1,Qt::SortOrder::AscendingOrder);34//查询power大于100的数据5model->setFilter(“power>100”);
看看程序运行结果是否符合预期
已经能顺利读取出来我们初始化时添加的数据了。但 QTableview 中的数据没有居中显示,表头也需要设置成我们所期望的代称而不是实际的字段名称。
修改表头显示,设置 View Item 内容居中
设置内容居中这个问题,目前并不能使用QSS来解决,只能通过重写QSqlTabelModel 的 data函数来实现。
1#ifndefCUSTOMMODEL_H 2#defineCUSTOMMODEL_H 3 4#include<QObject> 5#include<QSqlTableModel> 6#include<QVariant> 7#include<QSqlDatabase> 8 9classCustomModel:publicQSqlTableModel10{11Q_OBJECT12public:13explicitCustomModel(QObject*parent=nullptr,QSqlDatabasedb=QSqlDatabase());1415protected:16virtualQVariantdata(constQModelIndex&idx,introle=Qt::DisplayRole)constoverride;17signals:1819};2021#endif//CUSTOMMODEL_H
创建 CustomModel 类继承 QSqlTableModel ,重写其 data 函数,如下
1QVariantCustomModel::data(constQModelIndex&idx,introle)const 2{ 3 4//设置Model内容居中 5 6QVariantvalue=QSqlQueryModel::data(idx,role); 7if(Qt::TextAlignmentRole==role) 8value=Qt::AlignCenter; 9returnvalue;10}
在CustomMode 中新增 setHeaderLabels 函数,用于设置自定义表头
1//custommodel.h 2 3classCustomModel:publicQSqlTableModel 4{ 5Q_OBJECT 6public: 7explicitCustomModel(QObject*parent=nullptr,QSqlDatabasedb=QSqlDatabase()); 8 9//用于设置表头10voidsetHeaderLabels(constQStringList&labels);1112…1314//custommodel.cpp1516voidCustomModel::setHeaderLabels(constQStringList&labels)17{18for(inti=0;i<labels.size(); i)19setHeaderData(i,Qt::Horizontal,labels.at(i));20}2122…
完成后,我们需要在mainwindow中稍作修改,调用自定义表头设置
1//custommodel.h 2 3voidMainWindow::bindData() 4{ 5//简略部分代码… 6 7model->setTable(“tbl_users”); 8model->select(); 910//设置自定义表头11model->setHeaderLabels(QString(“序号,名讳,战力,绝技,创建时间,更新时间”).split(“,”));1213findChild<QTableView*>(“view”)->setModel(model);14}151617…
运行程序,我们将看到表头及内容居中均已按预期显示
数据库操作:新增
通过QSqlTabelModel新增一条记录保存到数据库
1voidMainWindow::addData() 2{ 3//获取用户输入 4QStringname=findChild<QLineEdit*>(“name_edit”)->text().trimmed(); 5QStringpower=findChild<QLineEdit*>(“power_edit”)->text().trimmed(); 6 7//获取当前的QSqlTableModel实例 8CustomModel*model=findChild<CustomModel*>(“cmodel”); 9QSqlRecordrecord=model->record();10record.setValue(“name”,name);11record.setValue(“power”,power);12QStringdt=QDateTime::currentDateTime().toString(“yyyy-MM-ddhh:ss:mm”);13record.setValue(“create_date”,dt);14record.setValue(“update_date”,dt);1516//新增记录17model->insertRecord(model->rowCount(),record);1819//提交修改20model->submitAll();21…2223}
数据库操作:修改
通过QSqlTabelModel修改一条记录并保存到数据库
1voidMainWindow::modifyData() 2{ 3//获取当前View选中的行 4intindex=findChild<QTableView*>(“view”)->currentIndex().row(); 5if(index<0) 6return; 7 8//获取用户修改后的数据 9QStringname=findChild<QLineEdit*>(“name_edit”)->text().trimmed();10QStringpower=findChild<QLineEdit*>(“power_edit”)->text().trimmed();11QStringcreate=findChild<QLineEdit*>(“create_edit”)->text().trimmed();1213CustomModel*model=findChild<CustomModel*>(“cmodel”);14QSqlRecordrecord=model->record(index);15record.setValue(“name”,name);16record.setValue(“power”,power);17record.setValue(“create_date”,create);18record.setValue(“update_date”,QDateTime::currentDateTime().toString(“yyyy-MM-ddhh:ss:mm”));1920model->setRecord(index,record);2122//提交变更23model->submitAll();24…
数据库操作:删除
通过QSqlTabelModel删除一条 View 中选中的记录并提交到数据库
1voidMainWindow::delData() 2{ 3CustomModel*model=findChild<CustomModel*>(“cmodel”); 4 5//删除当前View选中的行,如果未选中(row==-1)不会有变化 6model->removeRow(findChild<QTableView*>(“view”)->currentIndex().row()); 7//提交变更 8model->submitAll(); 910…
最后,给对应的按钮添加响应事件
欢迎加入程序员社群,更多技术摘要等你拿走
社群福利:
1. 行业大牛技术手札,知识点汇总;
2. 求职/招聘信息内推;
4. 人际交往,增强技术宅人际交流;
5. 调节繁杂无趣的闲暇时光;
6. 不定期线上周边於线下技术活动沙龙。