文章目录
- 1 QSqlQueryModel 功能概述
- 2 使用 QSqlQueryModel 实现数据查询
- 2.1 实例功能
- 2.2 可视化UI设计
- 2.3 主窗口类定义(去除自动生成的槽函数)
- 2.4 打开数据库
- 2.5 记录移动
1 QSqlQueryModel 功能概述
从下图中可以看到,QSqlQueryModel 是 QSqlTableModel 的父类。QSqlQueryModel封装了执行 SELECT 语句从数据库查询数据的功能,但是 QSqlQueryModel 只能作为只读数据源使用,不可以编辑数据。
QSqlQueryModel 类的主要接口函数见表 11-10(省略了函数中的 const 关键字和缺省参数)。
使用QSqlQueryModel作为数据模型从数据库里查询数据,只需使用 setQuery()函数设置一个SELECT 查询语句即可。QSqlQueryModel 可以作为 QTableView 等视图组件的数据源,也可以使用QDataWidgetMapper 创建字段与界面组件的映射,只是查询出来的数据是不可编辑的。
2 使用 QSqlQueryModel 实现数据查询
2.1 实例功能
使用 QSqlQueryModel 可以从一个数据表或多个数据表里查询数据,只需设计好 SELECT 语句即可。实例 samp11_2 使用 QSqQueryModel从employee 表里查询记录,并在界面上显示,运行窗口见下图。
窗口工具栏上几个工具栏按钮只有打开数据库和记录移动功能,记录移动通过调用QdataWidget-Mapper 类的记录移动功能实现。窗口上没有数据编辑和保存功能,因为 QSqlQueryModel 查询出来的数据是只读的。
2.2 可视化UI设计
samp11_2采用可视化UI设计,具体框架如下图所示
2.3 主窗口类定义(去除自动生成的槽函数)
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QLabel>
#include <QString>
#include <QtSql>
#include <QDataWidgetMapper>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
private:
QLabel *LabInfo;
QSqlDatabase DB; //数据库
QSqlQueryModel *qryModel; //数据模型
QItemSelectionModel *theSelection; //选择模型
QDataWidgetMapper *dataMapper;//数据界面映射
void openTable();//打开数据表
void refreshTableView();//移动记录时刷新TableView的当前行
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
// QTableView的SelectionModel的行发生了变化,进行处理
void on_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous);
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
这里定义了 QSqlQueryModel 类型的数据模型变量 qryModel,也定义了数据库、选择模型和数据界面映射的变量。refreshTableView()函数用于记录移动后刷新 tableView 上的当前行位置。
自定义槽函数on_currentRowChanged()在选择模型的当前行变化时,处理 Photo 字段的查询与照片显示。
2.4 打开数据库
工具栏上的“打开数据库”按钮对应 actOpenDB,选择SQLite数据库文件,然后调用openTable()函数打开数据库。槽函数 on_actOpenDB_triggered()的代码与实例 samp11_1 完全一样,这里不再列出。
openTable()用于查询数据,建立界面显示等具体操作,其代码如下:
void MainWindow::openTable()
{//打开数据表
qryModel=new QSqlQueryModel(this);
qryModel->setQuery("SELECT empNo, Name, Gender, Height, Birthday, Mobile, Province, City, Department, "
" Education, Salary FROM employee ORDER BY empNo");
if (qryModel->lastError().isValid())
{
QMessageBox::critical(this, "错误", "数据表查询错误,错误信息\n"+qryModel->lastError().text(),
QMessageBox::Ok,QMessageBox::NoButton);
return;
}
LabInfo->setText(QString::asprintf("记录条数:%d",qryModel->rowCount()));
qryModel->setHeaderData(0,Qt::Horizontal,"工号");
qryModel->setHeaderData(1,Qt::Horizontal,"姓名");
qryModel->setHeaderData(2,Qt::Horizontal,"性别");
qryModel->setHeaderData(3,Qt::Horizontal,"身高");
qryModel->setHeaderData(4,Qt::Horizontal,"出生日期");
qryModel->setHeaderData(5,Qt::Horizontal,"手机");
qryModel->setHeaderData(6,Qt::Horizontal,"省份");
qryModel->setHeaderData(7,Qt::Horizontal,"城市");
qryModel->setHeaderData(8,Qt::Horizontal,"部门");
qryModel->setHeaderData(9,Qt::Horizontal,"学历");
qryModel->setHeaderData(10,Qt::Horizontal,"工资");
theSelection=new QItemSelectionModel(qryModel);
//选择行变化时
connect(theSelection,SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
this,SLOT(on_currentRowChanged(QModelIndex,QModelIndex)));
ui->tableView->setModel(qryModel);
ui->tableView->setSelectionModel(theSelection);
// ui->tableView->resizeColumnsToContents();
// ui->tableView->horizontalHeader()->setStretchLastSection(true);
//创建数据映射
dataMapper= new QDataWidgetMapper();
dataMapper->setSubmitPolicy(QDataWidgetMapper::AutoSubmit);
dataMapper->setModel(qryModel);
dataMapper->addMapping(ui->dbSpinEmpNo,0);//"empNo";
dataMapper->addMapping(ui->dbEditName,1);//"Name";
dataMapper->addMapping(ui->dbComboSex,2);//"Gender";
dataMapper->addMapping(ui->dbSpinHeight,3);//"Height";
dataMapper->addMapping(ui->dbEditBirth,4);//"Birthday";
dataMapper->addMapping(ui->dbEditMobile,5);//"Mobile";
dataMapper->addMapping(ui->dbComboProvince,6);//"Province";
dataMapper->addMapping(ui->dbEditCity,7);//"City";
dataMapper->addMapping(ui->dbComboDep,8);//"Department";
dataMapper->addMapping(ui->dbComboEdu,9);//"Education";
dataMapper->addMapping(ui->dbSpinSalary,10);//"Salary";
dataMapper->toFirst();
ui->actOpenDB->setEnabled(false);
}
程序首先创建了 QSqlQueryModel 类型的私有变量 qryModel,然后调用 setQuery()函数设置了SELECT 查询语句,SELECT 语句从 employee 表里查询除了 Memo 和 Photo 之外的所有其他字段。
使用 setHeaderData()函数为每个字段设置显示标题,为使代码简化,这里直接使用了字段的序号。
下面是槽函数on_currentRowChange()的代码:
void MainWindow::on_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous)
{
Q_UNUSED(previous);
if (!current.isValid())
{
ui->dbLabPhoto->clear();
return;
}
dataMapper->setCurrentModelIndex(current); //更新数据映射的行号
bool first=(current.row()==0); //是否首记录
bool last=(current.row()==qryModel->rowCount()-1);//是否尾记录
ui->actRecFirst->setEnabled(!first); //更新使能状态
ui->actRecPrevious->setEnabled(!first);
ui->actRecNext->setEnabled(!last);
ui->actRecLast->setEnabled(!last);
int curRecNo=theSelection->currentIndex().row();
QSqlRecord curRec=qryModel->record(curRecNo); //获取当前记录
int empNo=curRec.value("EmpNo").toInt();
QSqlQuery query; //查询当前empNo的Memo和Photo字段的数据
query.prepare("select EmpNo, Memo, Photo from employee where EmpNo = :ID");
query.bindValue(":ID",empNo);
query.exec();
query.first();
QVariant va=query.value("Photo");//
if (!va.isValid()) //图片字段内容为空
ui->dbLabPhoto->clear();
else
{//显示照片
QByteArray data=va.toByteArray();
QPixmap pic;
pic.loadFromData(data);
ui->dbLabPhoto->setPixmap(pic.scaledToWidth(ui->dbLabPhoto->size().width()));
}
QVariant va2=query.value("Memo");//显示备注
ui->dbEditMemo->setPlainText(va2.toString());
}
这个函数实现了3个功能,第1个功能是更新数据映射的行号,即:
dataMapper->setCurrentModelIndex(current); //更新数据映射的行号
使窗口上的字段关联的显示组件刷新显示当前记录的内容
第 2个功能是根据当前行号,判断是否是首记录或尾记录,以此更新界面上 4 个记录移动的Action的使能状态。
第3 个功能是获取当前记录的 EmpNo 字段的值 (即员工编号),然后用一个 QSqlQuery 变量query 执行查询语句,只查询出这个员工的 Memo 和 Photo 字段的数据,然后在界面元件上显示。这里使用了 QSqlQuery 类,它用来执行任意的 SQL 语句。
在QSqlQueryModel 类的变量 qryModel 里设置 SELECT 语句时,并没有查询所有字段,因为 Photo是BLOB 字段,全部查询出来后必然占用较大内存,而且在做记录遍历时,如果存在 BLOB 字段数据执行速度会很慢。所以,这个实例里将普通字段的查询用 QSqlQueryModel 来查询并显示,而 Memo和 Photo 字段数据的查询采用按需查询的方式,这样可以减少内存消耗,提高记录遍历时的执行速度
openTable()函数剩余的部分是设置 tableView 的数据模型和选择模型,然后创建数据界面映射变量 dataMapper,设置各个界面组件与字段的映射关系。
2.5 记录移动
用于数据映射的 QDataWidgetMapper 类设置数据模型后,总是指向数据模型的当前记录。QDataWidgetMapper 有 4 个函数进行当前记录的移动,分别是 toFirst()、toLast()、toNext()和toPrevious()。当前记录移动时,会引起数据模型关联的选择模型发射 currentRowChanged()信号,也就会执行关联的自定义槽函数 on currentRowChanged()。
工具栏上有 4 个记录移动的按钮,它们调用 QDataWidgetMapper 的记录移动函数实现记录移动,4个Action 的槽函数代码如下:
void MainWindow::on_actRecFirst_triggered()
{ //首记录
dataMapper->toFirst();
refreshTableView();
}
void MainWindow::on_actRecPrevious_triggered()
{ //前一条记录
dataMapper->toPrevious();
refreshTableView();
}
void MainWindow::on_actRecNext_triggered()
{//后一条记录
dataMapper->toNext();
refreshTableView();
}
void MainWindow::on_actRecLast_triggered()
{//最后一条记录
dataMapper->toLast();
refreshTableView();
}
使用QDataWidgetMapper 的记录移动操作后,QDataWidgetMapper 会移动到新的记录上,映射了字段的界面组件也会自动显示新记录的字段的数据。但是,tableView 的当前行并不会自动变化,所以需要调用 refreshTableView()函数刷新 tableView 的显示,refreshTableView()函数的代码如下:
void MainWindow::refreshTableView()
{//刷新tableView的当前选择行
int index=dataMapper->currentIndex();
QModelIndex curIndex=qryModel->index(index,1);//
theSelection->clearSelection();//清空选择项
theSelection->setCurrentIndex(curIndex,QItemSelectionModel::Select);//设置刚插入的行为当前选择行
}