1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html 3)对正点原子Linux感兴趣的同学可以加群讨论:935446741 4)关注正点原子公众号,获取最新资料更新
第十二章 数据库数据库是什么?简易言之,就是保存数据的文件。可以存储大量数据,包括插入数据、更新数据、截取数据等。用专业术语来说,数据库是“按照数据结构来组织、存储和管理数据的仓库”。是一个长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集合。 什么时候需要数据库?在嵌入式里,存储大量数据,或者记录数据,就需要用到数据库。举个简单的例子,比如手机的闹钟就使用到了数据库,我们设置的闹钟数据将会保存到数据库里,闹钟程序运行时会从数据库里读取出上次保存的闹钟数据。如果没有数据库,则闹钟程序关机了数据不保存在物理储存设备里,下次运行闹钟时就没有上次设置的闹钟数据,这显然是不合理的。所以我们需要用到数据库。 本章认为读者已经基本了解数据库,已经对数据库有一定的认识,如果没有对数据库了解,请自行学习,毕竟本书是讲Qt的,不是讲数据库,数据库知识很多,而我们只是讲解Qt怎么去用数据库,对数据库的简单操作!目的就是在Qt里使用数据库! 想要在项目中使用Qt SQL模块,需要在项目配置文件里添加如下语句。 QT += core gui 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”具体代码如下。 mainwindow.h编程后的代码
/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName 17_sqlite_example
* @brief mainwindow.h
* @author Deng Zhimao
* @email 1252699831@qq.com
* @net www.openedv.com
* @date 2021-05-15
*******************************************************************/
1 #ifndef MAINWINDOW_H
2 #define MAINWINDOW_H
3
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11 #include
12 #include
13 #include
14 #include
15 #include "numberpicker.h"
16 #include "switchbutton.h"
17
18 class NumberPicker;
19 class SwitchButton;
20
21 /* ListWiget项结构体 */
22 struct ItemObjectInfo {
23 /* 闹钟开关 */
24 SwitchButton *switchButton;
25 /* Widget容器 */
26 QWidget *widget;
27 /* 水平布局 */
28 QHBoxLayout *hBoxLayout;
29 };
30
31
32 class MainWindow : public QMainWindow
33 {
34 Q_OBJECT
35
36 public:
37 MainWindow(QWidget *parent = nullptr);
38 ~MainWindow();
39
40 private:
41
42 /* 数据库连接类 */
43 QSqlDatabase sqlDatabase;
44
45 /* 数据库操作模型 */
46 QSqlTableModel *model;
47
48 /* 时针选择器 */
49 NumberPicker *hourPicker;
50
51 /* 分钟选择器 */
52 NumberPicker *minutePicker;
53
54 /* 弹出选择时间对话框 */
55 QDialog *alarmDialog;
56
57 /* 水平布局 */
58 QHBoxLayout *hBoxLayout[3];
59
60 /* 垂直布局 */
61 QVBoxLayout *vBoxLayout[2];
62
63 /* 显示闹钟列表 */
64 QListWidget *listWidget;
65
66 /* 主Widget */
67 QWidget *mainWidget;
68
69 /* 底部Wiget */
70 QWidget *bottomWidget;
71
72 /* 弹出对话框布局窗口选择时间容器 */
73 QWidget *timeWidget;
74
75 /* 弹出对话框布局窗口按钮容器 */
76 QWidget *btWidget;
77
78 /* 添加闹钟按钮 */
79 QPushButton *addAlarm;
80
81 /* 确认按钮 */
82 QPushButton *yesButton;
83
84 /* 取消按钮 */
85 QPushButton *cancelButton;
86
87 /* listWiget项信息存储 */
88 QVector itemObjectInfo;
89
90 private slots:
91 /* 添加闹钟按钮被点击 */
92 void addAlarmClicked();
93
94 /* 列表被点击 */
95 void listWidgetItemClicked(QListWidgetItem *);
96
97 /* 确认按钮被点击 */
98 void yesButtonClicked();
99
100 /* 取消按钮被点击 */
101 void cancelButtonClicked();
102
103 /* 开关按钮点击 */
104 void switchButtonClicked(bool);
105 };
106 #endif // 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 1252699831@qq.com
* @net www.openedv.com
* @date 2021-05-15
*******************************************************************/
1 #include "mainwindow.h"
2 #include
3 #include
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()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 rowCount(); i++) {
161 for (int j = 0; j index(i, j);
163 switch (j) {
164 case 0:
165 qDebug()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() readValue()) + ":";
283 else
284 hour = QString::number(hourPicker->readValue()) + ":";
285
286 if (minutePicker->readValue() 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 /* 执行上面语句 */
354 model->select();
355 itemObjectInfo.remove(listWidget->currentRow());
356 listWidget->takeItem(listWidget->currentRow());
357 }
358
359 /* 再确保提交 */
360 if (model->isDirty())
361 model->submitAll();
362
363 /* 关闭对话框 */
364 alarmDialog->close();
365 }
366
367
368 /* 当点击闹钟开关时,将闹钟开关状态同步更新到数据库里 */
369 void MainWindow::switchButtonClicked(bool checked)
370 {
371 listWidget->clearSelection();
372
373 SwitchButton *button = (SwitchButton *)sender();
374 for (int i = 0; i setData(model->index(i, 2), "true");
378 listWidget->item(i)
379 ->setTextColor(QColor(22, 22, 22, 225));
380 } else {
381 model->setData(model->index(i, 2), "false");
382 listWidget->item(i)
383 ->setTextColor(QColor(22, 22, 22, 60));
384 }
385
386 model->submit();
387 break;
388 }
389 }
390 }
第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
4 #include
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”代码如下。
/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName NumberPicker
* @brief numberpicker.h
* @author Deng Zhimao
* @email 1252699831@qq.com
* @net www.openedv.com
* @date 2021-05-14
*******************************************************************/
1 #ifndef NUMBERPICKER_H
2 #define NUMBERPICKER_H
3
4 #include
5 #include
6
7 class NumberPicker : public QWidget
8 {
9 Q_OBJECT
10
11 Q_PROPERTY(int deviation READ readDeviation WRITE setDeviation )
12 public:
13 NumberPicker(QWidget *parent = nullptr);
14 ~NumberPicker();
15
16 /* 设置最大值与最小值的范围 */
17 void setRange(int min, int max);
18
19 /* 读取当前值 */
20 int readValue();
21
22 protected:
23 void mousePressEvent(QMouseEvent *);
24
25 void mouseMoveEvent(QMouseEvent *);
26
27 void mouseReleaseEvent(QMouseEvent *);
28
29 void wheelEvent(QWheelEvent *);
30
31 void paintEvent(QPaintEvent *);
32
33 public:
34 /* 描绘数字 */
35 void paintNum(QPainter &painter, int num, int deviation);
36
37 /* 使选中的数字回到屏幕中间 */
38 void homing();
39
40 /* 鼠标移动偏移量,默认为0 */
41 int readDeviation();
42
43 /* 设置偏移量 */
44 void setDeviation(int n);
45
46 /* 设置字体大小 */
47 void setNumSize(int);
48
49 /* 设置间隔大小 */
50 void setInterval(int);
51
52 /* 设置分格数量,一般设置为3、5、7... */
53 void setDevide(int);
54
55 /* 设置数字颜色,设置rgb的数值 */
56 void setNumberColor(QRgb rgb);
57
58 /* 设置当前值 */
59 void setValue(int value);
60
61 signals:
62
63 void currentValueChanged(int value);
64
65 void deviationChange(int deviation);
66
67 private:
68 /* 最小值 */
69 int minRange;
70
71 /* 最大值 */
72 int maxRange;
73
74 /* 当前选中的值 */
75 int currentValue;
76
77 /* 鼠标是否按下 */
78 bool isDragging;
79
80 /* 偏移量,记录鼠标按下后移动的垂直距离 */
81 int deviation;
82
83 /* 鼠标按下的垂直位置 */
84 int mouseSrcPos;
85
86 /* 数字大小 */
87 int numSize;
88
89 /* 动画 */
90 QPropertyAnimation *homingAni;
91
92 /* 间隔大小 */
93 int interval;
94
95 /* 分格数量 */
96 int devide;
97
98 /* 数字颜色 */
99 QColor numberColor;
100 };
101 #endif // NUMBERPICKER_H
数字选择器的源文件“numberpicker.cpp”代码如下。
/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName NumberPicker
* @brief numberpicker.cpp
* @author Deng Zhimao
* @email 1252699831@qq.com
* @net www.openedv.com
* @date 2021-05-14
*******************************************************************/
1 #include
2 #include
3 #include "numberpicker.h"
4 #include
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 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 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 if (currentValue -height() / 10) {
201 homingAni->setStartValue(deviation);
202 homingAni->setEndValue(0);
203 } else if (deviation setStartValue(-(height() - 1) / 8 - deviation);
205 homingAni->setEndValue(0);
206 currentValue += interval;
207 }
208
209 emit currentValueChanged(currentValue);
210 homingAni->start();
211 }
212
213 int NumberPicker::readDeviation()
214 {
215 return deviation;
216 }
217
218 void NumberPicker::setDeviation(int n)
219 {
220 deviation = n;
221 repaint();
222 }
223
224 void NumberPicker::setNumSize(int size)
225 {
226 numSize = size;
227 repaint();
228 }
229
230 void NumberPicker::setInterval(int n)
231 {
232 interval = n;
233 repaint();
234 }
235
236 void NumberPicker::setDevide(int n)
237 {
238 devide = n;
239 repaint();
240 }
241
242 void NumberPicker::setNumberColor(QRgb rgb)
243 {
244 numberColor.setRgb(rgb);
245 repaint();
246 }
247
248 void NumberPicker::setValue(int value)
249 {
250 if (value maxRange) {
251 qDebug()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文件第一行添加的代码部分如下。 18_sqlite_table.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
22 HEADERS += \
23 mainwindow.h
24
25 # Default rules for deployment.
26 qnx: target.path = /tmp/$${TARGET}/bin
27 else: unix:!android: target.path = /opt/$${TARGET}/bin
28 !isEmpty(target.path): INSTALLS += target
29
30 RESOURCES +=
在头文件“mainwindow.h”具体代码如下。 mainwindow.h编程后的代码
/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName 18_sqlite_table
* @brief mainwindow.h
* @author Deng Zhimao
* @email 1252699831@qq.com
* @net www.openedv.com
* @date 2021-05-18
*******************************************************************/
1 #ifndef MAINWINDOW_H
2 #define MAINWINDOW_H
3
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11 #include
12 #include
13 #include
14 #include
15 #include
16 #include
17 #include
18 #include
19
20 class MainWindow : public QMainWindow
21 {
22 Q_OBJECT
23
24 public:
25 MainWindow(QWidget *parent = nullptr);
26 ~MainWindow();
27
28 private:
29
30 /* 数据库连接类 */
31 QSqlDatabase sqlDatabase;
32
33 /* 用于查询数据 */
34 QSqlQueryModel *sqlQueryModel;
35
36 /* 数据映射 */
37 QDataWidgetMapper *dataWidgetMapper;
38
39 /* 选择模型 */
40 QItemSelectionModel * itemSelectionModel;
41
42 /* 水平布局 */
43 QHBoxLayout *hBoxLayout[2];
44
45 /* 垂直布局 */
46 QVBoxLayout *vBoxLayout;
47
48 /* 网格布局 */
49 QGridLayout *gridLayout;
50
51 /* 用于显示的表格*/
52 QTableView *tableView;
53
54 /* 主Widget */
55 QWidget *mainWidget;
56
57 /* 底部容器 */
58 QWidget *bottomWidget;
59
60 /* 底部网格布局容器 */
61 QWidget *gridWidget;
62
63 /* 照片容器 */
64 QWidget *photoWidget;
65
66 /* Label,用于显示照片 */
67 QLabel *imageLabel;
68
69 /* Label,底部显示文本 */
70 QLabel *label[4];
71
72 /* 性别下拉选择框,选择信息 */
73 QComboBox *comboBox;
74
75 /* 数值选择框,[0, 100] */
76 QSpinBox *spinBox[2];
77
78 /* 单行输入框 */
79 QLineEdit *lineEdit;
80
81 private slots:
82 /* 表格当前行变化执行的槽函数 */
83 void on_currentRowChanged(const QModelIndex&, const QModelIndex&);
84 };
85 #endif // MAINWINDOW_H
头文件主要声明布局用的类和数据库,重要关注是QSqlDatabase、QSqlQueryModel 、QdataWidgetMapper和QItemSelectionModel。这里声明的是全局变量。
在源文件“mainwindow.cpp”具体代码如下。 mainwindow.cpp编程后的代码
/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName 18_sqlite_table
* @brief mainwindow.cpp
* @author Deng Zhimao
* @email 1252699831@qq.com
* @net www.openedv.com
* @date 2021-05-18
*******************************************************************/
1 #include "mainwindow.h"
2 #include
3 #include
4 #include
5 #include
6 #include
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()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 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 listsetLayout(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()
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?