11-2_Qt 5.9 C++开发指南_QSqlQueryModel的使用(QSqlQueryModel 只能作为只读数据源使用,不可以编辑数据)

2023-11-10

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 &current, 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 &current, 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);//设置刚插入的行为当前选择行
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

11-2_Qt 5.9 C++开发指南_QSqlQueryModel的使用(QSqlQueryModel 只能作为只读数据源使用,不可以编辑数据) 的相关文章

  • 如何将 std::string& 转换为 C# 引用字符串

    我正在尝试将 C 函数转换为std string参考C 我的 API 如下所示 void GetStringDemo std string str 理想情况下 我希望在 C 中看到类似的东西 void GetStringDemoWrap r
  • 调用 McAfee 病毒扫描引擎

    我收到客户的请求 要求使用他们服务器上的 McAfee 病毒扫描将病毒扫描集成到应用程序中 我做了一些调查 发现 McScan32 dll 是主要的扫描引擎 它导出各种看起来有用的函数 我还发现提到了 McAfee Scan Engine
  • C# 异步等待澄清?

    我读了here http blog stephencleary com 2012 02 async and await html that 等待检查等待的看看它是否有already完全的 如果 可等待已经完成 那么该方法将继续 运行 同步
  • 如何在 SQLite 中将时间戳转换为字符串?

    我有一个表 其中存储了时间戳 以毫秒为单位 我想将这些时间戳转换为人类可读的形式 这是我的表的输出示例 SELECT date raw strftime d m Y date 1000 as string FROM my table raw
  • 在一个数据访问层中处理多个连接字符串

    我有一个有趣的困境 我目前有一个数据访问层 它必须与多个域一起使用 并且每个域都有多个数据库存储库 具体取决于所调用的存储过程 目前 我只需使用 SWITCH 语句来确定应用程序正在运行的计算机 并从 Web config 返回适当的连接字
  • 机器Epsilon精度差异

    我正在尝试计算 C 中双精度数和浮点数的机器 epsilon 值 作为学校作业的一部分 我在 Windows 7 64 位中使用 Cygwin 代码如下 include
  • std::list 线程push_back、front、pop_front

    std list 线程安全吗 我假设不是这样 所以我添加了自己的同步机制 我认为我有正确的术语 但我仍然遇到问题 每个函数都由单独的线程调用 Thread1 不能等待 它必须尽可能快 std list
  • free 和 malloc 在 C 中如何工作?

    我试图弄清楚如果我尝试 从中间 释放指针会发生什么 例如 看下面的代码 char ptr char malloc 10 sizeof char for char i 0 i lt 10 i ptr i i 10 ptr ptr ptr pt
  • 传递给函数时多维数组的指针类型是什么? [复制]

    这个问题在这里已经有答案了 我在大学课堂上学习了 C 语言和指针 除了多维数组和指针之间的相似性之外 我认为我已经很好地掌握了这个概念 我认为由于所有数组 甚至多维 都存储在连续内存中 因此您可以安全地将其转换为int 假设给定的数组是in
  • 需要帮助优化算法 - 两百万以下所有素数的总和

    我正在尝试做一个欧拉计划 http projecteuler net问题 我正在寻找 2 000 000 以下所有素数的总和 这就是我所拥有的 int main int argc char argv unsigned long int su
  • 访问外部窗口句柄

    我当前正在处理的程序有问题 这是由于 vista Windows 7 中增强的安全性引起的 特别是 UIPI 它阻止完整性级别较低的窗口与较高完整性级别的窗口 对话 就我而言 我想告诉具有高完整性级别的窗口进入我们的应用程序 它在 XP 或
  • 重载 (c)begin/(c)end

    我试图超载 c begin c end类的函数 以便能够调用 C 11 基于范围的 for 循环 它在大多数情况下都有效 但我无法理解和解决其中一个问题 for auto const point fProjectData gt getPoi
  • 如何在当前 Visual Studio 主机内的 Visual Studio 扩展中调试使用 Roslyn 编译的代码?

    我有一个 Visual Studio 扩展 它使用 Roslyn 获取当前打开的解决方案中的项目 编译它并从中运行方法 程序员可以修改该项目 我已从当前 VisualStudioWorkspace 成功编译了 Visual Studio 扩
  • 为什么 isnormal() 说一个值是正常的,而实际上不是?

    include
  • 如何在 Linq to SQL 中使用distinct 和 group by

    我正在尝试将以下 sql 转换为 Linq 2 SQL select groupId count distinct userId from processroundissueinstance group by groupId 这是我的代码
  • 有没有办法让 doxygen 自动处理未记录的 C 代码?

    通常它会忽略未记录的 C 文件 但我想测试 Callgraph 功能 例如 您知道在不更改 C 文件的情况下解决此问题的方法吗 设置变量EXTRACT ALL YES在你的 Doxyfile 中
  • C# 中的 IPC 机制 - 用法和最佳实践

    不久前我在 Win32 代码中使用了 IPC 临界区 事件和信号量 NET环境下场景如何 是否有任何教程解释所有可用选项以及何时使用以及为什么 微软最近在IPC方面的东西是Windows 通信基础 http en wikipedia org
  • 为什么C++代码执行速度比java慢?

    我最近用 Java 编写了一个计算密集型算法 然后将其翻译为 C 令我惊讶的是 C 的执行速度要慢得多 我现在已经编写了一个更短的 Java 测试程序和一个相应的 C 程序 见下文 我的原始代码具有大量数组访问功能 测试代码也是如此 C 的
  • 指针和内存范围

    我已经用 C 语言编程有一段时间了 但对 C 语言还是很陌生 有时我对 C 处理内存的方式感到困惑 考虑以下有效的 C 代码片段 const char string void where is this pointer variable l
  • 类型或命名空间“MyNamespace”不存在等

    我有通常的类型或命名空间名称不存在错误 除了我引用了程序集 using 语句没有显示为不正确 并且我引用的类是公共的 事实上 我在不同的解决方案中引用并使用相同的程序集来执行相同的操作 并且效果很好 顺便说一句 这是VS2010 有人有什么

