超级版主
- 积分
- 4672
- 金钱
- 4672
- 注册时间
- 2019-5-8
- 在线时间
- 1224 小时
|
1)实验平台:正点原子阿尔法Linux开发板
2) 章节摘自【正点原子】《I.MX6U嵌入式Qt开发指南 V1.0》
3)购买链接:https://detail.tmall.com/item.htm?id=609033604451
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/arm-linux/zdyz-i.mx6ull.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子阿尔法Linux交流群:1027879335
第十三章 数据库
数据库是什么?简易言之,就是保存数据的文件。可以存储大量数据,包括插入数据、更新数据、截取数据等。用专业术语来说,数据库是“按照数据结构来组织、存储和管理数据的仓库”。是一个长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集合。
什么时候需要数据库?在嵌入式里,存储大量数据,或者记录数据,就需要用到数据库。举个简单的例子,比如手机的闹钟就使用到了数据库,我们设置的闹钟数据将会保存到数据库里,闹钟程序运行时会从数据库里读取出上次保存的闹钟数据。如果没有数据库,则闹钟程序关机了数据不保存在物理储存设备里,下次运行闹钟时就没有上次设置的闹钟数据,这显然是不合理的。所以我们需要用到数据库。
本章认为读者已经基本了解数据库,已经对数据库有一定的认识,如果没有对数据库了解,请自行学习,毕竟本书是讲Qt的,不是讲数据库,数据库知识很多,而我们只是讲解Qt怎么去用数据库,对数据库的简单操作!目的就是在Qt里使用数据库!
想要在项目中使用Qt SQL模块,需要在项目配置文件里添加如下语句。
13.1 Qt SQL简介
Qt SQL模块为数据库提供了编程支持,Qt支持很多种常见的数据库,如MySQL、Oracle、MS SQL Server、SQLite等。Qt SQL模块里包含了很多个类,可以轻松实现数据库的连接、执行SQL语句,获取数据库里的数据与界面显示等功能,一般数据与界面之间会采用Model/View架构,从而很方便的数据界面显示和操作数据库。
在嵌入式里,一般常用的数据库就是Sqlite3。SQLite 是非常小的,是轻量级的,完全配置时小于 400KiB,省略可选功能配置时小于250KiB。SQLite是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。它是一个零配置的数据库,这意味着与其他数据库不一样,您不需要在系统中配置。就像其他数据库,SQLite 引擎不是一个独立的进程,可以按应用程序需求进行静态或动态连接。SQLite 可以直接访问其存储文件。
本章主要对Sqlite3进行实验。需要用其他数据库的请自行学习,在我们正点原子里Linux开发板里就是用sqlite3,文件系统里不提供其他数据库类型。嵌入式一般是用sqlite3,如需要其他类型数据库,请自行移植与学习!
13.2 应用实例
本章不讲解数据库中的语法,本书认为读者是已经数据库语法有一定了解的了,请知悉!详细声明请看本章前言!本章前言。
Model(模型),复杂的事情往往可以简单化,Qt提供了QSqlDatabase类用于建立数据库的连接,往往以指定加载的数据库驱动,然后设置数据库的登录参数,如主机地址,用户名、登录密码等。这些都是服务器类型的数据库所需要做的操作。恰好单机型(本地数据库类型)的Sqlite3数据库就不需要设置登录参数就可以方便的打开数据库进行操作了。在QSqlDatabase连接数据库后,用QSqlTableModel从数据库里读取出表格模型,然后通过Qt的QTableView类显示数据库的内容在我们面前。需要对数据库的数据进行修改可以使用QSqlQuery,或者直接修改QSqlTableModel对象,修改里面的模型数据即可!Qt对数据库的基本操作流程大概是这样子,当然Qt提供了很多操作数据库的类,我们只讲解基本的与常用的就已经足够了。下面用个图示来再对上面的操作深入了解一下。
13.2.1 实用闹钟(非QTableView显示)
一般显示数据库表格会使用QTableView显示,但是QTableView适合专业看数员看且适用于对界面操作要求不高的开发人员看。如果直接用这种表格展示给一般用户看,估计用户看数据得头皮发麻。本例就如本章开头所说,结合数据库开发一个闹钟实例,记录新建的闹钟数据,可以对闹钟进行增、删、改等操作。注意(闹钟不做响铃操作设计。可后期使用本例自行开发,本例主要讲解不使用QTableView如何对数据库表格的操作)。本小节开发的闹钟实例很好理解,它与手机的闹钟操作基本一模一样,读者理解起来不会很吃力。本例程序篇幅过长,请注意,我们只需要关注mainwindow.h和mainwindow.cpp这两个文件即可!其他的.h和.cpp文件是编者为了界面的好看参考了一些博客而设计的程序。主要实现了数字选择器的功能和闹钟开关按钮的功能,因为Qt C++里它本身没有这种好看的控件,所以得自行设计。由于篇幅过长,数字选择器与闹钟开关的代码不作分析(有兴趣自行分析),我们可以直接将它们当作普通的控件来用即可!重点是mainwindow.h和mainwindow.cpp里的数据库操作。编者写这个例子都要好长时间,希望读者不要一口吃个胖老虎,急于求成,本例界面看似简单,可能大多数读者可能对数据库并不是很了解!理解这个例子时,编者担心是在看天书一样!抓住我们想要理解的重点即可!不必要每句都去理解!
本例目的:了解不使用QTableView的情况下,也能把数据表完好展示在用户面前。
例17_sqlite_alarm,实用闹钟(难度:很难【为什么定义为很难,编者认为大多数读者对数据库没有一定的了解】)。项目路径为Qt/2/17_sqlite_alarm。
项目文件17_sqlite_alarm文件第一行添加的代码部分如下。
- 17_sqlite_alarm.pro编程后的代码
- 1 QT += core gui sql
- 2
- 3 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
- 4
- 5 CONFIG += c++11
- 6
- 7 # The following define makes your compiler emit warnings if you use
- 8 # any Qt feature that has been marked deprecated (the exact warnings
- 9 # depend on your compiler). Please consult the documentation of the
- 10 # deprecated API in order to know how to port your code away from it.
- 11 DEFINES += QT_DEPRECATED_WARNINGS
- 12
- 13 # You can also make your code fail to compile if it uses deprecated APIs.
- 14 # In order to do so, uncomment the following line.
- 15 # You can also select to disable deprecated APIs only up to a certain version of Qt.
- 16 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
- 17
- 18 SOURCES += \
- 19 main.cpp \
- 20 mainwindow.cpp \
- 21 numberpicker.cpp \
- 22 switchbutton.cpp
- 23
- 24 HEADERS += \
- 25 mainwindow.h \
- 26 numberpicker.h \
- 27 switchbutton.h
- 28
- 29 # Default rules for deployment.
- 30 qnx: target.path = /tmp/${TARGET}/bin
- 31 else: unix:!android: target.path = /opt/${TARGET}/bin
- 32 !isEmpty(target.path): INSTALLS += target
- 33
- 34 RESOURCES += \
- 35 res.qrc
复制代码
在头文件“mainwindow.h”具体代码如下。
头文件主要声明布局用的类和数据库,重要关注是QSqlDatabase和QSqlTableModel。这里声明的是全局变量。
在源文件“mainwindow.cpp”具体代码如下。
- mainwindow.cpp编程后的代码
- /******************************************************************
- Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
- * @projectName 17_sqlite_example
- * @brief mainwindow.cpp
- * @author Deng Zhimao
- * @email <a href="mailto:1252699831@qq.com">1252699831@qq.com</a>
- * @net <a href="www.openedv.com" target="_blank">www.openedv.com</a>
- * @date 2021-05-15
- *******************************************************************/
- 1 #include "mainwindow.h"
- 2 #include <QDebug>
- 3 #include <QSqlError>
- 4
- 5 MainWindow::MainWindow(QWidget *parent)
- 6 : QMainWindow(parent)
- 7 {
- 8 /* 设置主窗体的显示位置与大小 */
- 9 this->setGeometry(0, 0, 800, 480);
- 10
- 11 /* 查看本机可用的数据库驱动 */
- 12 QStringList drivers = QSqlDatabase::drivers();
- 13 foreach(QString driver, drivers) {
- 14 qDebug()<<driver;
- 15 }
- 16
- 17 /* 以QSQLITE驱动方式打开或者创建数据库 */
- 18 sqlDatabase = QSqlDatabase::addDatabase("QSQLITE");
- 19 sqlDatabase.setDatabaseName("alarm.db");
- 20 /* 以open的方式打开alarm.db数据库,则会创建一个alarm.db */
- 21 if (!sqlDatabase.open())
- 22 qDebug()<<"连接数据库错误"<<sqlDatabase.lastError()<<endl;
- 23 else
- 24 qDebug()<<"连接数据库成功"<<endl;
- 25
- 26 QSqlQuery query(sqlDatabase);
- 27 /* 使用指令式创建表 */
- 28 query.exec("create table alarm (id int primary key, time vchar(15), flag vchar(5))");
- 29 /* 以指令的方式插入数据 */
- 30 //query.exec("insert into alarm values(0, '06:00', 'false')");
- 31
- 32 model = new QSqlTableModel(this, sqlDatabase);
- 33
- 34 /* 模型设置表的名字,需要与数据库的表的名字相同 */
- 35 model->setTable("alarm");
- 36
- 37 /* 如果有修改则同步修改到数据库,
- 38 * 注意这个规则需要与tabview这样的控件才生效,
- 39 * 因为tabview可以直接编辑表里的内容 */
- 40 model->setEditStrategy(QSqlTableModel::OnFieldChange);
- 41
- 42 /* 成功则返回true,查看数据库里是否有alarm这个表格 */
- 43 model->select();
- 44
- 45 /* 如果数据表数据为空,则添加两个闹钟 */
- 46 if (model->rowCount() == 0) {
- 47 /* 插入一行 */
- 48 model->insertRow(model->rowCount());
- 49 /* 在该行插入数据 */
- 50 model->setData(model->index(0, 0), 1);
- 51 model->setData(model->index(0, 1), "06:00");
- 52 model->setData(model->index(0, 2), "false");
- 53 /* 插入数据后记得提交 */
- 54 model->submit();
- 55
- 56 /* 再插入一行 */
- 57 model->insertRow(model->rowCount());
- 58 model->setData(model->index(1, 0), 2);
- 59 model->setData(model->index(1, 1), "18:00");
- 60 model->setData(model->index(1, 2), "true");
- 61 /* 提交 */
- 62 model->submit();
- 63 }
- 64
- 65 hourPicker = new NumberPicker(this);
- 66 hourPicker->setRange(0, 24);
- 67
- 68 minutePicker = new NumberPicker(this);
- 69 minutePicker->setRange(0, 60);
- 70
- 71 /* 标签,用于显示时&分 */
- 72 QLabel *label[3];
- 73 label[0] = new QLabel();
- 74 label[1] = new QLabel();
- 75 label[2] = new QLabel();
- 76
- 77 QFont font;
- 78 font.setBold(true);
- 79 font.setPixelSize(10);
- 80 QPalette pal;
- 81 pal.setBrush(QPalette::WindowText, QColor(0, 0, 0));
- 82
- 83 label[0]->setFont(font);
- 84 label[1]->setFont(font);
- 85 label[2]->setFont(font);
- 86
- 87 label[0]->setText(" ");
- 88 label[1]->setText("时");
- 89 label[2]->setText("分");
- 90
- 91 /* 主布局初始化 */
- 92 listWidget = new QListWidget();
- 93 mainWidget = new QWidget();
- 94 bottomWidget = new QWidget();
- 95 alarmDialog = new QDialog(this);
- 96 timeWidget = new QWidget();
- 97 btWidget = new QWidget();
- 98 addAlarm = new QPushButton();
- 99 yesButton = new QPushButton();
- 100 cancelButton = new QPushButton();
- 101 vBoxLayout[0] = new QVBoxLayout();
- 102 vBoxLayout[1] = new QVBoxLayout();
- 103 hBoxLayout[0] = new QHBoxLayout();
- 104 hBoxLayout[1] = new QHBoxLayout();
- 105 hBoxLayout[2] = new QHBoxLayout();
- 106
- 107 addAlarm->setMaximumSize(84, 84);
- 108 addAlarm->setObjectName("addAlarm");
- 109 addAlarm->setMinimumSize(84, 84);
- 110 bottomWidget->setMinimumHeight(84);
- 111 bottomWidget->setMaximumHeight(84);
- 112 yesButton->setText("确认");
- 113 cancelButton->setText("取消");
- 114 yesButton->setMaximumSize(100, 50);
- 115 yesButton->setMinimumSize(100, 50);
- 116 cancelButton->setMinimumSize(100, 50);
- 117 cancelButton->setMaximumSize(100, 50);
- 118 btWidget->setMaximumHeight(70);
- 119 btWidget->setMinimumHeight(70);
- 120 alarmDialog->setMinimumSize(300, 300);
- 121 alarmDialog->setMaximumSize(300, 300);
- 122 alarmDialog->setModal(true);
- 123 yesButton->setObjectName("yesButton");
- 124 cancelButton->setObjectName("cancelButton");
- 125
- 126 /* 主布局 */
- 127 vBoxLayout[0]->addWidget(listWidget);
- 128 vBoxLayout[0]->addWidget(bottomWidget);
- 129 vBoxLayout[0]->setContentsMargins(0, 0, 0, 0);
- 130
- 131 mainWidget->setLayout(vBoxLayout[0]);
- 132
- 133 setCentralWidget(mainWidget);
- 134
- 135 /* 底部按钮布局 */
- 136 hBoxLayout[0]->addWidget(addAlarm);
- 137 hBoxLayout[0]->setContentsMargins(0, 0, 0, 0);
- 138 bottomWidget->setLayout(hBoxLayout[0]);
- 139
- 140 /* 对话框布局 */
- 141 vBoxLayout[1]->addWidget(timeWidget);
- 142 vBoxLayout[1]->addWidget(btWidget);
- 143 vBoxLayout[1]->setContentsMargins(0, 0, 0, 0);
- 144 alarmDialog->setLayout(vBoxLayout[1]);
- 145
- 146 hBoxLayout[1]->addWidget(label[0]);
- 147 hBoxLayout[1]->addWidget(hourPicker);
- 148 hBoxLayout[1]->addWidget(label[1]);
- 149 hBoxLayout[1]->addWidget(minutePicker);
- 150 hBoxLayout[1]->addWidget(label[2]);
- 151 hBoxLayout[1]->setContentsMargins(0, 0, 0, 0);
- 152 timeWidget->setLayout(hBoxLayout[1]);
- 153
- 154 hBoxLayout[2]->addWidget(yesButton);
- 155 hBoxLayout[2]->addWidget(cancelButton);
- 156
- 157 btWidget->setLayout(hBoxLayout[2]);
- 158
- 159 /* 打印出闹钟数据库里的信息 */
- 160 for (int i = 0; i < model->rowCount(); i++) {
- 161 for (int j = 0; j < 3; j++) {
- 162 QModelIndex qindex = model->index(i, j);
- 163 switch (j) {
- 164 case 0:
- 165 qDebug()<<"第"<<model->data(qindex).toInt()<<"行数据";
- 166 break;
- 167 case 1:
- 168 listWidget->addItem(model->data(qindex).toString());
- 169 qDebug()<<"闹钟时间为:"<<model->data(qindex).toString();
- 170 break;
- 171 case 2:
- 172 qDebug()<<"闹钟状态为:"
- 173 <<model->data(qindex).toString()<<endl;
- 174 if (model->data(qindex).toString() != "true")
- 175 listWidget->item(i)
- 176 ->setTextColor(QColor(22, 22, 22, 60));
- 177 else
- 178 listWidget->item(i)
- 179 ->setTextColor(QColor(22, 22, 22, 225));
- 180 break;
- 181 default:
- 182 break;
- 183 }
- 184 }
- 185 }
- 186
- 187 /* 在列表里添加闹钟开关 */
- 188 for (int i = 0; i < model->rowCount(); i++) {
- 189 ItemObjectInfo info;
- 190 info.widget = new QWidget();
- 191 info.switchButton = new SwitchButton();
- 192 info.hBoxLayout = new QHBoxLayout();
- 193 info.switchButton->setMaximumSize(55, 30);
- 194 info.switchButton->setMinimumSize(55, 30);
- 195 info.hBoxLayout->setContentsMargins(0, 0, 0, 0);
- 196 info.hBoxLayout->setAlignment(Qt::AlignRight);
- 197 info.hBoxLayout->addWidget(info.switchButton);
- 198 info.widget->setLayout(info.hBoxLayout);
- 199 listWidget->setItemWidget(listWidget->item(i),
- 200 info.widget);
- 201 itemObjectInfo.append(info);
- 202
- 203 /* 连接信号槽 */
- 204 connect(info.switchButton,
- 205 SIGNAL(toggled(bool)),
- 206 this,
- 207 SLOT(switchButtonClicked(bool)));
- 208
- 209 /* 获取数据库里的闹钟开关状态 */
- 210 QModelIndex qindex = model->index(i, 2);
- 211 if (model->data(qindex).toBool())
- 212 /* 设置列表里的闹钟开关按钮状态 */
- 213 info.switchButton->setToggle(true);
- 214 }
- 215
- 216 /* 按钮 */
- 217 connect(addAlarm, SIGNAL(clicked()), this,
- 218 SLOT(addAlarmClicked()));
- 219
- 220 connect(yesButton, SIGNAL(clicked()), this,
- 221 SLOT(yesButtonClicked()));
- 222
- 223 connect(cancelButton, SIGNAL(clicked()), this,
- 224 SLOT(cancelButtonClicked()));
- 225
- 226 /* 列表 */
- 227 connect(listWidget,
- 228 SIGNAL(itemClicked(QListWidgetItem*)),
- 229 this,
- 230 SLOT(listWidgetItemClicked(QListWidgetItem*)));
- 231 }
- 232
- 233 MainWindow::~MainWindow()
- 234 {
- 235 /* 关闭数据库 */
- 236 sqlDatabase.close();
- 237 }
- 238
- 239 void MainWindow::addAlarmClicked()
- 240 {
- 241 /* 选择时间对话框里显示当前系统时间 */
- 242 hourPicker->setValue(QTime::currentTime().hour());
- 243 minutePicker->setValue(QTime::currentTime().minute());
- 244
- 245 /* 取消按钮显示文本为"取消" */
- 246 cancelButton->setText("取消");
- 247
- 248 /* 如果是点击添加闹钟的按钮,则设置闹钟列表的索引index为-1 */
- 249 listWidget->setCurrentRow(-1);
- 250
- 251 /* 显示对话框 */
- 252 alarmDialog->show();
- 253 }
- 254
- 255 void MainWindow::listWidgetItemClicked(QListWidgetItem *item)
- 256 {
- 257 /* 从被点击项里获取闹钟数据 */
- 258 QStringList list =
- 259 listWidget->item(listWidget->row(item))->text().split(":");
- 260
- 261 /* 选择时间对话框里显示被选择项的时间 */
- 262 hourPicker->setValue(list.at(0).toInt());
- 263 minutePicker->setValue(list.at(1).toInt());
- 264
- 265 /* 取消按钮显示文本为"删除" */
- 266 cancelButton->setText("删除");
- 267
- 268 /* 显示闹钟选择对话框 */
- 269 alarmDialog->show();
- 270
- 271 /* 作用使其失去选择 */
- 272 listWidget->clearSelection();
- 273 }
- 274
- 275 void MainWindow::yesButtonClicked()
- 276 {
- 277 /* 获取数值选择值的数据,转为字符串 */
- 278 QString hour;
- 279 QString minute;
- 280
- 281 if (hourPicker->readValue() < 10)
- 282 hour = "0" + QString::number(hourPicker->readValue()) + ":";
- 283 else
- 284 hour = QString::number(hourPicker->readValue()) + ":";
- 285
- 286 if (minutePicker->readValue() < 10)
- 287 minute = "0" + QString::number(minutePicker->readValue());
- 288 else
- 289 minute = QString::number(minutePicker->readValue());
- 290
- 291 /* 如果不是选中闹钟列表的数据 */
- 292 if (listWidget->currentRow() == -1) {
- 293 /* 插入一行数据,闹钟时间为选择的闹钟时间 */
- 294 int row = model->rowCount();
- 295
- 296 /* 插入数据到数据库 */
- 297 model->insertRow(row);
- 298 model->setData(model->index(row, 0), row + 1);
- 299 model->setData(model->index(row, 1), hour + minute);
- 300 model->setData(model->index(row, 2), "true");
- 301 model->submit();
- 302
- 303 /* 添加闹钟到列表 */
- 304 listWidget->addItem(hour + minute);
- 305
- 306 /* 添加到容器 */
- 307 ItemObjectInfo info;
- 308 info.widget = new QWidget();
- 309 info.switchButton = new SwitchButton();
- 310 info.hBoxLayout = new QHBoxLayout();
- 311 info.switchButton->setMaximumSize(55, 30);
- 312 info.switchButton->setMinimumSize(55, 30);
- 313 info.hBoxLayout->setContentsMargins(0, 0, 0, 0);
- 314 info.hBoxLayout->setAlignment(Qt::AlignRight);
- 315 info.hBoxLayout->addWidget(info.switchButton);
- 316 info.widget->setLayout(info.hBoxLayout);
- 317 info.switchButton->setToggle(true);
- 318
- 319 /* 连接信号槽 */
- 320 connect(info.switchButton, SIGNAL(toggled(bool)), this,
- 321 SLOT(switchButtonClicked(bool)));
- 322
- 323 listWidget->setItemWidget(
- 324 listWidget->item(listWidget->count() - 1),
- 325 info.widget);
- 326 itemObjectInfo.append(info);
- 327 } else {
- 328 /* 修改数据(更新闹钟数据) */
- 329 int row = listWidget->currentRow();
- 330 model->setData(model->index(row, 0), row + 1);
- 331 model->setData(model->index(row, 1), hour + minute);
- 332 model->setData(model->index(row, 2), "true");
- 333 model->submit();
- 334
- 335 /* 设置当前项的闹钟文本 */
- 336 listWidget->currentItem()->setText(hour + minute);
- 337 }
- 338
- 339 /* 再确保提交 */
- 340 if (model->isDirty())
- 341 model->submitAll();
- 342
- 343 /* 关闭对话框 */
- 344 alarmDialog->close();
- 345 }
- 346
- 347 void MainWindow::cancelButtonClicked()
- 348 {
- 349 if (cancelButton->text() == "删除") {
- 350 /* 删除数据库整一行数据 */
- 351 model->removeRow(listWidget->currentRow());
- 352 model->submit();
- 353 itemObjectInfo.remove(listWidget->currentRow());
- 354 listWidget->takeItem(listWidget->currentRow());
- 355 }
- 356
- 357 /* 再确保提交 */
- 358 if (model->isDirty())
- 359 model->submitAll();
- 360
- 361 /* 关闭对话框 */
- 362 alarmDialog->close();
- 363 }
- 364
- 365
- 366 /* 当点击闹钟开关时,将闹钟开关状态同步更新到数据库里 */
- 367 void MainWindow::switchButtonClicked(bool checked)
- 368 {
- 369 listWidget->clearSelection();
- 370
- 371 SwitchButton *button = (SwitchButton *)sender();
- 372 for (int i = 0; i < itemObjectInfo.count(); i++) {
- 373 if (button == itemObjectInfo.at(i).switchButton) {
- 374 if (checked) {
- 375 model->setData(model->index(i, 2), "true");
- 376 listWidget->item(i)
- 377 ->setTextColor(QColor(22, 22, 22, 225));
- 378 } else {
- 379 model->setData(model->index(i, 2), "false");
- 380 listWidget->item(i)
- 381 ->setTextColor(QColor(22, 22, 22, 60));
- 382 }
- 383
- 384 model->submit();
- 385 break;
- 386 }
- 387 }
- 388 }
复制代码
第5~231行,数据库的连接、建立模型和界面布局等。界面布局这些不再详细说,这些在前面章节已经讲过很多次。在这部分代码里,我们发现没有用到QTableView来展示我们的闹钟数据,原因很简单,因为我们的界面需要适合大众眼光,而不是展示一个表格,应该展示一个闹钟列表,进而编者设计使用了QListWidget这个控件。恰好与手机里的闹钟的列表相似。
12~15行,查看本地主机可用的数据库驱动。一般Qt 安装时都会自带Sqlite3驱动。注意了,如果本地主机没有可用的数据库,则实验不可操作!不可生搬硬套到其他开发板子测试。所以查看本地主机的数据库操作是调试时候必须的!
18~24行,添加一个数据库,以QSQLITE驱动方式打开或者连接名字为alarm.db的数据库文件。数据库存储的形式为一个alarm.db文件。
26~28行,在数据库里创建一个名字为alarm的表格。如果已经创建,也会覆盖这个表格名字,但是不会覆盖表格内容。必须先创建表格,才可以对表格的数据进行操作(增删查减等)。
32~43行,新建模型model,使用通过setTable()设置的表中的数据填充模型,使用指定的过滤器和排序条件,如果成功返回true;否则返回false。注意:调用select()将恢复任何未提交的更改,并删除任何插入的列。
46~63行,编者在这里判断,如果是刚运行该程序,发现数据库表中没有数据,则默认设置两行闹钟数据,数据ID为1,闹钟时间为06:00,状态为关;另一条是数据ID为2,闹钟时间为18:00,状态为开。到这里我们就已经学会在数据库里插入数据,记得插入数据后需要手动执行submit()函数,表示提交。不提交是不会记录保存到数据库里的。
328~336行,直接设置数据库里的行数据,即可覆盖该行数据的内容。
347~363行,model对象直接移除某一行的数据就完成删除数据库里某行内容。注意可以移除一行或者一列,闹钟数据是以每行记录保存,所以这里是移除一行。移除之后记得提交。
其他的内容都是一些逻辑与界面设计的内容,重点讲解的是Qt对数据库操作的步骤。其他内容请根据源码的注释理解即可。或者运行程序去理解本例逻辑。
main .cpp内容如下,主要是加载qss样式文件。
- 1 #include "mainwindow.h"
- 2
- 3 #include <QApplication>
- 4 #include <QFile>
- 5
- 6 int main(int argc, char *argv[])
- 7 {
- 8 QApplication a(argc, argv);
- 9 /* 指定文件 */
- 10 QFile file(":/style.qss");
- 11
- 12 /* 判断文件是否存在 */
- 13 if (file.exists() ) {
- 14 /* 以只读的方式打开 */
- 15 file.open(QFile::ReadOnly);
- 16 /* 以字符串的方式保存读出的结果 */
- 17 QString styleSheet = QLatin1String(file.readAll());
- 18 /* 设置全局样式 */
- 19 qApp->setStyleSheet(styleSheet);
- 20 /* 关闭文件 */
- 21 file.close();
- 22 }
- 23
- 24 MainWindow w;
- 25 w.show();
- 26 return a.exec();
- 27 }
复制代码
style.qss样式文件如下。素材已经在源码处提供。注意下面的style.qss不能有注释!
- 1 QListWidget {
- 2 font-size: 30px;
- 3 outline:none;
- 4 }
- 5
- 6 QListWidget::item:active {
- 7 background: transparent;
- 8 }
- 9
- 10 QListWidget::item {
- 11 height:80;
- 12 }
- 13
- 14 QListWidget::item:selected:hover {
- 15 background:#22222222;
- 16 }
- 17
- 18 QListWidget::item:selected {
- 19 background:transparent;
- 20 color:#ee222222;
- 21 }
- 22
- 23 QPushButton#addAlarm {
- 24 border-image:url(:/icons/addalarm1.png);
- 25 background:transparent;
- 26 outline: none;
- 27 }
- 28
- 29 QPushButton#addAlarm:hover {
- 30 border-image:url(:/icons/addalarm2.png);
- 31 }
- 32
- 33 QPushButton#yesButton {
- 34 border: 1px solid #22222222;
- 35 border-radius: 25px;
- 36 background:#22222222;
- 37 outline:none;
- 38 }
- 39
- 40 QPushButton#yesButton:pressed {
- 41 background:#44222222;
- 42 color:white;
- 43 }
- 44
- 45 QPushButton#cancelButton {
- 46 border: 1px solid #22222222;
- 47 border-radius: 25px;
- 48 background:#22222222;
- 49 outline:none;
- 50 }
- 51
- 52 QPushButton#cancelButton:pressed {
- 53 background:#44222222;
- 54 color:white;
- 55 }
- 56
- 57 QScrollBar:vertical {
- 58 width:30px;
- 59 background:rgba(255, 255, 255, 100%)
- 60 }
- 61
- 62 QScrollBar::handle:vertical {
- 63 width:30px;
- 64 background:rgba(200, 200, 200, 20%);
- 65 border-radius:15px;
- 66 }
- 67
- 68 QScrollBar::add-line:vertical {
- 69 width:0px; height:0px;
- 70 }
- 71 QScrollBar::sub-line:vertical {
- 72 width:0px;
- 73 height:0px;
- 74 }
- 75 QScrollBar::handle:vertical:hover {
- 76 width:30px;
- 77 background:rgba(200, 200, 200, 80%);
- 78 border-radius:15px;
- 79 }
- 80 QScrollBar::add-page:vertical,QScrollBar::sub-page:vertical {
- 81 background:rgba(255, 255, 255, 100%)
- 82 }
复制代码
其中数字选择器与闹钟开关按钮的代码,代码编者参考了一些博客优化后写成的代码。有兴趣可以细读代码,不作为本章注释讲解的代码。
数字选择器的作用是选择闹钟的时间,效果如下,通过上下滑动可以选择数字作为时钟的时针和分针数据。通过点击对话框确认后存储到数据库里。
数字选择器的头文件“numberpicker.h”代码如下。
数字选择器的源文件“numberpicker.cpp”代码如下。
- /******************************************************************
- Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
- * @projectName NumberPicker
- * @brief numberpicker.cpp
- * @author Deng Zhimao
- * @email <a href="mailto:1252699831@qq.com">1252699831@qq.com</a>
- * @net <a href="www.openedv.com" target="_blank">www.openedv.com</a>
- * @date 2021-05-14
- *******************************************************************/
- 1 #include <QMouseEvent>
- 2 #include <QDebug>
- 3 #include "numberpicker.h"
- 4 #include <QPainter>
- 5
- 6 NumberPicker::NumberPicker(QWidget *parent) :
- 7 /* 最小值默认为0 */
- 8 minRange(0),
- 9
- 10 /* 最大值默认60 */
- 11 maxRange(60),
- 12
- 13 /* 当前值默认0 */
- 14 currentValue(0),
- 15
- 16 /* 按下标志位为假 */
- 17 isDragging(false),
- 18
- 19 /* 默认偏移量为0 */
- 20 deviation(0),
- 21
- 22 /* 数值越大 */
- 23 numSize(15),
- 24
- 25 /* 间隔为1 */
- 26 interval(1),
- 27
- 28 /* 默认分成3格 */
- 29 devide(3),
- 30
- 31 /* 默认颜色黑色 */
- 32 numberColor(0, 0, 0)
- 33 {
- 34 setParent(parent);
- 35 setMinimumSize(50, 150);
- 36 homingAni = new QPropertyAnimation(this, "deviation");
- 37 homingAni->setDuration(300);
- 38 homingAni->setEasingCurve(QEasingCurve::OutQuad);
- 39 }
- 40
- 41 NumberPicker::~NumberPicker()
- 42 {
- 43
- 44 }
- 45
- 46 void NumberPicker::setRange(int min, int max)
- 47 {
- 48 minRange = min;
- 49 maxRange = max;
- 50 if (currentValue < min) {
- 51 currentValue = min;
- 52 }
- 53 if (currentValue > max) {
- 54 currentValue = max;
- 55 }
- 56 repaint();
- 57 }
- 58
- 59 int NumberPicker::readValue()
- 60 {
- 61 return currentValue;
- 62 }
- 63
- 64 void NumberPicker::mousePressEvent(QMouseEvent *e)
- 65 {
- 66 homingAni->stop();
- 67 isDragging = true;
- 68 mouseSrcPos = e->pos().y();
- 69 QWidget::mousePressEvent(e);
- 70 }
- 71
- 72 void NumberPicker::mouseMoveEvent(QMouseEvent *e)
- 73 {
- 74 if (isDragging){
- 75 deviation = e->pos().y() - mouseSrcPos;
- 76
- 77 /* 若移动速度过快,则进行限制 */
- 78 if (deviation > (height() - 1) / devide) {
- 79 deviation = (height() - 1) / devide;
- 80 } else if (deviation < -(height() - 1) / devide) {
- 81 deviation = -( height() - 1) / devide;
- 82 }
- 83
- 84 emit deviationChange(deviation / ((height() - 1) / devide));
- 85 repaint();
- 86 }
- 87 }
- 88
- 89 void NumberPicker::mouseReleaseEvent(QMouseEvent *)
- 90 {
- 91 if (isDragging) {
- 92 isDragging = false;
- 93 homing();
- 94 }
- 95 }
- 96
- 97 void NumberPicker::wheelEvent(QWheelEvent *e)
- 98 {
- 99 if (e->delta() > 0) {
- 100 deviation = (this->height() - 1) / devide;
- 101 } else {
- 102 deviation = -(this->height() - 1) / devide;
- 103 }
- 104
- 105 homing();
- 106 repaint();
- 107 }
- 108
- 109 void NumberPicker::paintEvent(QPaintEvent *)
- 110 {
- 111 QPainter painter(this);
- 112 painter.setRenderHint(QPainter::Antialiasing, true);
- 113 int Height = height() - 1;
- 114
- 115 if (deviation >= Height / devide && currentValue > minRange ) {
- 116 mouseSrcPos += Height / devide;
- 117 deviation -= Height / devide;
- 118 currentValue -= interval;
- 119 }
- 120
- 121 if (deviation <= -Height / devide && currentValue < maxRange ) {
- 122 mouseSrcPos -= Height / devide;
- 123 deviation += Height / devide;
- 124 currentValue += interval;
- 125 }
- 126
- 127 if (qAbs(int(currentValue)) >= int(maxRange))
- 128 currentValue = minRange;
- 129
- 130 paintNum(painter, qAbs(int(currentValue + maxRange) % maxRange),
- 131 deviation);
- 132
- 133 paintNum(painter,
- 134 qAbs((currentValue - interval + maxRange) % maxRange),
- 135 deviation - Height / devide);
- 136
- 137 paintNum(painter,
- 138 qAbs((currentValue + interval + maxRange) % maxRange),
- 139 deviation + Height / devide);
- 140
- 141 for (int i = 2; i <= devide / 2; ++i) {
- 142 if (qAbs(currentValue - interval * i) >= minRange) {
- 143 paintNum(painter,
- 144 qAbs((currentValue - interval * i + maxRange)
- 145 % maxRange),
- 146 deviation - Height / devide * i);
- 147 }
- 148
- 149 if (qAbs(currentValue + interval * i) <= maxRange) {
- 150 paintNum(painter,
- 151 qAbs((currentValue + interval * i + maxRange)
- 152 % maxRange),
- 153 deviation + Height / devide * i);
- 154 }
- 155 }
- 156 }
- 157
- 158 void NumberPicker::paintNum(QPainter &painter, int num, int deviation)
- 159 {
- 160 int Width = width() - 1;
- 161 int Height = height() - 1;
- 162
- 163 /* 偏移量越大,数字越小 */
- 164 //int size = (Height - qAbs(deviation)) / numSize;
- 165 int size = (Height - qAbs(deviation)) * numSize / 80;
- 166 int transparency = 255 - 255 * qAbs(deviation) / Height;
- 167 int height = Height / devide;
- 168 int y = Height / 2 + deviation - height / 2;
- 169
- 170 QFont font;
- 171 font.setPixelSize(size);
- 172 painter.setFont(font);
- 173 painter.setPen(QColor(numberColor.red(),
- 174 numberColor.green(),
- 175 numberColor.blue(),
- 176 transparency));
- 177
- 178 if ( y >= 0 && y + height < Height) {
- 179 //painter.drawRect(0, y, Width, height);
- 180 if (num < 10)
- 181 painter.drawText(QRectF(0, y, Width, height),
- 182 Qt::AlignCenter,
- 183 "0" + QString::number(num, 'f', 0));
- 184 else
- 185 painter.drawText(QRectF(0, y, Width, height),
- 186 Qt::AlignCenter,
- 187 QString::number(num, 'f', 0));
- 188 }
- 189 }
- 190
- 191 void NumberPicker::homing()
- 192 {
- 193 if (deviation > height() / 10) {
- 194 homingAni->setStartValue((height() - 1 ) / 8 - deviation);
- 195 homingAni->setEndValue(0);
- 196 currentValue -= interval;
- 197 } else if (deviation > -height() / 10) {
- 198 homingAni->setStartValue(deviation);
- 199 homingAni->setEndValue(0);
- 200 } else if (deviation < -height() / 10) {
- 201 homingAni->setStartValue(-(height() - 1) / 8 - deviation);
- 202 homingAni->setEndValue(0);
- 203 currentValue += interval;
- 204 }
- 205
- 206 emit currentValueChanged(currentValue);
- 207 homingAni->start();
- 208 }
- 209
- 210 int NumberPicker::readDeviation()
- 211 {
- 212 return deviation;
- 213 }
- 214
- 215 void NumberPicker::setDeviation(int n)
- 216 {
- 217 deviation = n;
- 218 repaint();
- 219 }
- 220
- 221 void NumberPicker::setNumSize(int size)
- 222 {
- 223 numSize = size;
- 224 repaint();
- 225 }
- 226
- 227 void NumberPicker::setInterval(int n)
- 228 {
- 229 interval = n;
- 230 repaint();
- 231 }
- 232
- 233 void NumberPicker::setDevide(int n)
- 234 {
- 235 devide = n;
- 236 repaint();
- 237 }
- 238
- 239 void NumberPicker::setNumberColor(QRgb rgb)
- 240 {
- 241 numberColor.setRgb(rgb);
- 242 repaint();
- 243 }
- 244
- 245 void NumberPicker::setValue(int value)
- 246 {
- 247 if (value < minRange || value > maxRange) {
- 248 qDebug()<<"数值设置必须在"<<minRange
- 249 <<"和"<<maxRange<<"之间"<<endl;
- 250 return;
- 251 }
- 252 currentValue = value;
- 253 repaint();
- 254 }
复制代码
开关按钮的效果如下。运行时有动画效果,类似IOS手册里的开关按钮一样。
开关按钮的头文件“switchbutton.h”代码如下。
- /******************************************************************
- Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
- * @projectName 17_sqlite_example
- * @brief switchbutton.h
- * @author Deng Zhimao
- * @email <a href="mailto:1252699831@qq.com">1252699831@qq.com</a>
- * @net <a href="www.openedv.com" target="_blank">www.openedv.com</a>
- * @date 2021-05-14
- *******************************************************************/
- 1 #ifndef SWITCHBUTTON_H
- 2 #define SWITCHBUTTON_H
- 3
- 4 #include <QWidget>
- 5 #include <QTimer>
- 6
- 7 class SwitchButton : public QWidget
- 8 {
- 9 Q_OBJECT
- 10
- 11 public:
- 12 explicit SwitchButton(QWidget *parent = nullptr);
- 13
- 14 /* 返回开关状态 - 打开:true 关闭:false */
- 15 bool isToggled() const;
- 16
- 17 /* 设置开关状态 */
- 18 void setToggle(bool checked);
- 19
- 20 /* 设置背景颜色 */
- 21 void setBackgroundColor(QColor color);
- 22
- 23 /* 设置选中颜色 */
- 24 void setCheckedColor(QColor color);
- 25
- 26 /* 设置不可用颜色 */
- 27 void setDisbaledColor(QColor color);
- 28
- 29 protected:
- 30 /* 绘制开关 */
- 31 void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
- 32
- 33 /* 鼠标按下事件 */
- 34 void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
- 35
- 36 /* 鼠标释放事件 - 切换开关状态、发射toggled()信号 */
- 37 void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
- 38
- 39 /* 大小改变事件 */
- 40 void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE;
- 41
- 42 /* 缺省大小 */
- 43 QSize sizeHint() const Q_DECL_OVERRIDE;
- 44 QSize minimumSizeHint() const Q_DECL_OVERRIDE;
- 45
- 46 signals:
- 47 /* 状态改变时,发射信号 */
- 48 void toggled(bool checked);
- 49
- 50 private slots:
- 51 /* 状态切换时,用于产生滑动效果 */
- 52 void onTimeout();
- 53
- 54 private:
- 55 /* 是否选中 */
- 56 bool m_bChecked;
- 57
- 58 /* 背景颜色 */
- 59 QColor m_background;
- 60
- 61 /* 选中颜色 */
- 62 QColor m_checkedColor;
- 63
- 64 /* 不可用颜色 */
- 65 QColor m_disabledColor;
- 66
- 67 /* 拇指颜色 */
- 68 QColor m_thumbColor;
- 69
- 70 /* 圆角 */
- 71 qreal m_radius;
- 72
- 73 /* x点坐标 */
- 74 qreal m_nX;
- 75
- 76 /* y点坐标 */
- 77 qreal m_nY;
- 78
- 79 /* 高度 */
- 80 qint16 m_nHeight;
- 81
- 82 /* 外边距 */
- 83 qint16 m_nMargin;
- 84
- 85 /* 定时器 */
- 86 QTimer m_timer;
- 87 };
- 88 #endif // SWITCHBUTTON_H
复制代码
开关按钮的源文件“switchbutton.cpp”代码如下。
- /******************************************************************
- Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
- * @projectName 17_sqlite_example
- * @brief switchbutton.cpp
- * @author Deng Zhimao
- * @email <a href="mailto:1252699831@qq.com">1252699831@qq.com</a>
- * @net <a href="www.openedv.com" target="_blank">www.openedv.com</a>
- * @date 2021-05-14
- *******************************************************************/
- 1 #include "switchbutton.h"
- 2
- 3 #include <QPainter>
- 4 #include <QMouseEvent>
- 5
- 6 SwitchButton::SwitchButton(QWidget *parent)
- 7 : QWidget(parent),
- 8 m_bChecked(false),
- 9 m_background(Qt::gray),
- 10 m_checkedColor(34, 131, 246),
- 11 m_disabledColor(190, 190, 190),
- 12 m_thumbColor(Qt::gray),
- 13 m_radius(12.5),
- 14 m_nHeight(16),
- 15 m_nMargin(3)
- 16 {
- 17 /* 鼠标滑过光标形状 - 手型 */
- 18 setCursor(Qt::pointingHandCursor);
- 19
- 20 /* 连接信号槽 */
- 21 connect(&m_timer, SIGNAL(timeout()),
- 22 this, SLOT(onTimeout()));
- 23 }
- 24
- 25 /* 绘制开关 */
- 26 void SwitchButton::paintEvent(QPaintEvent *event)
- 27 {
- 28 Q_UNUSED(event)
- 29
- 30 QPainter painter(this);
- 31 painter.setPen(Qt::NoPen);
- 32 painter.setRenderHint(QPainter::Antialiasing);
- 33
- 34 QPainterPath path;
- 35 QColor background;
- 36 QColor thumbColor;
- 37 qreal dOpacity;
- 38 /* 可用状态 */
- 39 if (isEnabled()) {
- 40 /* 打开状态 */
- 41 if (m_bChecked) {
- 42 background = m_checkedColor;
- 43 thumbColor = m_checkedColor;
- 44 dOpacity = 0.600;
- 45 /* 关闭状态 */
- 46 } else {
- 47 background = m_background;
- 48 thumbColor = m_thumbColor;
- 49 dOpacity = 0.800;
- 50 }
- 51 /* 不可用状态 */
- 52 } else {
- 53 background = m_background;
- 54 dOpacity = 0.260;
- 55 thumbColor = m_disabledColor;
- 56 }
- 57 /* 绘制大椭圆 */
- 58 painter.setBrush(background);
- 59 painter.setOpacity(dOpacity);
- 60 path.addRoundedRect(QRectF(m_nMargin,
- 61 m_nMargin, width() - 2 * m_nMargin,
- 62 height() - 2 * m_nMargin),
- 63 m_radius, m_radius);
- 64 painter.drawPath(path.simplified());
- 65
- 66 /* 绘制小椭圆 */
- 67 painter.setBrush(thumbColor);
- 68 painter.setOpacity(1.0);
- 69 painter.drawEllipse(QRectF(m_nX - (m_nHeight / 2),
- 70 m_nY - (m_nHeight / 2),
- 71 height(),
- 72 height()));
- 73 }
- 74
- 75 /* 鼠标按下事件 */
- 76 void SwitchButton::mousePressEvent(QMouseEvent *event)
- 77 {
- 78 if (isEnabled()) {
- 79 if (event->buttons() & Qt::LeftButton) {
- 80 event->accept();
- 81 } else {
- 82 event->ignore();
- 83 }
- 84 }
- 85 }
- 86
- 87 /* 鼠标释放事件 - 切换开关状态、发射toggled()信号 */
- 88 void SwitchButton::mouseReleaseEvent(QMouseEvent *event)
- 89 {
- 90 if (isEnabled()) {
- 91 if ((event->type() == QMouseEvent::MouseButtonRelease)
- 92 && (event->button() == Qt::LeftButton)) {
- 93 event->accept();
- 94 m_bChecked = !m_bChecked;
- 95 emit toggled(m_bChecked);
- 96 m_timer.start(10);
- 97 } else {
- 98 event->ignore();
- 99 }
- 100 }
- 101 }
- 102
- 103 /* 大小改变事件 */
- 104 void SwitchButton::resizeEvent(QResizeEvent *event)
- 105 {
- 106 m_nX = m_nHeight / 2;
- 107 m_nY = m_nHeight / 2;
- 108 QWidget::resizeEvent(event);
- 109 }
- 110
- 111 /* 默认大小 */
- 112 QSize SwitchButton::sizeHint() const
- 113 {
- 114 return minimumSizeHint();
- 115 }
- 116
- 117 /* 最小大小 */
- 118 QSize SwitchButton::minimumSizeHint() const
- 119 {
- 120 return QSize(2 * (m_nHeight + m_nMargin),
- 121 m_nHeight + 2 * m_nMargin);
- 122 }
- 123
- 124 /* 切换状态 - 滑动 */
- 125 void SwitchButton::OnTimeout()
- 126 {
- 127 if (m_bChecked) {
- 128 m_nX += 1;
- 129 if (m_nX >= width() - m_nHeight - m_nHeight / 2 ) {
- 130 m_timer.stop();
- 131 m_nX -= 1;
- 132 }
- 133 } else {
- 134 m_nX -= 1;
- 135 if (m_nX <= m_nHeight / 2) {
- 136 m_timer.stop();
- 137 m_nX += 1;
- 138 }
- 139 }
- 140 update();
- 141 }
- 142
- 143 /* 返回开关状态 - 打开:true 关闭:false */
- 144 bool SwitchButton::isToggled() const
- 145 {
- 146 return m_bChecked;
- 147 }
- 148
- 149 /* 设置开关状态 */
- 150 void SwitchButton::setToggle(bool checked)
- 151 {
- 152 m_bChecked = checked;
- 153 m_timer.start(10);
- 154 }
- 155
- 156 /* 设置背景颜色 */
- 157 void SwitchButton::setBackgroundColor(QColor color)
- 158 {
- 159 m_background = color;
- 160 }
- 161
- 162 /* 设置选中颜色 */
- 163 void SwitchButton::setCheckedColor(QColor color)
- 164 {
- 165 m_checkedColor = color;
- 166 }
- 167
- 168 /* 设置不可用颜色 */
- 169 void SwitchButton::setDisbaledColor(QColor color)
- 170 {
- 171 m_disabledColor = color;
- 172 }
复制代码
13.2.1.1 程序运行效果
点击程序正下方的“+”按钮则开始添加闹钟数据,根据当前系统的时间或者滑动选择闹钟的时间,点击确认即新增一条闹钟数据。
修改或者删除数据,点击要修改闹钟的数据项目,弹出的对话框,可以重新设置闹钟或者删除闹钟。并保存记录到数据库里,下次运行打开时会从数据库里读取保存的闹钟数据。
13.2.2 数据库表格(QTableView显示)
本小节设计一个生活中的例子,使用数据库修改/查询员工的编号、姓名、年龄、性别与照片信息。
本例将数据库的内容显示到QTableView上。如果只是简单的显示数据库的内容到QTableView上,可以使用下面的方法,此方法QTableView上可以看到员工的编号、姓名、年龄、性别信息,同时可以双击表格进行项修改,修改完成将自动保存到数据库里。
- 1 /* 初始化表格模型 */
- 2 QSqlTableModel *model = new QSqlTableModel(this, sqlDatabase);
- 3
- 4 /* 设置要选中的表格名称 */
- 5 model->setTable("employee");
- 6 /* 如果有修改则同步修改到数据库,
- 7 * 注意这个规则需要与tabview这样的控件才生效,
- 8 * 因为tabview可以直接编辑表里的内容 */
- 9 model->setEditStrategy(QSqlTableModel::OnFieldChange);
- 10 /* 成功则返回true,查看数据库里是否有employee这个表格 */
- 11 model->select();
- 12 /* 设置表格的头信息,若不设置则显示数据库里的英文字段头信息 */
- 13 model->setHeaderData(model->fieldIndex("id"),
- 14 Qt::Horizontal, tr("编号"));
- 15 model->setHeaderData(model->fieldIndex("name"),
- 16 Qt::Horizontal, tr("姓名"));
- 17 model->setHeaderData(model->fieldIndex("age"),
- 18 Qt::Horizontal, tr("年龄"));
- 19 model->setHeaderData(model->fieldIndex("sex"),
- 20 Qt::Horizontal, tr("性别"));
- 21
- 22 QTableView *view = new QTableView;
- 23
- 24 /* 设置表格的模型为model */
- 25 view->setModel(model);
- 26 /* 不显示图片路径信息行 */
- 27 view->hideColumn(4);
- 28 /* 表格居中 */
- 29 setCentralWidget(view);
- 30 return;
复制代码
上面的程序可以修改数据库的内容也可以查看。但是看不到员工的照片信息。本例就讲解如何将数据库数据显示到QTableView上,及查看选择的员工项的全部信息显示。介绍Qt如何使用数据库存储照片的信息。我们知道数据库类型有个BLOB数据类型可以用于存储照片信息。但是本例并不那样做,当数据库数据很多时,将照片(二进制数据)存储到数据库里就不是一个明智的选择了。大字段数据会加重数据库的负担,拖慢数据库,数据库文件越小访问肯定越快,数据库也不用遍历那么多内容,或者加载那么大的数据到内存里,造成响应不及时等。计算机可能处理速度愉快,但是对于普通的单核和多核ARM开发板来说速度可能会跟不上啊!所以照片最好是存储照片的路径。照片路径属于字符串文本,不会占用太多空间。
好了现在上例子,例子就是查询员工的编号、姓名、年龄、性别与照片信息,简单实用好理解,不复杂。
本例目的:用QTableView显示数据库表的数据,显示员工的信息。
例18_sqlite_table,查询数据表(难度:一般)。项目路径为Qt/2/ 18_sqlite_table。
项目文件18_sqlite_table文件第一行添加的代码部分如下。
头文件主要声明布局用的类和数据库,重要关注是QSqlDatabase、QSqlQueryModel 、QdataWidgetMapper和QItemSelectionModel。这里声明的是全局变量。
在源文件“mainwindow.cpp”具体代码如下。
- <font size="4">mainwindow.cpp编程后的代码
- /******************************************************************
- Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
- * @projectName 18_sqlite_table
- * @brief mainwindow.cpp
- * @author Deng Zhimao
- * @email <a href="mailto:1252699831@qq.com">1252699831@qq.com</a>
- * @net <a href="www.openedv.com" target="_blank">www.openedv.com</a>
- * @date 2021-05-18
- *******************************************************************/
- 1 #include "mainwindow.h"
- 2 #include <QDebug>
- 3 #include <QHeaderView>
- 4 #include <QSqlError>
- 5 #include <QApplication>
- 6 #include <QSqlRecord>
- 7
- 8 MainWindow::MainWindow(QWidget *parent)
- 9 : QMainWindow(parent)
- 10 {
- 11 /* 设置主窗体的显示位置与大小 */
- 12 this->setGeometry(0, 0, 800, 480);
- 13
- 14 /* 查看本机可用的数据库驱动 */
- 15 QStringList drivers = QSqlDatabase::drivers();
- 16 foreach(QString driver, drivers) {
- 17 qDebug()<<driver;
- 18 }
- 19
- 20 /* 以QSQLITE驱动方式打开或者创建数据库 */
- 21 sqlDatabase = QSqlDatabase::addDatabase("QSQLITE");
- 22 sqlDatabase.setDatabaseName("employee.db");
- 23 /* 以open的方式打开employee.db数据库,则会创建一个employee.db */
- 24 if (!sqlDatabase.open())
- 25 qDebug()<<"连接数据库错误"<<sqlDatabase.lastError()<<endl;
- 26 else
- 27 qDebug()<<"连接数据库成功"<<endl;
- 28
- 29 QSqlQuery query(sqlDatabase);
- 30 /* 使用指令式创建表 */
- 31 query.exec("create table employee (id int primary key, name vchar(10), "
- 32 "age int, sex vchar(3), photo text)");
- 33
- 34 QStringList photoPath;
- 35 /* 当前可执行程序的路径 */
- 36 QString path(QApplication::applicationDirPath());
- 37 photoPath<< path + "/photos/啊万.jpg"<< path + "/photos/啊棠.jpg";
- 38
- 39 /* 以指令的方式插入数据,如果数据已经存在则不会成功不能插入 */
- 40 query.exec(tr("insert into employee values(1, '啊万', 27, '男', '%1')").arg(photoPath[0]));
- 41 query.exec(tr("insert into employee values(2, '啊棠', 28, '男', '%1')").arg(photoPath[1]));
- 42
- 43 // /* 初始化表格模型 */
- 44 // QSqlTableModel *model = new QSqlTableModel(this, sqlDatabase);
- 45
- 46 // /* 设置要选中的表格名称 */
- 47 // model->setTable("employee");
- 48 // /* 如果有修改则同步修改到数据库,
- 49 // * 注意这个规则需要与tabview这样的控件才生效,
- 50 // * 因为tabview可以直接编辑表里的内容 */
- 51 // model->setEditStrategy(QSqlTableModel::OnFieldChange);
- 52 // /* 成功则返回true,查看数据库里是否有employee这个表格 */
- 53 // model->select();
- 54 // /* 设置表格的头信息,若不设置则显示数据库里的英文字段头信息 */
- 55 // model->setHeaderData(model->fieldIndex("id"),
- 56 // Qt::Horizontal, tr("编号"));
- 57 // model->setHeaderData(model->fieldIndex("name"),
- 58 // Qt::Horizontal, tr("姓名"));
- 59 // model->setHeaderData(model->fieldIndex("age"),
- 60 // Qt::Horizontal, tr("年龄"));
- 61 // model->setHeaderData(model->fieldIndex("sex"),
- 62 // Qt::Horizontal, tr("性别"));
- 63
- 64 // QTableView *view = new QTableView;
- 65
- 66 // /* 设置表格的模型为model */
- 67 // view->setModel(model);
- 68 // /* 不显示图片路径信息行 */
- 69 // view->hideColumn(4);
- 70 // /* 表格居中 */
- 71 // setCentralWidget(view);
- 72 // return;
- 73
- 74 /* QSqlQueryModel适合用于查询数据,不能修改数据 */
- 75 sqlQueryModel = new QSqlQueryModel(this);
- 76
- 77 /* 选择编号,姓名,年龄和性别的内容,显示到tableView上,
- 78 * 图片最后通过数据选择再读取Label上 */
- 79 sqlQueryModel->setQuery("select id, name, age, sex from employee");
- 80
- 81 if (sqlQueryModel->lastError().isValid())
- 82 qDebug()<<"选择数据失败!"<<endl;
- 83
- 84 sqlQueryModel->setHeaderData(0, Qt::Horizontal, "编号");
- 85 sqlQueryModel->setHeaderData(1, Qt::Horizontal, "姓名");
- 86 sqlQueryModel->setHeaderData(2, Qt::Horizontal, "年龄");
- 87 sqlQueryModel->setHeaderData(3, Qt::Horizontal, "性别");
- 88
- 89 tableView = new QTableView();
- 90 tableView->setModel(sqlQueryModel);
- 91
- 92 /* 设置显示平均分列 */
- 93 tableView->horizontalHeader()
- 94 ->setSectionResizeMode(QHeaderView::Stretch);
- 95
- 96 mainWidget = new QWidget();
- 97 bottomWidget = new QWidget();
- 98 gridWidget = new QWidget();
- 99 photoWidget = new QWidget();
- 100 imageLabel = new QLabel();
- 101
- 102 /* 设置照片属性 */
- 103 imageLabel->setScaledContents(true);
- 104 imageLabel->setMaximumSize(200, 200);
- 105
- 106
- 107 vBoxLayout = new QVBoxLayout();
- 108 hBoxLayout[0] = new QHBoxLayout();
- 109 hBoxLayout[1] = new QHBoxLayout();
- 110 gridLayout = new QGridLayout();
- 111
- 112 for (int i = 0; i < 4; i++)
- 113 label</font><i><font size="4"> = new QLabel();
- 114
- 115 for (int i = 0; i < 2; i++) {
- 116 spinBox</font><i><font size="4"> = new QSpinBox();
- 117 spinBox</font><i><font size="4">->setRange(1, 100);
- 118 }
- 119
- 120 comboBox = new QComboBox();
- 121 comboBox->addItem("男");
- 122 comboBox->addItem("女");
- 123
- 124 lineEdit = new QLineEdit();
- 125
- 126 bottomWidget->setMinimumHeight(this->height() / 2 - 30);
- 127 gridWidget->setMaximumWidth(this->width() / 2 - 30);
- 128
- 129 /* 垂直布局 */
- 130 vBoxLayout->addWidget(tableView);
- 131 vBoxLayout->addWidget(bottomWidget);
- 132
- 133 mainWidget->setLayout(vBoxLayout);
- 134 setCentralWidget(mainWidget);
- 135
- 136 /* 水平布局 */
- 137 hBoxLayout[0]->addWidget(gridWidget);
- 138 hBoxLayout[0]->addWidget(photoWidget);
- 139 bottomWidget->setLayout(hBoxLayout[0]);
- 140
- 141 QStringList list;
- 142 list<<"姓名:"<<"编号:"<<"年龄:"<<"性别:";
- 143
- 144 /* 网格布局 */
- 145 for (int i = 0; i < 4; i++) {
- 146 gridLayout->addWidget(label</font><i><font size="4">, i, 0);
- 147 label</font><i><font size="4">->setText(list</font><i><font size="4">);
- 148 switch (i) {
- 149 case 0:
- 150 gridLayout->addWidget(lineEdit, i, 1);
- 151 break;
- 152 case 1:
- 153 gridLayout->addWidget(spinBox[0], i, 1);
- 154 break;
- 155 case 2:
- 156 gridLayout->addWidget(spinBox[1], i, 1);
- 157 break;
- 158 case 3:
- 159 gridLayout->addWidget(comboBox, i, 1);
- 160 break;
- 161 default:
- 162 break;
- 163 }
- 164 }
- 165
- 166 gridWidget->setLayout(gridLayout);
- 167 hBoxLayout[1]->addWidget(imageLabel);
- 168 photoWidget->setLayout(hBoxLayout[1]);
- 169
- 170 itemSelectionModel = new QItemSelectionModel(sqlQueryModel);
- 171 tableView->setSelectionModel(itemSelectionModel);
- 172
- 173 /* 信号槽连接,表示表中行数据变化时,触发槽函数 */
- 174 connect(itemSelectionModel,
- 175 SIGNAL(currentRowChanged(QModelIndex, QModelIndex)),
- 176 this,
- 177 SLOT(on_currentRowChanged(QModelIndex, QModelIndex)));
- 178
- 179 dataWidgetMapper = new QDataWidgetMapper(this);
- 180 /* 设置为自动提交 */
- 181 dataWidgetMapper->setSubmitPolicy(QDataWidgetMapper::AutoSubmit);
- 182 dataWidgetMapper->setModel(sqlQueryModel);
- 183 /* 创建数据映射,将前面的数据库内容映射到控件上 */
- 184 dataWidgetMapper->addMapping(lineEdit, 1);
- 185 dataWidgetMapper->addMapping(spinBox[0], 0);
- 186 dataWidgetMapper->addMapping(spinBox[1], 2);
- 187 dataWidgetMapper->addMapping(comboBox, 3);
- 188 }
- 189
- 190 MainWindow::~MainWindow()
- 191 {
- 192 /* 关闭数据库 */
- 193 sqlDatabase.close();
- 194 }
- 195
- 196 void MainWindow::On_currentRowChanged(const QModelIndex ¤t,
- 197 const QModelIndex &previous)
- 198 {
- 199 Q_UNUSED(previous)
- 200 /* 更新数据映射行号,初始化时映射到第0行 */
- 201 dataWidgetMapper->setCurrentModelIndex(current);
- 202 /* 获取当前行号 */
- 203 int row = itemSelectionModel->currentIndex().row();
- 204 /* 获取当前模型记录 */
- 205 QSqlRecord record = sqlQueryModel->record(row);
- 206 /* 获取id信息 */
- 207 int id = record.value("id").toInt();
- 208 QSqlQuery query;
- 209 /* 使用bindValue绑定prepare里语句的值,需要使用":",":"是占位符 */
- 210 query.prepare("select photo from employee where id = :ID");
- 211 query.bindValue(":ID", id);
- 212 query.exec();
- 213 /* 返回到选择的第一条记录,因为id是唯一的,也只有一条记录 */
- 214 query.first();
- 215
- 216 /* 获取字段为photo的值,也就是存储照片的路径 */
- 217 QVariant temp = query.value("photo");
- 218 if (!temp.isValid()) {
- 219 qDebug()<<"数据无效!"<<endl;
- 220 return;
- 221 }
- 222
- 223 /* 清空图片显示 */
- 224 imageLabel->clear();
- 225
- 226 QImage image(temp.toString());
- 227
- 228 if (image.isNull()) {
- 229 qDebug()<<"未找到"<<temp.toString()<<endl;
- 230 return;
- 231 }
- 232
- 233 /* 显示照片 */
- 234 imageLabel->setPixmap(QPixmap::fromImage(image));
- 235 }</font></i></i></i></i></i></i>
复制代码
第15~41行,连接数据库,创建数据库表,插入数据,与上一小节实用闹钟基本一样,不再赘述。
第43~72行,被注释行,这部分程序就是本小节开头所说的,如果只想显示数据库表里的数据就打开这个被注释掉的内容即可。
第75~168行,布局及一些设置的内容,不再解释。布局前面入门篇已经讲过。
第170~187行,重点关注这几行代码,170~177行,QItemSelectionModel将sqlQueryModel作为项的选择模型,然后,tableView设置项的选择模型为itemSelectionModel,这个目的就是使用itemSelection的行发生的变化的信号currentRowChanged()。
第179~187行,这里主要是是将sqlQueryModel的数据通过dataWidgetMapper这个对象映射到我们的普通控件类上。比如映射第4个数据(index = 3)性别数据到comboBox上。可以看出dataWidgetMapper只是一个搬运工而已,将指定的数据搬到我们需要显示的控件上。
第196~235行,当我们点击表中的数据,行发生变化后,则这个槽函数触发,流程是从当前选择的行里,使用QSqlRecord记录当前行的数据,然后从当前行的数据提取出id的编号,再使用QSqlQuery的exec()方法执行sql的select语句获取出photo字段照片的路径数据,最后将获取的照片路径数据初始化一个QImage对象显示到imageLable上,这样就实现了数据显示的功能。并且映射到控件上的内容也发生了改变。
当我们的数据库表足够大时,我们若想使用一些按钮来点击切换查询当前项的数据可以使用按钮连接到dataWidgetMapper的toFirst()、toLast()、toNext()和toPrevious()槽函数。意思是跳转到第一行,最后一行,下一行和前一行。最后将tableView设置为dataWidgetMapper当前行就会触发currentRowChanged(),就能实现按钮控制查询数据了。至于读者想加什么功能由读者自由设计,本例只是写了个大概框架。
main .cpp内容如下,没有修改。
- 1 #include "mainwindow.h"
- 2
- 3 #include <QApplication>
- 4
- 5 int main(int argc, char *argv[])
- 6 {
- 7 QApplication a(argc, argv);
- 8 MainWindow w;
- 9 w.show();
- 10 return a.exec();
- 11 }
复制代码
13.2.2.1 程序运行效果
运行本例时,先点击构建,构建完成后将本项目下的整个photos文件夹拷贝到构建出来的build-18_sqlite_table-Desktop_Qt_5_12_9_GCC_64bit-Debug目录下。因为本程序会从数据库里读取出员工的照片路径信息,所以需要提前将照片文件夹放至可执行程序同一级目录下。点击表中的项,当切换员工的信息里,我们可以看到左下角被映射的内容发生了改变,变成了当前选择行的员工信息,右下角的照片头像也变成了该员工的照片信息。本例实现的就是从数据库里取数据并显示到QTableView及搭配其他控件使用的例子。
员工啊万的信息:
员工啊棠信息:
|
|