Qt SQL 模块提供数据库编程的支持,Qt 支持多种常见的数据库,如 MySQL、Oracle、MS SQLServer、SQLite 等。Qt SQL 模块包括多个类,可以实现数据库连接、SQL 语句执行、数据获取与界面显示等功能,数据与界面之间使用Model/View
架构,从而可以方便地实现数据的界面显示和操作。
要在项目中使用 Qt SQL 模块,需在项目配置文件中增加下面一条设置语句:
Qt += sql
在头文件或源程序文件中使用 Qt SQL 模块中的类,可以使用包含语句:
#include
这样会将 Qt SOL 模块中的所有类都包含进去,如果只使用其中的某些类,为避免冗余可以单独包含某个类。
Qt SQL 提供了一些常见数据库的驱动,包括网络型数据库,如 Oracle、MS SQL Server等,也包括简单的单机型数据库,如 SQLite。Qt SQL 提供的数据库驱动见《Qt5.9 c++开发指南》中表 11-1。
亦可参考以下链接:关于数据库的总结
在假设已经了解数据库基本概念和SQL语句基础知识的前提下,此处以SQLite数据库作为实例进行介绍。对于涉及到的基础知识可以参考,简简单单利用Qt实现sqlite的交互实现方法(利用Qt内部封装的类库,将SQLite语句发送给SQLite软件,QSqlDatabase实现数据库连接,QSqlQuery执行指定的SQLite语句),其中开始部分有提到基础知识的学习链接。
SQLite Expert 是 SQLite 数据库可视化管理工具
,可以从其官网下载最新的安装文件,SQLite Expert 安装文件带有 SQLite 数据库驱动,所以安装 SQLite Expert 后无需再下载安装 SQLite 数据库驱动。在SQLite Expert 软件中创建一个数据库demodb.db3,数据库中建立4个表,本章均使用此数据库作为数据库实例。具体界面如下图所示:
各个表中的具体含义可以查看《Qt5.9 c++开发指南》,上图中的studInfo表中记录学生的所在学院采用了代码字段 departID,具体的学院名称需要通过查询departments 表中相同的 departID 的记录获得;majorID 记录了专业代码,具体的专业名称需要香找 majors 表中的记录获取。这两个字段都是代码字段,只存储代码,具体的意义需要查询关联数据表的相应记录获得,在实际的数据库设计中经常用到这种设计方式。
Qt SQL中使用QSqlRelationalTableModel 可以很方便地实现这种代码型数据表的显示与编辑。
Qt SQL 提供的主要类的简要功能描述见下图
QSqlDatabase 用于建立与数据库的连接,一般是先加载需要的数据库驱动,然后设置数据库的登录参数,如主机地址、用户名、登录密码等,如果是单机型数据库,如 SQLite,只需设置数据库文件即可。
数据库的操作一般需要将数据库的内容在界面上进行显示和编辑,Qt 采用 Model/View 结构进行数据库内容的界面显示
。QTableView 是常用的数据库内容显示视图组件,用于数据库操作的数据模型类有 QSqlQueryModel、QSqlTableModel 和 QSqlRelationalTableModel,这几个类的继承关系如下图所示:
实例 sampl1_1 使用QSqlTableModel 显示实例数据库 demodb 中 employee 数据表的内容,实现编辑、插入、删除记录的操作,实现数据的排序和记录过滤,还实现 BLOB 类型字段 Photo 中存储照片的显示、导入等操作,运行界面如下图所示。
上图中,左侧数据表格是一个 QTableView 组件,设置一个 QSqlTableModel 类的变量作为数据模型后就可以显示数据表的内容。右侧的一些编辑框、下拉列表框等。界面组件通过QDataWidgetMapper 类的实例设置为与某个字段关联,自动显示字段的内容。但是没有现成的组件可以通过字段映射显示 Photo 字段中的照片。为此使用一个 QLabel 组件,通过其 pixmap 属性显示图片,所以照片的显示、导入和清除需要单独操作,并且与数据模块的记录移动保持同步,如同将此界面组件与 Photo 字段映射了一样。
QTableView 显示内容有缺省的代理组件,一般是自动使用 QLineEdit 组件。但是对于某些字段期望通过下拉列表框来选择输入,例如“性别”和“部门”这两个字段。为此,还设计了自定义数据代理类,“性别”和“部门”两个字段使用QComboBox 进行编辑。
工具栏上的按钮根据当前状态自动可用或禁用,特别是“保存”和“取消”两个按钮在数据表的内容被修改后自动变为可用,当保存或取消修改后又变为不可用。
实例samp11_1是主窗口继承自QMainWindow的应用程序,使用UI设计器可视化设计界面。整体布局如下图所示:
以下是主窗口MainWindow的定义(自动生成的界面组件的槽函数进行省略)
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
#include
#include
#include
#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_currentChanged(const QModelIndex ¤t, const QModelIndex &previous);
// QTableView的SelectionModel的行发生了变化,进行处理
void on_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous);
......
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
MainWindow 类中定义了几个私有变量。
私有函数 openTable()用于打开数据表,getFieldNames()用于获取数据表 employee 的所有字段的名称,并填充界面上“排序字段”后的 ComboBox 组件。
定义了两个槽函数,功能如下:
MainWindow的构造函数代码如下,主要是对tableView的一些显示属性的设置
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setCentralWidget(ui->splitter);
// tableView显示属性设置
ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);
ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection);
ui->tableView->setAlternatingRowColors(true);
}
工具栏上的“打开”按钮由actOpenDB 生成,单击此按钮时将添加 SQLite 数据库驱动、打开数据库文件、连接 employee 数据表并设置显示属性,并且创建 tableView 显示的代理组件,设置数据源与界面组件的映射等。其响应代码如下:
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); //设置数据库名称
// DB.setHostName();
// DB.setUserName();
// DB.setPassword();
if (!DB.open()) //打开数据库
{
QMessageBox::warning(this, "错误", "打开数据库失败",
QMessageBox::Ok,QMessageBox::NoButton);
return;
}
//打开数据表
openTable();
}
由打开文件对话框选择需要打开的 SQLite 数据库文件,本实例中需要打开 demodb.db3 数据,此文件在第 11 章实例程序总目录下。
私有变量 DB 是 QSqlDatabase 类。QSqlDatabase 用于数据库的操作,包括建立数据库连接,设置登录数据库的参数,打开数据库等。下面的一行语句使用 QSqlDatabase 的静态函数addDatabase()添加 SQLite 数据库的驱动。
DB=QSqlDatabase::addDatabase("QSQLITE");
然后需要使用 QSqlDatabase 的几个函数设置数据库登录参数,setDatabaseName()设置数据库名称,对于SQLite 数据库,就设置为数据库文件。如果是网络型数据库,如 Oracle、MS SQL Server等,还需要使用 setHostName()设置数据库主机名,setUserName()设置数据库用户名,setPassword()设置数据库登录密码。对于SQLite 数据库,只需用 setDatabaseName()设置数据库文件即可。
数据库连接与登录参数设置后,调用 QSlDatabaseopen()函数打开数据库。如果成功打开数据库,再调用自定义函数 openTable()打开数据表 employee,并进行相应的操作。openTable()函数代码如下:
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);
}
函数 openTable()主要是创建 QSqlTableModel 类型的私有变量 tabModel,指定需要打开的数据表为employee,设置与界面显示组件的关联等。QSqlTableModel 设置一个数据表名称后,作为数据表的数据模型,可以方便地编辑一个数据表。
使用的核心的类是 QSqlTableModel,其主要的函数功能见下表 (省略了函数中的 const 关键字,省略了缺省参数)。
函数openTable()的代码主要包括以下几部分的功能。
首先创建 QSqlTableModel 类型的私有变量 tabModel,并且在创建时指定了数据库连接,就是前面设置的私有变量 DB,然后用 setTable()函数指定数据表
setSort()函数设置排序字段和排序方式,其函数原型为:
void QSqlTableModel::setSort(int *column*, [Qt::SortOrder](../qtcore/qt.html#SortOrder-enum) *order*)
第1个参数 column 是排序字段的列号;第2个参数 order 是枚举类型 Ot:SortOrder,表示排序方式,取值 Qt:AscendingOrder 表示升序,Qt::DescendingOrder 表示降序。程序中设置为根据EmpNo 字段升序排序,即:
tabModel->setSort(tabModel->fieldIndex("empNo"),Qt::AscendingOrder); //排序
QSqlTableModel::setEditStrategy()函数用于设置编辑策略,参数是枚举类型 QSqlTableModel::EditStrategy,各取值的意义如下。
QSqlTableModel::OnFieldChange,字段值变化时立即更新到数据库。
QSqlTableModel:: OnRowChange,当前行 (就是记录
)变换时更新到数据库。-
QSqlTableModel::OnManualSubmit,所有修改暂时缓存,手动调用 submitAll()保存所有修改,或调用revertAll()函数取消所有未保存修改
程序里设置编辑策略为 QSqlTableModel::OnManualSubmit,在修改数据后并不直接提交更新,只是让工具栏上的“保存”和“取消”按钮可用,用户手动提交或取消修改。
对 tabModel 设置这些属性后,调用 select()函数打开数据表,如果打开失败,可以通过 QSqITableModel::lastError()函数获取上一错误信息的文本说明。
QSqlTableModel::setHeaderData()函数用于设置每个字段的表头数据,主要是设置显示标题。如果不进行表头设置,在 TableView 里显示时将显示字段名作为表头。为此,将每个字段的显示设置为相应的中文标题。例如,设置“Name”字段的显示标题为“姓名”的代码是:
tabModel->setHeaderData(tabModel->fieldIndex("Name"),Qt::Horizontal,"姓名");
setHeaderData()函数的第 1个参数是字段的序号,通过 fieldIndex()函数可以获取某个字段名在数据模型里的序号,避免直接使用数字时不便于理解和修改的问题。
为数据模型创建一个选择模型的代码如下:
theSelection=new QItemSelectionModel(tabModel);//关联选择模型
选择模型的作用是当用户在 TableView 组件上操作时,获取当前选择的行、列信息,并且在选择的单元格变化时发射currentChanged()信号,在当前行变化时发射currentRowChanged()信号。
为 currentChanged()信号编写槽函数 on_currentChanged(),获取 tabModel->isDirty()函数的值,根据是否有未提交的修改,更新工具栏按钮“保存”或“修改”的使能状态。槽函数on_currentChanged()的代码如下:
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());
}
为currentRowChanged()信号编写槽函数 on_currentRowChanged(),用于在当前行变化时,从新的记录里提取 Photo 字段的内容,并将图片在 QLabel组件中显示出来。on_currentRowChanged()的代码如下:
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()); //更细数据映射的行号
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()));
}
}
槽函数on_currentRowChanged()传递的参数current 是行切换后新的当前行的模型索引,首先根据 current 是否有效,更新 3 个Action 的使能状态。若 current 是有效的,更新数据映射 dataMapper的当前行,即:
dataMapper->setCurrentIndex(current.row()); //更细数据映射的行号
这将使界面上的编辑框、下拉列表框等与字段关联的界面组件显示当前记录的内容。
由于没有现成的界面组件可以通过数据映射显示 BLOB 字段内的图片,在此槽函数里通过编程获取 Photo 字段的数据,并将其显示为一个QLabel 组件的 pixmap。
用下面两行代码获取当前行的记录:
int curRecNo=current.row();//获取行号
QSqlRecord curRec=tabModel->record(curRecNo); //获取当前记录
curRec 是 QSqlRecord 类型,返回了当前记录的数据,可以获取当前记录的每个字段的内容。程序中它获取 Photo 字段的内容,并将其转换为图片后作为 ui->dbLabPhoto 组件的 pixmap来显示。
QSqlRecord 类记录了数据表的字段信息和一条记录(表中一行信息)的数据内容,QSqlTableModel 有两种参数的函数 record()可以返回一条记录。
QSqlRecord QSqlTableModel::record(),不带参数的record()函数,返回的一个QSqlRecord对象只有记录的字段定义,但是没有数据。这个函数一般用于获取一个数据表的字段定义。
QSqlRecord QSqlTableModel::record(int row),返回行号为 row 的记录,包括记录的字段定义和数据。
QSqlRecord 类封装了对记录的字段定义和数据的操作,其主要函数见下表,(省略了函数参数中的 const 关键字,对于具有不同参数的同名函数,只列出一种参数形式的函数)。
QSqlRecord 用于字段操作的函数一般有两种参数形式的同名函数,用字段序号或字段名表示一个字段,如 value()函数返回一个字段的值,有如下两种参数形式的函数:
QSqlRecord 的 field()函数返回某个字段,返回数据类型是 QSqlField。QSqlField 封装了字段定义信息和数据。字段的定义一般在设计数据表时就固定了,不用在 QSqlRecord 里修改。QSqlRecord 用于字段数据读写的函数见下表 (省略了函数参数中的 const 关键字)。
界面上用一个QTableView 组件显示 tabModel 的表格数据内容,设置其数据模型和选择模型,并且将 Memo 和 Photo 两个字段的列设置为隐藏,因为在表格里难以显示备注文字和图片。
为在 tableView 中显示和编辑“性别”和“部门”两个字段,设计了基于QComboBox 的自定义代理组件类 QWComboBoxDelegate。自定义代理组件 QWComboBoxDelegate 的设计方法在第五章中有详细介绍,这里只是稍作改变,增加了一个 setItems()函数,用于初始化下拉列表框和设置是否可以编辑,这样一个QWComboBoxDelegate 可以创建多个代理组件实例,分别用于“性别”和“部门”两个字段。
下面是相对于 5.5 节的QWComboBoxDelegate 修改或增加的部分的代码,其余相同的部分没有列出。
#ifndef QWCOMBOBOXDELEGATE_H
#define QWCOMBOBOXDELEGATE_H
#include
#include
class QWComboBoxDelegate : public QStyledItemDelegate
{
Q_OBJECT
private:
QStringList m_ItemList;//选择列表
bool m_isEdit; //是否可编辑
public:
QWComboBoxDelegate(QObject *parent=0);
void setItems(QStringList items, bool isEdit);//初始化设置列表内容,是否可编辑
//自定义代理组件必须继承以下4个函数
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const Q_DECL_OVERRIDE;
};
#endif // QWCOMBOBOXDELEGATE_H
两个函数的实现代码如下:
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;
}
QDataWidgetMapper 用于建立界面组件与数据模型之间的映射,可以将界面的 QLineEdit、
QCombobox 等组件与数据模型的一个字段关联起来。
创建 QDataWidgetMapper 类的变量 dataMapper 后,用 setMode()设置关联的数据模型,setSubmitPolicy()函数设置数据提交策略,有自动(AutoSubmit) 和手动 (ManualSubmit)两种方式。addMapping用于设置界面组件与数据模型的列的映射,程序在界面上的各编辑组件与数据表的各字段之间建立了映射关系。Memo 字段可以与一个QPlainTextEdit 的组件映射,但是 Photo 是个 BLOB 字段,没有组件可以直接显示其内容。
QDataWidgetMapper 还有 toFirst()、toPrevious()、toNext()和 toLast()4 个函数用于在记录间移动,setCurrentIndex()和setCurrentModelIndex()可以直接移动到某一行记录,revert()和 submit()用于手工取消或提交当前记录的修改,当提交策略设置为QDataWidgetMapper::AutoSubmit 时,行切换时将自动提交修改。
自定义函数 getFieldNames()获取数据表的所有字段名称,并填充到界面上的“排序字段”下拉列表框里,用于选择排序字段。getFieldNames()的实现代码如下:
void MainWindow::getFieldNames()
{ //获取所有字段名称
QSqlRecord emptyRec=tabModel->record();//获取空记录,只有字段名
for (int i=0;i<emptyRec.count();i++)
ui->comboFields->addItem(emptyRec.fieldName(i));
}
利用debug进行调试,查看其中内容
工具栏上有“添加”“插入”“删除”3 个按用于记录操作。actRecAppend 用于添加记录其槽函数代码如下:
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()信号里去处理
}
QSqlTableModel::insertRow(int row)函数在数据模型的 row 行前面插入一行记录,如果 row 大于或等于数据模型的总行数,则在最后添加一行记录。
actRecInsert 实现在当前行的前面插入一行,其槽函数代码如下:
void MainWindow::on_actRecInsert_triggered()
{//插入记录
QModelIndex curIndex=ui->tableView->currentIndex();
tabModel->insertRow(curIndex.row(),QModelIndex());
theSelection->clearSelection();//清除已有选择
theSelection->setCurrentIndex(curIndex,QItemSelectionModel::Select);
}
actRecDelete 用于删除当前记录,其槽函数实现代码如下:
void MainWindow::on_actRecDelete_triggered()
{//删除当前记录
QModelIndex curIndex=theSelection->currentIndex();//获取当前选择单元格的模型索引
tabModel->removeRow(curIndex.row()); //删除最后一行
}
在插入或删除记录操作,未提交保存之前,tableView 的左侧表头会以标记表示记录编辑状态,“*”表示新插入的记录,“!”表示删除的记录。在保存或取消修改后,这些标记就消失,删除的记录行也从 tableView 里删除。
在打开数据表初始化时,设置数据模型的编辑策略为 OnManualSubmit,即手动提交修改。当数据模型的数据被修改后,不管是直接修改字段值,还是插入或删除记录,在未提交修改前,QSqlTableModel::isDirty()函数返回 true,就是利用这个函数在自定义槽函数 on_currentChanged()里修改actSubmit 和actRevert 两个Action的使能状态
actSubmit 用于保存修改,actRevert 用于取消修改,对应工具栏上的“保存”和“取消”两个按钮,下面是这两个 Action 的槽函数的代码。
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);
}
}
employee 数据表的 Photo 字段是 BLOB 字段,用于存储图片文件。Photo 字段内容的显示已经在自定义槽函数on_currentRowChanged()里实现了,就是在当前记录变化时提取 Photo 字段的内容,并显示为 dbLabPhoto的 pixmap。
actPhoto 和 actPhotoClear 是设置照片和清除照片的两个 Action,与工具栏上的“设置照片”和“清除照片”按钮关联。actPhoto 的槽函数代码如下:
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();
int curRecNo=theSelection->currentIndex().row();
QSqlRecord curRec=tabModel->record(curRecNo); //获取当前记录
curRec.setValue("Photo",data); //设置字段数据
tabModel->setRecord(curRecNo,curRec);
QPixmap pic;
pic.load(aFile); //在界面上显示
ui->dbLabPhoto->setPixmap(pic.scaledToWidth(ui->dbLabPhoto->width()));
}
代码功能是用文件对话框选择一个图片文件,读取文件内容到 QByteArray 类型的变量 data里,获取当前记录到变量 curRec 后,用 QSqlRecord 的 setValue()函数为 Photo 字段设置数据为 data,然后用 setRecord()函数更新当前记录。
curRec.setValue("Photo",data); //设置字段数据
tabModel->setRecord(curRecNo,curRec);
这里的更新只是更新到数据模型,并没有更新到数据库。
actPhotoClear 用于清除照片,其槽函数实现代码如下:
void MainWindow::on_actPhotoClear_triggered()
{
int curRecNo=theSelection->currentIndex().row();
QSqlRecord curRec=tabModel->record(curRecNo); //获取当前记录
curRec.setNull("Photo");//设置为空值
tabModel->setRecord(curRecNo,curRec);
ui->dbLabPhoto->clear();
}
获取当前记录到变量 curRec 后,调用 setNull()函数将 Photo 字段设置为 NULL,就是清除了字段的内容,然后更新记录到数据模型。
工具栏上的“涨工资”按钮用于将数据表内所有记录的 salary 字段的内容增加 10%,演示了记录遍历的功能。actScan 实现此按钮的功能,其槽函数实现代码如下:
void MainWindow::on_actScan_triggered()
{//涨工资,记录遍历
if (tabModel->rowCount()==0)
return;
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;irowCount();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);
}
QSqlTableModel 的 setSort()函数设置数据表根据某个字段按照升序或降序排列,实际上就是设置了SQL语句里的ORDER BY子句。
在打开数据库时,已经调用 getFicldNames()函数将数据表的所有字段名添加到界面上的comboFields 下拉列表框了。“排序字段”下拉列表框、“升序”和“降序”两个 RadioButton 的相应操作的槽函数实现按选择字段排序。
void MainWindow::on_comboFields_currentIndexChanged(int index)
{//选择字段进行排序
if (ui->radioBtnAscend->isChecked())
tabModel->setSort(index,Qt::AscendingOrder);
else
tabModel->setSort(index,Qt::DescendingOrder);
tabModel->select();
}
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();
}
在调用 setSort()函数设置排序规则后,需要调用 QSqlTableModel::select()重新读取数据表的数据才会使排序规则生效。
QSqlTableModel 的 setFilter()函数设置记录过滤条件,实际上就是设置了 SQL 语句里的 WHERE字句。
实例里演示针对 Gender 字段设置字段过滤条件,窗口界面上“数据过滤”分组框里有 3个RadioButton,分别为“男”“女”“全显示”,3 个按钮的 clicked()信号的槽函数实现代码如下:
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("");
}
调用 setFilter()后无需调用 select()函数就可以立即刷新记录,若要取消过滤条件,只需在setFilter()函数里传递一个空字符串。
(1)程序框架
(2)qwcomboboxdelegate.h
#ifndef QWCOMBOBOXDELEGATE_H
#define QWCOMBOBOXDELEGATE_H
#include
#include
class QWComboBoxDelegate : public QStyledItemDelegate
{
Q_OBJECT
private:
QStringList m_ItemList;//选择列表
bool m_isEdit; //是否可编辑
public:
QWComboBoxDelegate(QObject *parent=0);
void setItems(QStringList items, bool isEdit);//初始化设置列表内容,是否可编辑
//自定义代理组件必须继承以下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
(3)qwcomboboxdelegate.cpp
#include "qwcomboboxdelegate.h"
#include
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);
}
(4)mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
#include
#include
#include
#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_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_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
(5)mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include
#include
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);
}
void MainWindow::getFieldNames()
{ //获取所有字段名称
QSqlRecord emptyRec=tabModel->record();//获取空记录,只有字段名
for (int i=0;i<emptyRec.count();i++)
ui->comboFields->addItem(emptyRec.fieldName(i));
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setCentralWidget(ui->splitter);
// tableView显示属性设置
ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);
ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection);
ui->tableView->setAlternatingRowColors(true);
// ui->tableView->resizeColumnsToContents();
// ui->tableView->horizontalHeader()->setStretchLastSection(true);
}
MainWindow::~MainWindow()
{
delete ui;
}
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());
}
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()); //更细数据映射的行号
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()));
}
}
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); //设置数据库名称
// DB.setHostName();
// DB.setUserName();
// DB.setPassword();
if (!DB.open()) //打开数据库
{
QMessageBox::warning(this, "错误", "打开数据库失败",
QMessageBox::Ok,QMessageBox::NoButton);
return;
}
//打开数据表
openTable();
}
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);
}
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);
}
}
void MainWindow::on_actRecDelete_triggered()
{//删除当前记录
QModelIndex curIndex=theSelection->currentIndex();//获取当前选择单元格的模型索引
tabModel->removeRow(curIndex.row()); //删除最后一行
}
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();
int curRecNo=theSelection->currentIndex().row();
QSqlRecord curRec=tabModel->record(curRecNo); //获取当前记录
curRec.setValue("Photo",data); //设置字段数据
tabModel->setRecord(curRecNo,curRec);
QPixmap pic;
pic.load(aFile); //在界面上显示
ui->dbLabPhoto->setPixmap(pic.scaledToWidth(ui->dbLabPhoto->width()));
}
void MainWindow::on_actPhotoClear_triggered()
{
int curRecNo=theSelection->currentIndex().row();
QSqlRecord curRec=tabModel->record(curRecNo); //获取当前记录
curRec.setNull("Photo");//设置为空值
tabModel->setRecord(curRecNo,curRec);
ui->dbLabPhoto->clear();
}
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("");
}
void MainWindow::on_comboFields_currentIndexChanged(int index)
{//选择字段进行排序
if (ui->radioBtnAscend->isChecked())
tabModel->setSort(index,Qt::AscendingOrder);
else
tabModel->setSort(index,Qt::DescendingOrder);
tabModel->select();
}
void MainWindow::on_actScan_triggered()
{//涨工资,记录遍历
if (tabModel->rowCount()==0)
return;
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;irowCount();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);
}