随机推荐

  • TortoiseGit 如何回退到以前的版本?

    要在 TortoiseGit 中回退到以前的版本 可以按照以下步骤进行操作 在资源管理器中 右键单击你的 Git 仓库文件夹 然后选择 TortoiseGit 再选择 Show log 这将打开 TortoiseGit 的日志界面 在日志界
  • 【华为OD】

    华为OD试题注意事项 使用合适的编程语言 在华为OD机试中多数情况下使用C 或Java 按照题目要求进行编码 仔细阅读题目描述并理解要求 在编码前可以进行伪代码编写或画流程图有助于理解和排除逻辑错误 注意代码的规范性 注重代码的可读性和可维
  • 测试架构师的职责及困境

    架构师 架构师来自于建筑学 英文是Architect 建筑工程中的架构师是负责整体建筑的架构设计 因此从宏观上看 软件行业的架构师也类似 是负责整体架构的设计 在软件工程中架构师是一个团队的技术的领头者 主要工作内容除去对项目的整体设计和规
  • redis一主二从时,主中读取不到从的信息

    一 错误情境描述 1 主 6379 2 从1 6380 3 从2 6381 二 错误原因 主中带有密码 三 解决办法 1 将主中配置文件中注释掉代码 2 在从的配置文件中添加主的密码 当master服务设置了密码保护时 slav服务连接ma
  • uniapp--- 微信小程序 用户隐私新规相关代码调整【vue3+ts+uView框架】

    uniapp 微信小程序 用户隐私新规相关代码调整 vue3 ts uView框架 官方公告地址 https developers weixin qq com community develop doc 00042e3ef54940ce85
  • elementUI中的$confirm调换两个按钮的位置

    confirm默认两个按钮的位置为 取消在前 确认在后 而我们在项目中经常要求 确认在前 取消在后 所以需要调换两个按钮的位置 修改后的样式如下图所示 用css样式调换两个按钮的位置 代码如下 给取消按钮添加样式 this confirm
  • 关于取模运算的特点与应用

    对于取模 取余 运算 比如A M 结果永远都是在 0 M 1 之间循环 并且如果A lt M 则结果和没有进行取模运算一样 这一特点有很多应用场景 1 最常见的就是对2取模来判断奇偶数 2 循环队列中通过对最大容量取模来控制数组下标 防止索
  • 资源记录

    AE插件 https zhuanlan zhihu com p 26304609 GLSL内置函数使用 https blog csdn net jeffasd article details 77989274 ops request mis
  • js中forEache()和Map()的区别

    定义剖析 我们首先来看一看MDN上对Map和ForEach的定义 forEach 针对每一个元素执行提供的函数 executes a provided function once for each array element map 创建一
  • 地埋式积水在线监测系统助力城市内涝解决方案

    一 方案背景 随着我国城镇化快速发展 城市建设产生的大量地面硬底化 大部分的降雨将形成地表径流 仅有少量雨水渗入地下 导致城市内涝等一系列问题 当前 全国多地发生洪涝 我国南北方全面进入主汛期 与往年相比 今年的汛期不仅提前4天 而且汛情呈
  • 3.app自动化项目

    app自动化项目 我们可以使用AirtestIDE工具进行脚本的调试 元素的定位等辅助功能 但是真正意义上的脚本 在 AirtestIDE 工具中实现还是比较麻烦 问题 1 如何使用pycharm实现airtest内容脚本 解决方案 如果是
  • JQuery入门

    JQuery jQuery 是一个 JavaScript 库 所谓的库 就是一个 JS 文件 里面封装了很多预定义的函数 比如获取元素 执行隐藏 移动等 目的就 是在使用时直接调用 不需要再重复定义 这样就可以极大地简化了 JavaScri
  • 华为OD机试 -求最大连续bit数(C++ & Java & JS & Python)

    描述 求一个int类型数字对应的二进制数字中1的最大连续数 例如3的二进制为00000011 最大连续2个1 数据范围 数据组数 1 5 1 t 5 1 500000 1 n 500000 进阶 时间复杂度 O logn 空间复杂度 1 O
  • 出现d3dcompiler_41.dll错误怎么解决

    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题 如果是新手第一时间会认为是软件或游戏出错了 其实并不是这样 其主要原因就是你电脑系统的该dll文件丢失了或者损坏了 这时你只需下载这个d3dcompiler 41 dll文件进行安装
  • React+AntDesign结合Table、Modal+Form的使用,以及Ant Design 4.x Modal + Form搭配使用时,如何在Modal中使用表单验证

    React AntDesign结合Table Modal Form的使用 最近写React AntDesign时 使用到Modal和Form组件时候 因为Modal组件自带onOk按钮 因此不能使用Form自带的onFinish函数进行表单
  • 【转载】三十而已,信智依然

    1993年4月17日 我和丁健等几位留美学生在德克萨斯创立了AsiaInfo 亚信 如今正好30周年 图 亚信科技初期骨干人员合影 论语 讲 三十而立 对于人生来说 三十岁既意味着触摸到青春的峰值 也代表着爬上了成熟的阶梯 而对于企业来说
  • 一文了解快手广告生态:磁力智投、磁力金牛、磁力聚星、磁力万象、磁力方舟!

    抖音和快手的对照表 通过抖音和快手的对照表 我们可以知道磁力智投是快手的信息流投放平台 主要做站外引流 磁力金牛则对标巨量千川 主要做快手电商的站内闭环 磁力聚星则是围绕着达人营销 协助于品牌做出达播和自播兼具的经营阵地 也帮助达人在变现上
  • 【2023持续更新】网络安全工程师常用网站集合

    文章目录 信息搜集 子域名搜集 在线工具 信息处理 威胁情报及分析 在线靶场 综合学习 安全资讯技术 大会演讲PPT 安全社区 社工工程学 信息搜集 http www yunsee cn http finger tidesec com ht
  • Vue.js子级向父级传递数据

    组件之间的数据的传递 子传父 注意 html对大小写不敏感 事件名字最好用 隔开或全部小写 步骤 1 在父组件中在子组件上添加事件 自定义 监听 代表的含意 事件分两类 浏览器自带的事件 click mouseover mousedown
  • 11-2_Qt 5.9 C++开发指南_QSqlQueryModel的使用(QSqlQueryModel 只能作为只读数据源使用,不可以编辑数据)

    文章目录 1 QSqlQueryModel 功能概述 2 使用 QSqlQueryModel 实现数据查询 2 1 实例功能 2 2 可视化UI设计 2 3 主窗口类定义 去除自动生成的槽函数 2 4 打开数据库 2 5 记录移动 1 QS