Qt SQL 支持的数据库
QtSQL 提供 了一些常见数据库的驱动,包括网络型数据库,如 Oracle 、 MS SQL Server 等 ,
也包括简单的单机型数据库 ,如 SQLite , Qt SQL 提供的数据库驱动
QtSQL 模块的主要类
界面布局:
菜单布局:
界面布局部分先介绍这么多,下面来看程序部分:
首先要在工程项目中添加
QT += sql
先让我们看看主程序:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QLabel>
#include <QString>
#include <QtSql>
#include <QDataWidgetMapper>
#include "qwcomboboxdelegate.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
private:
QSqlDatabase DB;//数据库连接
QSqlTableModel *tabModel; //数据模型
QItemSelectionModel *theSelection; //选择模型
QDataWidgetMapper *dataMapper; //数据映射
QWComboBoxDelegate delegateSex; //自定义数据代理,性别
QWComboBoxDelegate delegateDepart; //自定义数据代理,部门
void openTable();//打开数据表
void getFieldNames();//获取字段名称,填充“排序字段”的comboBox
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
// void on_primeInsert(int row, QSqlRecord &record);//插入记录时,用于设置缺省字段数据
//
void on_currentChanged(const QModelIndex ¤t, const QModelIndex &previous);
// QTableView的SelectionModel的行发生了变化,进行处理
void on_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous);
///
void on_actOpenDB_triggered();
void on_actRecAppend_triggered();
void on_actRecInsert_triggered();
void on_actRevert_triggered();
void on_actSubmit_triggered();
void on_actRecDelete_triggered();
void on_actPhoto_triggered();
void on_actPhotoClear_triggered();
void on_radioBtnAscend_clicked();
void on_radioBtnDescend_clicked();
// void on_radioButton_3_clicked();
void on_radioBtnMan_clicked();
void on_radioBtnWoman_clicked();
void on_radioBtnBoth_clicked();
void on_comboFields_currentIndexChanged(int index);
void on_actScan_triggered();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
对于MainWindow 类中定义了 几个私有变量。
- QSqlDatabase DB , 用于加载数据库驱动和建立与数据库之间的连接。
- QSqlTableModel *tabModel ,用于指定某一个数据表 ,作为数据表的数据模型 。
- QltemSelectionModel *theSelection,作为 tabMode l 的选择模型,提供 currentChanged () 、currentRowChanged ()等信号 ,在 tabModel 选择的字段发生变化、当前记录发生变化时发射信号,以便程序进行响应 。 例如在 currentChanged()信号发射时,检查 tabModel 是否有数据被修改,从而更新界面上 “保存”和“取消” 两个按钮的使能状态
- QDataWidgetMapper *dataMapper 用于实现界面组件与 tabMode l 的字段之间 的映射 。
- QWComboBoxDelegate 是自定义的基于 QComboBox 的代理类
定义了两个槽函数 , 功能如下。
- on_ currentChanged()用于检查数据表内容是否有修改,从而更新“保存”和“取消”两个按钮的使能状态 。
- on_ currentRowChanged()用于在当前记录发生变化时,从新的当前记录里提取 Photo 字段的内容,并作为界面上的 QLabel 类型的 dblabPhoto 组件的 pixmap 显示出来 。
先看构造函数吧:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setCentralWidget(ui->splitter);
ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);
ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection);
ui->tableView->setAlternatingRowColors(true);
}
打开数据表:
void MainWindow::on_actOpenDB_triggered()
{//打开数据表
QString aFile=QFileDialog::getOpenFileName(this,"选择数据库文件","",
"SQL Lite数据库(*.db *.db3)");
if (aFile.isEmpty()) //选择SQL Lite数据库文件
return;
//打开数据库
DB=QSqlDatabase::addDatabase("QSQLITE"); //添加 SQL LITE数据库驱动
DB.setDatabaseName(aFile); //设置数据库名称
if (!DB.open()) //打开数据库
{
QMessageBox::warning(this, "错误", "打开数据库失败",
QMessageBox::Ok,QMessageBox::NoButton);
return;
}
//打开数据表
openTable();
}
本实例中需要打开 demodb.db3 数据库 (例子在最后)
函数 openTable()主要是创建 QSqITableModel 类型的私有变量 tabModel ,指定需要打开的数据
表为 employee,设置与界面显示组件的关联等 。
void MainWindow::openTable()
{//打开数据表
tabModel=new QSqlTableModel(this,DB);//数据表
tabModel->setTable("employee"); //设置数据表
tabModel->setEditStrategy(QSqlTableModel::OnManualSubmit);//数据保存方式,OnManualSubmit , OnRowChange
tabModel->setSort(tabModel->fieldIndex("empNo"),Qt::AscendingOrder); //排序
if (!(tabModel->select()))//查询数据
{
QMessageBox::critical(this, "错误信息",
"打开数据表错误,错误信息\n"+tabModel->lastError().text(),
QMessageBox::Ok,QMessageBox::NoButton);
return;
}
//字段显示名
tabModel->setHeaderData(tabModel->fieldIndex("empNo"),Qt::Horizontal,"工号");
tabModel->setHeaderData(tabModel->fieldIndex("Name"),Qt::Horizontal,"姓名");
tabModel->setHeaderData(tabModel->fieldIndex("Gender"),Qt::Horizontal,"性别");
tabModel->setHeaderData(tabModel->fieldIndex("Height"),Qt::Horizontal,"身高");
tabModel->setHeaderData(tabModel->fieldIndex("Birthday"),Qt::Horizontal,"出生日期");
tabModel->setHeaderData(tabModel->fieldIndex("Mobile"),Qt::Horizontal,"手机");
tabModel->setHeaderData(tabModel->fieldIndex("Province"),Qt::Horizontal,"省份");
tabModel->setHeaderData(tabModel->fieldIndex("City"),Qt::Horizontal,"城市");
tabModel->setHeaderData(tabModel->fieldIndex("Department"),Qt::Horizontal,"部门");
tabModel->setHeaderData(tabModel->fieldIndex("Education"),Qt::Horizontal,"学历");
tabModel->setHeaderData(tabModel->fieldIndex("Salary"),Qt::Horizontal,"工资");
tabModel->setHeaderData(tabModel->fieldIndex("Memo"),Qt::Horizontal,"备注"); //这两个字段不再tableView中显示
tabModel->setHeaderData(tabModel->fieldIndex("Photo"),Qt::Horizontal,"照片");
theSelection=new QItemSelectionModel(tabModel);//关联选择模型
//theSelection当前项变化时触发currentChanged信号
connect(theSelection,SIGNAL(currentChanged(QModelIndex,QModelIndex)),
this,SLOT(on_currentChanged(QModelIndex,QModelIndex)));
//选择行变化时
connect(theSelection,SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
this,SLOT(on_currentRowChanged(QModelIndex,QModelIndex)));
ui->tableView->setModel(tabModel);//设置数据模型
ui->tableView->setSelectionModel(theSelection); //设置选择模型
ui->tableView->setColumnHidden(tabModel->fieldIndex("Memo"),true);//隐藏列
ui->tableView->setColumnHidden(tabModel->fieldIndex("Photo"),true);//隐藏列
//tableView上为“性别”和“部门”两个字段设置自定义代理组件
QStringList strList;
strList<<"男"<<"女";
bool isEditable=false;
delegateSex.setItems(strList,isEditable);
ui->tableView->setItemDelegateForColumn(
tabModel->fieldIndex("Gender"),&delegateSex); //Combbox选择型
strList.clear();
strList<<"销售部"<<"技术部"<<"生产部"<<"行政部";
isEditable=true;
delegateDepart.setItems(strList,isEditable);
ui->tableView->setItemDelegateForColumn(tabModel->fieldIndex("Department"),&delegateDepart); //Combbox选择型
//创建界面组件与数据模型的字段之间的数据映射
dataMapper= new QDataWidgetMapper();
dataMapper->setModel(tabModel);//设置数据模型
dataMapper->setSubmitPolicy(QDataWidgetMapper::AutoSubmit);//
// dataMapper->setItemDelegate(new QSqlRelationalDelegate(this)); //含有外键的
//界面组件与tabModel的具体字段之间的联系
dataMapper->addMapping(ui->dbSpinEmpNo,tabModel->fieldIndex("empNo"));
dataMapper->addMapping(ui->dbEditName,tabModel->fieldIndex("Name"));
dataMapper->addMapping(ui->dbComboSex,tabModel->fieldIndex("Gender"));
dataMapper->addMapping(ui->dbSpinHeight,tabModel->fieldIndex("Height"));
dataMapper->addMapping(ui->dbEditBirth,tabModel->fieldIndex("Birthday"));
dataMapper->addMapping(ui->dbEditMobile,tabModel->fieldIndex("Mobile"));
dataMapper->addMapping(ui->dbComboProvince,tabModel->fieldIndex("Province"));
dataMapper->addMapping(ui->dbEditCity,tabModel->fieldIndex("City"));
dataMapper->addMapping(ui->dbComboDep,tabModel->fieldIndex("Department"));
dataMapper->addMapping(ui->dbComboEdu,tabModel->fieldIndex("Education"));
dataMapper->addMapping(ui->dbSpinSalary,tabModel->fieldIndex("Salary"));
dataMapper->addMapping(ui->dbEditMemo,tabModel->fieldIndex("Memo"));
// dataMapper->addMapping(ui->dbPhoto,tabModel->fieldIndex("Photo")); //图片无法直接映射
dataMapper->toFirst();//移动到首记录
getFieldNames();//获取字段名称列表,填充ui->groupBoxSort组件
//更新actions和界面组件的使能状态
ui->actOpenDB->setEnabled(false);
ui->actRecAppend->setEnabled(true);
ui->actRecInsert->setEnabled(true);
ui->actRecDelete->setEnabled(true);
ui->actScan->setEnabled(true);
ui->groupBoxSort->setEnabled(true);
ui->groupBoxFilter->setEnabled(true);
}
剩下的部分:
对应的知识点可以看帮助手册:
取值 Qt::AscendingOrder表示升序 , Qt: :DescendingOrder 表示降序。
QSqITableModel: :setEditStrategy()函数用于设置编辑策略:
- QSqlTableModel: : OnFieldChange,字段值变化时立即更新到数据库 。
- QSqlTableModel:: OnRowChange,当前行(就是记录)变换时更新到数据库 。
- QSqlTableModel:: OnManualSubmit,所有修改暂时缓存,手动调用 submitAll()保存所有修改 , 或调用 revertAll ()函数取消所有未保存修改。
QSqlTableModel::setHeaderData()函数用于设置每个字段 的表头数据 , 主要是设置显示标题。如果不进行表头设置,在 TableView 里显示时将显示字段名作为表头。为此,将每个字段的显示设置为相应的中文标题。
选择模型及其信号的作用
theSelection=new QitemSelectionModel(tabModel);
选择模型的作用是当用户在 TableView 组件上操作时 , 获取当前选择的行、列信息,并且在选择的单元格变化时发射 currentChanged()信号,在当前行变化时发射 currentR.owChanged()信号。
void MainWindow::on_currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
{//更新actPost和actCancel 的状态
Q_UNUSED(current);
Q_UNUSED(previous);
ui->actSubmit->setEnabled(tabModel->isDirty()); //有未保存修改时可用
ui->actRevert->setEnabled(tabModel->isDirty());
// if (current.isValid())
// ui->actRecDelete->setEnabled(true);
// else
// ui->actRecDelete->setEnabled(false);
}
void MainWindow::on_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous)
{
Q_UNUSED(previous);
// 行切换时的状态控制
ui->actRecDelete->setEnabled(current.isValid());
ui->actPhoto->setEnabled(current.isValid());
ui->actPhotoClear->setEnabled(current.isValid());
if (!current.isValid())
{
ui->dbLabPhoto->clear(); //清除图片显示
return;
}
dataMapper->setCurrentIndex(current.row()); //更细数据映射的行号
// QModelIndex curIndex=theSelection->currentIndex();//获取当前选择单元格的模型索引
int curRecNo=current.row();//获取行号
QSqlRecord curRec=tabModel->record(curRecNo); //获取当前记录
if (curRec.isNull("Photo")) //图片字段内容为空
ui->dbLabPhoto->clear();
else
{
QByteArray data=curRec.value("Photo").toByteArray();
QPixmap pic;
pic.loadFromData(data);
ui->dbLabPhoto->setPixmap(pic.scaledToWidth(ui->dbLabPhoto->size().width()));
}
}
表示记录的类 QSqlRecord
QSqlRecord 类记录了数据表 的字段信息和 一条记录的数据内容, QSqlTableModel 有两种参数的函数 record()可以返回一条记录。
QSq!Record QSqlTableModel::record(int row),返 回行号为 row 的记录,包括记录的字段定义和数据 。
表示字段的类 QSqlField
QWComboBoxDelegate 类的设计:
#ifndef QWCOMBOBOXDELEGATE_H
#define QWCOMBOBOXDELEGATE_H
#include <QStyledItemDelegate>
#include <QComboBox>
class QWComboBoxDelegate : public QStyledItemDelegate
{
Q_OBJECT
private:
QStringList m_ItemList;//选择列表
bool m_isEdit; //是否可编辑
public:
QWComboBoxDelegate(QObject *parent=0);
void setItems(QStringList items, bool isEdit);//初始化设置列表内容,是否可编辑
// void clearItems();
//自定义代理组件必须继承以下4个函数
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const Q_DECL_OVERRIDE;
void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE;
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const Q_DECL_OVERRIDE;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
const QModelIndex &index) const Q_DECL_OVERRIDE;
};
#endif // QWCOMBOBOXDELEGATE_H
对应的cpp:
#include "qwcomboboxdelegate.h"
#include <QComboBox>
QWComboBoxDelegate::QWComboBoxDelegate(QObject *parent):QStyledItemDelegate(parent)
{
}
void QWComboBoxDelegate::setItems(QStringList items, bool isEdit)
{
m_ItemList=items;
m_isEdit=isEdit;
}
QWidget *QWComboBoxDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(option);
Q_UNUSED(index);
QComboBox *editor = new QComboBox(parent);
for (int i=0;i<m_ItemList.count();i++) //从字符串列表初始下拉列表
editor->addItem(m_ItemList.at(i));
editor->setEditable(m_isEdit); //是否可编辑
return editor;
}
void QWComboBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QString str = index.model()->data(index, Qt::EditRole).toString();
QComboBox *comboBox = static_cast<QComboBox*>(editor);
comboBox->setCurrentText(str);
}
void QWComboBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
QComboBox *comboBox = static_cast<QComboBox*>(editor);
QString str = comboBox->currentText();
model->setData(index, str, Qt::EditRole);
}
void QWComboBoxDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index);
editor->setGeometry(option.rect);
}
数据映射
QDataWidgetMapper 用于建立界面组件与数据模型之间的 映射,可 以将界面的 QLineEdit 、QCombobox 等组件与数据模型的一个字段关联起来。
获取数据表的所有字段名称
自定义函数 getfieldNames()获取数据表的所有字段名称,并填充到界面上的“排序宇段”下拉列表框里 , 用于选择排序字段 。 getFieldNames()的实现代码如下:
void MainWindow::getFieldNames()
{ //获取所有字段名称
QSqlRecord emptyRec=tabModel->record();//获取空记录,只有字段名
for (int i=0;i<emptyRec.count();i++)
ui->comboFields->addItem(emptyRec.fieldName(i));
}
添加、插入与删除记录
添加:
void MainWindow::on_actRecAppend_triggered()
{//添加记录
tabModel->insertRow(tabModel->rowCount(),QModelIndex()); //在末尾添加一个记录
QModelIndex curIndex=tabModel->index(tabModel->rowCount()-1,1);//创建最后一行的ModelIndex
theSelection->clearSelection();//清空选择项
theSelection->setCurrentIndex(curIndex,QItemSelectionModel::Select);//设置刚插入的行为当前选择行
int currow=curIndex.row(); //获得当前行
tabModel->setData(tabModel->index(currow,0),2000+tabModel->rowCount()); //自动生成编号
tabModel->setData(tabModel->index(currow,2),"男");
// 插入行时设置缺省值,需要在primeInsert()信号里去处理
}
插入:
void MainWindow::on_actRecInsert_triggered()
{//插入记录
QModelIndex curIndex=ui->tableView->currentIndex();
tabModel->insertRow(curIndex.row(),QModelIndex());
theSelection->clearSelection();//清除已有选择
theSelection->setCurrentIndex(curIndex,QItemSelectionModel::Select);
// ui->actPost->setEnabled(true);
// ui->actCancel->setEnabled(true);
}
删除:
void MainWindow::on_actRecDelete_triggered()
{//删除当前记录
QModelIndex curIndex=theSelection->currentIndex();//获取当前选择单元格的模型索引
tabModel->removeRow(curIndex.row()); //删除最后一行
}
保存与取消修改
void MainWindow::on_actRevert_triggered()
{//取消修改
tabModel->revertAll();
ui->actSubmit->setEnabled(false);
ui->actRevert->setEnabled(false);
}
void MainWindow::on_actSubmit_triggered()
{//保存修改
bool res=tabModel->submitAll();
if (!res)
QMessageBox::information(this, "消息", "数据保存错误,错误信息\n"+tabModel->lastError().text(),
QMessageBox::Ok,QMessageBox::NoButton);
else
{
ui->actSubmit->setEnabled(false);
ui->actRevert->setEnabled(false);
}
}
QSqlTableModel : :submitAll () 函数用于将数据表所有未提交的修改保存到数据库, revertAll ()函数取消所有修改。
调用 submitAll ()函数保存数据时如果失败 , 可以通过 lastError()函数获取错误的具体信息,例如 , Name 是必填字段 , 添加记录时若没有填写 Name 字段 的 内容就提交 , 就会出现错误信息提示对话框。
设置和清除照片
employee 数据表的 Photo 字段是 BLOB 字段,用于存储图片文件。 Photo 宇段内容的显示己经在自定义槽函数 on_cutTentRowChanged()里实现了,就是在当前记录变化时提取 Photo 字段的内容 , 井显示为 dbLabPhoto 的 pixmap 。
void MainWindow::on_actPhoto_triggered()
{
//设置照片
QString aFile=QFileDialog::getOpenFileName(this,"选择图片文件","","照片(*.jpg)");
if (aFile.isEmpty())
return;
QByteArray data;
QFile* file=new QFile(aFile); //fileName为二进制数据文件名
file->open(QIODevice::ReadOnly);
data = file->readAll();
file->close();
// QVariant photo(data); //
// QModelIndex curIndex=theSelection->currentIndex();//获取当前选择单元格的模型索引
int curRecNo=theSelection->currentIndex().row();
QSqlRecord curRec=tabModel->record(curRecNo); //获取当前记录
curRec.setValue("Photo",data); //设置字段数据
tabModel->setRecord(curRecNo,curRec);
// tabModel->submit(); //并不能保存到数据库
QPixmap pic;
pic.load(aFile); //在界面上显示
ui->dbLabPhoto->setPixmap(pic.scaledToWidth(ui->dbLabPhoto->width()));
}
代码功能是用文件对话框选择一个图片文件 , 读取文件内容到 QByteArray 类型的变量 data里 , 获取当前记录到变量 curRec 后,用 QSqlRecord 的 setValue()函数为 Photo 字段设置数据为 data,然后用 setRecord()函数更新当前记录。
void MainWindow::on_actPhotoClear_triggered()
{
// QModelIndex curIndex=theSelection->currentIndex();//获取当前选择单元格的模型索引
int curRecNo=theSelection->currentIndex().row();
QSqlRecord curRec=tabModel->record(curRecNo); //获取当前记录
curRec.setNull("Photo");//设置为空值
tabModel->setRecord(curRecNo,curRec);
// tabModel->submit(); //并不能保存到数据库
ui->dbLabPhoto->clear();
}
这段函数为清除图片。
数据记录的遍历
void MainWindow::on_actScan_triggered()
{//涨工资,记录遍历
if (tabModel->rowCount()==0)
return;
// QSqlRecord aRec; //这种方式速度慢,因为需要提取记录,并且记录含有BLOB字段
// float salary;
for (int i=0;i<tabModel->rowCount();i++)
{
QSqlRecord aRec=tabModel->record(i); //获取当前记录
float salary=aRec.value("Salary").toFloat();
salary=salary*1.1;
aRec.setValue("Salary",salary);
tabModel->setRecord(i,aRec);
}
// 索引方式刷新记录,速度一样
// float salary;
// for (int i=0;i<tabModel->rowCount();i++)
// {
// salary=tabModel->data(tabModel->index(i,10)).toFloat();
// salary=salary*1.1;
// tabModel->setData(tabModel->index(i,10),salary);
// }
if (tabModel->submitAll())
QMessageBox::information(this, "消息", "涨工资计算完毕",
QMessageBox::Ok,QMessageBox::NoButton);
}
记录排序与记录过滤
void MainWindow::on_radioBtnAscend_clicked()
{//升序
tabModel->setSort(ui->comboFields->currentIndex(),Qt::AscendingOrder);
tabModel->select();
}
void MainWindow::on_radioBtnDescend_clicked()
{//降序
tabModel->setSort(ui->comboFields->currentIndex(),Qt::DescendingOrder);
tabModel->select();
}
void MainWindow::on_radioBtnMan_clicked()
{
tabModel->setFilter(" Gender='男' ");
// tabModel->select();
}
void MainWindow::on_radioBtnWoman_clicked()
{
tabModel->setFilter(" Gender='女' ");
// tabModel->select();
}
void MainWindow::on_radioBtnBoth_clicked()
{
tabModel->setFilter("");
// tabModel->setFilter(" Gender='男' or Gender='女' ");
// tabModel->setTable("employee");//重新设置tableName并打开,会取消所有初始设置
// tabModel->select();
}
void MainWindow::on_comboFields_currentIndexChanged(int index)
{//选择字段进行排序
if (ui->radioBtnAscend->isChecked())
tabModel->setSort(index,Qt::AscendingOrder);
else
tabModel->setSort(index,Qt::DescendingOrder);
tabModel->select();
}
好了已上就是本章的内容了,有关数据库的类容,如果不会的话,那就去学呀,哈哈,可以的,本例子放在了下面了,喜欢的同学可以下载下来:
链接:本程序的例子 提取码:isz6