搜索
bottom↓
回复: 0

《I.MX6U嵌入式Qt开发指南》第十三章 数据库

[复制链接]

出0入234汤圆

发表于 2021-7-19 12:40:54 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2021-8-11 12:28 编辑

1)实验平台:正点原子i.MX6ULL Linux阿尔法开发板
2)  章节摘自【正点原子】《I.MX6U嵌入式Qt开发指南》
3)购买链接:https://item.taobao.com/item.htm?&id=603672744434
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/arm-linux/zdyz-i.mx6ull.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)
正点原子Linux技术交流群:1027879335    1.png


2.jpg


3.png


第十三章 数据库

数据库是什么?简易言之,就是保存数据的文件。可以存储大量数据,包括插入数据、更新数据、截取数据等。用专业术语来说,数据库是“按照数据结构来组织、存储和管理数据的仓库”。是一个长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集合。
什么时候需要数据库?在嵌入式里,存储大量数据,或者记录数据,就需要用到数据库。举个简单的例子,比如手机的闹钟就使用到了数据库,我们设置的闹钟数据将会保存到数据库里,闹钟程序运行时会从数据库里读取出上次保存的闹钟数据。如果没有数据库,则闹钟程序关机了数据不保存在物理储存设备里,下次运行闹钟时就没有上次设置的闹钟数据,这显然是不合理的。所以我们需要用到数据库。
本章认为读者已经基本了解数据库,已经对数据库有一定的认识,如果没有对数据库了解,请自行学习,毕竟本书是讲Qt的,不是讲数据库,数据库知识很多,而我们只是讲解Qt怎么去用数据库,对数据库的简单操作!目的就是在Qt里使用数据库!
想要在项目中使用Qt SQL模块,需要在项目配置文件里添加如下语句。
  1. 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提供了很多操作数据库的类,我们只讲解基本的与常用的就已经足够了。下面用个图示来再对上面的操作深入了解一下。
1.png

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. 1   QT       += core gui sql
  2. 2
  3. 3   greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
  4. 4
  5. 5   CONFIG += c++11
  6. 6
  7. 7   # The following define makes your compiler emit warnings if you use
  8. 8   # any Qt feature that has been marked deprecated (the exact warnings
  9. 9   # depend on your compiler). Please consult the documentation of the
  10. 10  # deprecated API in order to know how to port your code away from it.
  11. 11  DEFINES += QT_DEPRECATED_WARNINGS
  12. 12
  13. 13  # You can also make your code fail to compile if it uses deprecated APIs.
  14. 14  # In order to do so, uncomment the following line.
  15. 15  # You can also select to disable deprecated APIs only up to a certain version of Qt.
  16. 16  #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
  17. 17
  18. 18  SOURCES += \
  19. 19      main.cpp \
  20. 20      mainwindow.cpp \
  21. 21      numberpicker.cpp \
  22. 22      switchbutton.cpp
  23. 23
  24. 24  HEADERS += \
  25. 25      mainwindow.h \
  26. 26      numberpicker.h \
  27. 27      switchbutton.h
  28. 28
  29. 29  # Default rules for deployment.
  30. 30  qnx: target.path = /tmp/${TARGET}/bin
  31. 31  else: unix:!android: target.path = /opt/${TARGET}/bin
  32. 32  !isEmpty(target.path): INSTALLS += target
  33. 33
  34. 34  RESOURCES += \
  35. 35      res.qrc
复制代码

在头文件“mainwindow.h”具体代码如下。
mainwindow.h编程后的代码
  1.    /******************************************************************
  2.     Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
  3.     * @projectName   17_sqlite_example
  4.     * @brief         mainwindow.h
  5.     * @author        Deng Zhimao
  6.     * @email         <a href="mailto:1252699831@qq.com" target="_blank">1252699831@qq.com</a>
  7.     * @net            <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  8.     * @date           2021-05-15
  9.     *******************************************************************/
  10. 1   #ifndef MAINWINDOW_H
  11. 2   #define MAINWINDOW_H
  12. 3  
  13. 4   #include <QSqlDatabase>
  14. 5   #include <QSqlQuery>
  15. 6   #include <QMainWindow>
  16. 7   #include <QDialog>
  17. 8   #include <QHBoxLayout>
  18. 9   #include <QVBoxLayout>
  19. 10  #include <QPushButton>
  20. 11  #include <QListWidget>
  21. 12  #include <QLabel>
  22. 13  #include <QTime>
  23. 14  #include <QSqlTableModel>
  24. 15  #include "numberpicker.h"
  25. 16  #include "switchbutton.h"
  26. 17
  27. 18  class NumberPicker;
  28. 19  class SwitchButton;
  29. 20
  30. 21  /* ListWiget项结构体 */
  31. 22  struct ItemObjectInfo {
  32. 23      /* 闹钟开关 */
  33. 24      SwitchButton *switchButton;
  34. 25      /* Widget容器 */
  35. 26      QWidget *widget;
  36. 27      /* 水平布局 */
  37. 28      QHBoxLayout *hBoxLayout;
  38. 29  };
  39. 30
  40. 31
  41. 32  class MainWindow : public QMainWindow
  42. 33  {
  43. 34      Q_OBJECT
  44. 35
  45. 36  public:
  46. 37      MainWindow(QWidget *parent = nullptr);
  47. 38      ~MainWindow();
  48. 39
  49. 40  private:
  50. 41
  51. 42      /* 数据库连接类 */
  52. 43      QSqlDatabase sqlDatabase;
  53. 44
  54. 45      /* 数据库操作模型 */
  55. 46      QSqlTableModel *model;
  56. 47
  57. 48      /* 时针选择器 */
  58. 49      NumberPicker *hourPicker;
  59. 50
  60. 51      /* 分钟选择器 */
  61. 52      NumberPicker *minutePicker;
  62. 53
  63. 54      /* 弹出选择时间对话框 */
  64. 55      QDialog *alarmDialog;
  65. 56
  66. 57      /* 水平布局 */
  67. 58      QHBoxLayout *hBoxLayout[3];
  68. 59
  69. 60      /* 垂直布局 */
  70. 61      QVBoxLayout *vBoxLayout[2];
  71. 62
  72. 63      /* 显示闹钟列表 */
  73. 64      QListWidget *listWidget;
  74. 65
  75. 66      /* 主Widget */
  76. 67      QWidget *mainWidget;
  77. 68
  78. 69      /* 底部Wiget */
  79. 70      QWidget *bottomWidget;
  80. 71
  81. 72      /* 弹出对话框布局窗口选择时间容器 */
  82. 73      QWidget *timeWidget;
  83. 74
  84. 75      /* 弹出对话框布局窗口按钮容器 */
  85. 76      QWidget *btWidget;
  86. 77
  87. 78      /* 添加闹钟按钮 */
  88. 79      QPushButton *addAlarm;
  89. 80
  90. 81      /* 确认按钮 */
  91. 82      QPushButton *yesButton;
  92. 83
  93. 84      /* 取消按钮 */
  94. 85      QPushButton *cancelButton;
  95. 86
  96. 87      /* listWiget项信息存储 */
  97. 88      QVector<ItemObjectInfo> itemObjectInfo;
  98. 89
  99. 90  private slots:
  100. 91      /* 添加闹钟按钮被点击 */
  101. 92      void addAlarmClicked();
  102. 93
  103. 94      /* 列表被点击 */
  104. 95      void listWidgetItemClicked(QListWidgetItem *);
  105. 96
  106. 97      /* 确认按钮被点击 */
  107. 98      void yesButtonClicked();
  108. 99
  109. 100     /* 取消按钮被点击 */
  110. 101     void cancelButtonClicked();
  111. 102
  112. 103     /* 开关按钮点击 */
  113. 104     void switchButtonClicked(bool);
  114. 105 };
  115. 106 #endif // MAINWINDOW_H
复制代码

        头文件主要声明布局用的类和数据库,重要关注是QSqlDatabase和QSqlTableModel。这里声明的是全局变量。
在源文件“mainwindow.cpp”具体代码如下。
mainwindow.cpp编程后的代码
  1.    /******************************************************************
  2.     Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
  3.     * @projectName   17_sqlite_example
  4.     * @brief         mainwindow.cpp
  5.     * @author        Deng Zhimao
  6.     * @email         <a href="mailto:1252699831@qq.com" target="_blank">1252699831@qq.com</a>
  7.     * @net            <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  8.     * @date           2021-05-15
  9.     *******************************************************************/
  10. 1   #include "mainwindow.h"
  11. 2   #include <QDebug>
  12. 3   #include <QSqlError>
  13. 4  
  14. 5   MainWindow::MainWindow(QWidget *parent)
  15. 6       : QMainWindow(parent)
  16. 7   {
  17. 8       /* 设置主窗体的显示位置与大小 */
  18. 9       this->setGeometry(0, 0, 800, 480);
  19. 10
  20. 11      /* 查看本机可用的数据库驱动 */
  21. 12      QStringList drivers = QSqlDatabase::drivers();
  22. 13      foreach(QString driver, drivers) {
  23. 14          qDebug()<<driver;
  24. 15      }
  25. 16
  26. 17      /* 以QSQLITE驱动方式打开或者创建数据库 */
  27. 18      sqlDatabase = QSqlDatabase::addDatabase("QSQLITE");
  28. 19      sqlDatabase.setDatabaseName("alarm.db");
  29. 20      /* 以open的方式打开alarm.db数据库,则会创建一个alarm.db */
  30. 21      if (!sqlDatabase.open())
  31. 22          qDebug()<<"连接数据库错误"<<sqlDatabase.lastError()<<endl;
  32. 23      else
  33. 24          qDebug()<<"连接数据库成功"<<endl;
  34. 25
  35. 26      QSqlQuery query(sqlDatabase);
  36. 27      /* 使用指令式创建表 */
  37. 28      query.exec("create table alarm (id int primary key, time vchar(15), flag vchar(5))");
  38. 29      /* 以指令的方式插入数据 */
  39. 30      //query.exec("insert into alarm values(0, '06:00', 'false')");
  40. 31
  41. 32      model = new QSqlTableModel(this, sqlDatabase);
  42. 33
  43. 34      /* 模型设置表的名字,需要与数据库的表的名字相同 */
  44. 35      model->setTable("alarm");
  45. 36
  46. 37      /* 如果有修改则同步修改到数据库,
  47. 38       * 注意这个规则需要与tabview这样的控件才生效,
  48. 39       * 因为tabview可以直接编辑表里的内容 */
  49. 40      model->setEditStrategy(QSqlTableModel::OnFieldChange);
  50. 41
  51. 42      /* 成功则返回true,查看数据库里是否有alarm这个表格 */
  52. 43      model->select();
  53. 44
  54. 45      /* 如果数据表数据为空,则添加两个闹钟 */
  55. 46      if (model->rowCount() == 0) {
  56. 47          /* 插入一行 */
  57. 48          model->insertRow(model->rowCount());
  58. 49          /* 在该行插入数据 */
  59. 50          model->setData(model->index(0, 0), 1);
  60. 51          model->setData(model->index(0, 1), "06:00");
  61. 52          model->setData(model->index(0, 2), "false");
  62. 53          /* 插入数据后记得提交 */
  63. 54          model->submit();
  64. 55
  65. 56          /* 再插入一行 */
  66. 57          model->insertRow(model->rowCount());
  67. 58          model->setData(model->index(1, 0), 2);
  68. 59          model->setData(model->index(1, 1), "18:00");
  69. 60          model->setData(model->index(1, 2), "true");
  70. 61          /* 提交 */
  71. 62          model->submit();
  72. 63      }
  73. 64
  74. 65      hourPicker = new NumberPicker(this);
  75. 66      hourPicker->setRange(0, 24);
  76. 67
  77. 68      minutePicker = new NumberPicker(this);
  78. 69      minutePicker->setRange(0, 60);
  79. 70
  80. 71      /* 标签,用于显示时&分 */
  81. 72      QLabel *label[3];
  82. 73      label[0] = new QLabel();
  83. 74      label[1] = new QLabel();
  84. 75      label[2] = new QLabel();
  85. 76
  86. 77      QFont font;
  87. 78      font.setBold(true);
  88. 79      font.setPixelSize(10);
  89. 80      QPalette pal;
  90. 81      pal.setBrush(QPalette::WindowText, QColor(0, 0, 0));
  91. 82
  92. 83      label[0]->setFont(font);
  93. 84      label[1]->setFont(font);
  94. 85      label[2]->setFont(font);
  95. 86
  96. 87      label[0]->setText(" ");
  97. 88      label[1]->setText("时");
  98. 89      label[2]->setText("分");
  99. 90
  100. 91      /* 主布局初始化 */
  101. 92      listWidget = new QListWidget();
  102. 93      mainWidget = new QWidget();
  103. 94      bottomWidget = new QWidget();
  104. 95      alarmDialog = new QDialog(this);
  105. 96      timeWidget = new QWidget();
  106. 97      btWidget = new QWidget();
  107. 98      addAlarm = new QPushButton();
  108. 99      yesButton = new QPushButton();
  109. 100     cancelButton = new QPushButton();
  110. 101     vBoxLayout[0] = new QVBoxLayout();
  111. 102     vBoxLayout[1] = new QVBoxLayout();
  112. 103     hBoxLayout[0] = new QHBoxLayout();
  113. 104     hBoxLayout[1] = new QHBoxLayout();
  114. 105     hBoxLayout[2] = new QHBoxLayout();
  115. 106
  116. 107     addAlarm->setMaximumSize(84, 84);
  117. 108     addAlarm->setObjectName("addAlarm");
  118. 109     addAlarm->setMinimumSize(84, 84);
  119. 110     bottomWidget->setMinimumHeight(84);
  120. 111     bottomWidget->setMaximumHeight(84);
  121. 112     yesButton->setText("确认");
  122. 113     cancelButton->setText("取消");
  123. 114     yesButton->setMaximumSize(100, 50);
  124. 115     yesButton->setMinimumSize(100, 50);
  125. 116     cancelButton->setMinimumSize(100, 50);
  126. 117     cancelButton->setMaximumSize(100, 50);
  127. 118     btWidget->setMaximumHeight(70);
  128. 119     btWidget->setMinimumHeight(70);
  129. 120     alarmDialog->setMinimumSize(300, 300);
  130. 121     alarmDialog->setMaximumSize(300, 300);
  131. 122     alarmDialog->setModal(true);
  132. 123     yesButton->setObjectName("yesButton");
  133. 124     cancelButton->setObjectName("cancelButton");
  134. 125
  135. 126     /* 主布局 */
  136. 127     vBoxLayout[0]->addWidget(listWidget);
  137. 128     vBoxLayout[0]->addWidget(bottomWidget);
  138. 129     vBoxLayout[0]->setContentsMargins(0, 0, 0, 0);
  139. 130
  140. 131     mainWidget->setLayout(vBoxLayout[0]);
  141. 132
  142. 133     setCentralWidget(mainWidget);
  143. 134
  144. 135     /* 底部按钮布局 */
  145. 136     hBoxLayout[0]->addWidget(addAlarm);
  146. 137     hBoxLayout[0]->setContentsMargins(0, 0, 0, 0);
  147. 138     bottomWidget->setLayout(hBoxLayout[0]);
  148. 139
  149. 140     /* 对话框布局 */
  150. 141     vBoxLayout[1]->addWidget(timeWidget);
  151. 142     vBoxLayout[1]->addWidget(btWidget);
  152. 143     vBoxLayout[1]->setContentsMargins(0, 0, 0, 0);
  153. 144     alarmDialog->setLayout(vBoxLayout[1]);
  154. 145
  155. 146     hBoxLayout[1]->addWidget(label[0]);
  156. 147     hBoxLayout[1]->addWidget(hourPicker);
  157. 148     hBoxLayout[1]->addWidget(label[1]);
  158. 149     hBoxLayout[1]->addWidget(minutePicker);
  159. 150     hBoxLayout[1]->addWidget(label[2]);
  160. 151     hBoxLayout[1]->setContentsMargins(0, 0, 0, 0);
  161. 152     timeWidget->setLayout(hBoxLayout[1]);
  162. 153
  163. 154     hBoxLayout[2]->addWidget(yesButton);
  164. 155     hBoxLayout[2]->addWidget(cancelButton);
  165. 156
  166. 157     btWidget->setLayout(hBoxLayout[2]);
  167. 158
  168. 159     /* 打印出闹钟数据库里的信息 */
  169. 160     for (int i = 0; i < model->rowCount(); i++) {
  170. 161         for (int j = 0; j < 3; j++) {
  171. 162             QModelIndex qindex = model->index(i, j);
  172. 163             switch (j) {
  173. 164             case 0:
  174. 165                 qDebug()<<"第"<<model->data(qindex).toInt()<<"行数据";
  175. 166                 break;
  176. 167             case 1:
  177. 168                 listWidget->addItem(model->data(qindex).toString());
  178. 169                 qDebug()<<"闹钟时间为:"<<model->data(qindex).toString();
  179. 170                 break;
  180. 171             case 2:
  181. 172                 qDebug()<<"闹钟状态为:"
  182. 173                        <<model->data(qindex).toString()<<endl;
  183. 174                 if (model->data(qindex).toString() != "true")
  184. 175                     listWidget->item(i)
  185. 176                             ->setTextColor(QColor(22, 22, 22, 60));
  186. 177                 else
  187. 178                     listWidget->item(i)
  188. 179                             ->setTextColor(QColor(22, 22, 22, 225));
  189. 180                 break;
  190. 181             default:
  191. 182                 break;
  192. 183             }
  193. 184         }
  194. 185     }
  195. 186
  196. 187     /* 在列表里添加闹钟开关 */
  197. 188     for (int i = 0; i < model->rowCount(); i++) {
  198. 189         ItemObjectInfo info;
  199. 190         info.widget = new QWidget();
  200. 191         info.switchButton = new SwitchButton();
  201. 192         info.hBoxLayout = new QHBoxLayout();
  202. 193         info.switchButton->setMaximumSize(55, 30);
  203. 194         info.switchButton->setMinimumSize(55, 30);
  204. 195         info.hBoxLayout->setContentsMargins(0, 0, 0, 0);
  205. 196         info.hBoxLayout->setAlignment(Qt::AlignRight);
  206. 197         info.hBoxLayout->addWidget(info.switchButton);
  207. 198         info.widget->setLayout(info.hBoxLayout);
  208. 199         listWidget->setItemWidget(listWidget->item(i),
  209. 200                                   info.widget);
  210. 201         itemObjectInfo.append(info);
  211. 202
  212. 203         /* 连接信号槽 */
  213. 204         connect(info.switchButton,
  214. 205                 SIGNAL(toggled(bool)),
  215. 206                 this,
  216. 207                 SLOT(switchButtonClicked(bool)));
  217. 208
  218. 209         /* 获取数据库里的闹钟开关状态 */
  219. 210         QModelIndex qindex = model->index(i, 2);
  220. 211         if (model->data(qindex).toBool())
  221. 212             /* 设置列表里的闹钟开关按钮状态 */
  222. 213             info.switchButton->setToggle(true);
  223. 214     }
  224. 215
  225. 216     /* 按钮 */
  226. 217     connect(addAlarm, SIGNAL(clicked()), this,
  227. 218             SLOT(addAlarmClicked()));
  228. 219
  229. 220     connect(yesButton, SIGNAL(clicked()), this,
  230. 221             SLOT(yesButtonClicked()));
  231. 222
  232. 223     connect(cancelButton, SIGNAL(clicked()), this,
  233. 224             SLOT(cancelButtonClicked()));
  234. 225
  235. 226     /* 列表 */
  236. 227     connect(listWidget,
  237. 228             SIGNAL(itemClicked(QListWidgetItem*)),
  238. 229             this,
  239. 230             SLOT(listWidgetItemClicked(QListWidgetItem*)));
  240. 231 }
  241. 232
  242. 233 MainWindow::~MainWindow()
  243. 234 {
  244. 235     /* 关闭数据库 */
  245. 236     sqlDatabase.close();
  246. 237 }
  247. 238
  248. 239 void MainWindow::addAlarmClicked()
  249. 240 {
  250. 241     /* 选择时间对话框里显示当前系统时间 */
  251. 242     hourPicker->setValue(QTime::currentTime().hour());
  252. 243     minutePicker->setValue(QTime::currentTime().minute());
  253. 244
  254. 245     /* 取消按钮显示文本为"取消" */
  255. 246     cancelButton->setText("取消");
  256. 247
  257. 248     /* 如果是点击添加闹钟的按钮,则设置闹钟列表的索引index为-1 */
  258. 249     listWidget->setCurrentRow(-1);
  259. 250
  260. 251     /* 显示对话框 */
  261. 252     alarmDialog->show();
  262. 253 }
  263. 254
  264. 255 void MainWindow::listWidgetItemClicked(QListWidgetItem *item)
  265. 256 {
  266. 257     /* 从被点击项里获取闹钟数据 */
  267. 258     QStringList list =
  268. 259             listWidget->item(listWidget->row(item))->text().split(":");
  269. 260
  270. 261     /* 选择时间对话框里显示被选择项的时间 */
  271. 262     hourPicker->setValue(list.at(0).toInt());
  272. 263     minutePicker->setValue(list.at(1).toInt());
  273. 264
  274. 265     /* 取消按钮显示文本为"删除" */
  275. 266     cancelButton->setText("删除");
  276. 267
  277. 268     /* 显示闹钟选择对话框 */
  278. 269     alarmDialog->show();
  279. 270
  280. 271     /* 作用使其失去选择 */
  281. 272     listWidget->clearSelection();
  282. 273 }
  283. 274
  284. 275 void MainWindow::yesButtonClicked()
  285. 276 {
  286. 277     /* 获取数值选择值的数据,转为字符串 */
  287. 278     QString hour;
  288. 279     QString minute;
  289. 280
  290. 281     if (hourPicker->readValue() < 10)
  291. 282         hour = "0" + QString::number(hourPicker->readValue()) + ":";
  292. 283     else
  293. 284         hour = QString::number(hourPicker->readValue()) + ":";
  294. 285
  295. 286     if (minutePicker->readValue() < 10)
  296. 287         minute = "0" + QString::number(minutePicker->readValue());
  297. 288     else
  298. 289         minute = QString::number(minutePicker->readValue());
  299. 290
  300. 291     /* 如果不是选中闹钟列表的数据 */
  301. 292     if (listWidget->currentRow() == -1) {
  302. 293         /* 插入一行数据,闹钟时间为选择的闹钟时间 */
  303. 294         int row = model->rowCount();
  304. 295
  305. 296         /* 插入数据到数据库 */
  306. 297         model->insertRow(row);
  307. 298         model->setData(model->index(row, 0), row + 1);
  308. 299         model->setData(model->index(row, 1), hour + minute);
  309. 300         model->setData(model->index(row, 2), "true");
  310. 301         model->submit();
  311. 302
  312. 303         /* 添加闹钟到列表 */
  313. 304         listWidget->addItem(hour + minute);
  314. 305
  315. 306         /* 添加到容器 */
  316. 307         ItemObjectInfo info;
  317. 308         info.widget = new QWidget();
  318. 309         info.switchButton = new SwitchButton();
  319. 310         info.hBoxLayout = new QHBoxLayout();
  320. 311         info.switchButton->setMaximumSize(55, 30);
  321. 312         info.switchButton->setMinimumSize(55, 30);
  322. 313         info.hBoxLayout->setContentsMargins(0, 0, 0, 0);
  323. 314         info.hBoxLayout->setAlignment(Qt::AlignRight);
  324. 315         info.hBoxLayout->addWidget(info.switchButton);
  325. 316         info.widget->setLayout(info.hBoxLayout);
  326. 317         info.switchButton->setToggle(true);
  327. 318
  328. 319         /* 连接信号槽 */
  329. 320         connect(info.switchButton, SIGNAL(toggled(bool)), this,
  330. 321                 SLOT(switchButtonClicked(bool)));
  331. 322
  332. 323         listWidget->setItemWidget(
  333. 324                     listWidget->item(listWidget->count() - 1),
  334. 325                     info.widget);
  335. 326         itemObjectInfo.append(info);
  336. 327     } else {
  337. 328         /* 修改数据(更新闹钟数据) */
  338. 329         int row =  listWidget->currentRow();
  339. 330         model->setData(model->index(row, 0), row + 1);
  340. 331         model->setData(model->index(row, 1), hour + minute);
  341. 332         model->setData(model->index(row, 2), "true");
  342. 333         model->submit();
  343. 334
  344. 335         /* 设置当前项的闹钟文本 */
  345. 336         listWidget->currentItem()->setText(hour + minute);
  346. 337     }
  347. 338
  348. 339     /* 再确保提交 */
  349. 340     if (model->isDirty())
  350. 341         model->submitAll();
  351. 342
  352. 343     /* 关闭对话框 */
  353. 344     alarmDialog->close();
  354. 345 }
  355. 346
  356. 347 void MainWindow::cancelButtonClicked()
  357. 348 {
  358. 349     if (cancelButton->text() == "删除") {
  359. 350         /* 删除数据库整一行数据 */
  360. 351         model->removeRow(listWidget->currentRow());
  361. 352         model->submit();
  362. 353         itemObjectInfo.remove(listWidget->currentRow());
  363. 354         listWidget->takeItem(listWidget->currentRow());
  364. 355     }
  365. 356
  366. 357     /* 再确保提交 */
  367. 358     if (model->isDirty())
  368. 359         model->submitAll();
  369. 360
  370. 361     /* 关闭对话框 */
  371. 362     alarmDialog->close();
  372. 363 }
  373. 364
  374. 365
  375. 366 /* 当点击闹钟开关时,将闹钟开关状态同步更新到数据库里 */
  376. 367 void MainWindow::switchButtonClicked(bool checked)
  377. 368 {
  378. 369     listWidget->clearSelection();
  379. 370
  380. 371     SwitchButton *button = (SwitchButton *)sender();
  381. 372     for (int i = 0; i < itemObjectInfo.count(); i++) {
  382. 373         if (button == itemObjectInfo.at(i).switchButton) {
  383. 374             if (checked) {
  384. 375                 model->setData(model->index(i, 2), "true");
  385. 376                 listWidget->item(i)
  386. 377                         ->setTextColor(QColor(22, 22, 22, 225));
  387. 378             } else {
  388. 379                 model->setData(model->index(i, 2), "false");
  389. 380                 listWidget->item(i)
  390. 381                         ->setTextColor(QColor(22, 22, 22, 60));
  391. 382             }
  392. 383
  393. 384             model->submit();
  394. 385             break;
  395. 386         }
  396. 387     }
  397. 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. 1   #include "mainwindow.h"
  2. 2
  3. 3   #include <QApplication>
  4. 4   #include <QFile>
  5. 5
  6. 6   int main(int argc, char *argv[])
  7. 7   {
  8. 8       QApplication a(argc, argv);
  9. 9       /* 指定文件 */
  10. 10      QFile file(":/style.qss");
  11. 11
  12. 12      /* 判断文件是否存在 */
  13. 13      if (file.exists() ) {
  14. 14          /* 以只读的方式打开 */
  15. 15          file.open(QFile::ReadOnly);
  16. 16          /* 以字符串的方式保存读出的结果 */
  17. 17          QString styleSheet = QLatin1String(file.readAll());
  18. 18          /* 设置全局样式 */
  19. 19          qApp->setStyleSheet(styleSheet);
  20. 20          /* 关闭文件 */
  21. 21          file.close();
  22. 22      }
  23. 23
  24. 24      MainWindow w;
  25. 25      w.show();
  26. 26      return a.exec();
  27. 27  }
复制代码

        style.qss样式文件如下。素材已经在源码处提供。注意下面的style.qss不能有注释!
  1. 1   QListWidget {
  2. 2   font-size: 30px;
  3. 3   outline:none;
  4. 4   }
  5. 5
  6. 6   QListWidget::item:active {
  7. 7   background: transparent;
  8. 8   }
  9. 9
  10. 10  QListWidget::item {
  11. 11  height:80;
  12. 12  }
  13. 13
  14. 14  QListWidget::item:selected:hover {
  15. 15  background:#22222222;
  16. 16  }
  17. 17
  18. 18  QListWidget::item:selected {
  19. 19  background:transparent;
  20. 20  color:#ee222222;
  21. 21  }
  22. 22
  23. 23  QPushButton#addAlarm {
  24. 24  border-image:url(:/icons/addalarm1.png);
  25. 25  background:transparent;
  26. 26  outline: none;
  27. 27  }
  28. 28
  29. 29  QPushButton#addAlarm:hover {
  30. 30  border-image:url(:/icons/addalarm2.png);
  31. 31  }
  32. 32
  33. 33  QPushButton#yesButton {
  34. 34  border: 1px solid #22222222;
  35. 35  border-radius: 25px;
  36. 36  background:#22222222;
  37. 37  outline:none;
  38. 38  }
  39. 39
  40. 40  QPushButton#yesButton:pressed {
  41. 41  background:#44222222;
  42. 42  color:white;
  43. 43  }
  44. 44
  45. 45  QPushButton#cancelButton {
  46. 46  border: 1px solid #22222222;
  47. 47  border-radius: 25px;
  48. 48  background:#22222222;
  49. 49  outline:none;
  50. 50  }
  51. 51
  52. 52  QPushButton#cancelButton:pressed {
  53. 53  background:#44222222;
  54. 54  color:white;
  55. 55  }
  56. 56
  57. 57  QScrollBar:vertical {
  58. 58  width:30px;
  59. 59  background:rgba(255, 255, 255, 100%)
  60. 60  }
  61. 61
  62. 62  QScrollBar::handle:vertical {
  63. 63  width:30px;
  64. 64  background:rgba(200, 200, 200, 20%);
  65. 65  border-radius:15px;
  66. 66  }
  67. 67
  68. 68  QScrollBar::add-line:vertical {
  69. 69  width:0px; height:0px;
  70. 70  }
  71. 71  QScrollBar::sub-line:vertical {
  72. 72  width:0px;
  73. 73  height:0px;
  74. 74  }
  75. 75  QScrollBar::handle:vertical:hover {
  76. 76  width:30px;
  77. 77  background:rgba(200, 200, 200, 80%);
  78. 78  border-radius:15px;
  79. 79  }
  80. 80  QScrollBar::add-page:vertical,QScrollBar::sub-page:vertical {
  81. 81  background:rgba(255, 255, 255, 100%)
  82. 82  }
复制代码


        其中数字选择器与闹钟开关按钮的代码,代码编者参考了一些博客优化后写成的代码。有兴趣可以细读代码,不作为本章注释讲解的代码。
        数字选择器的作用是选择闹钟的时间,效果如下,通过上下滑动可以选择数字作为时钟的时针和分针数据。通过点击对话框确认后存储到数据库里。
数据库23087.png

        数字选择器的头文件“numberpicker.h”代码如下。
  
  1.   /******************************************************************
  2.     Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
  3.     * @projectName   NumberPicker
  4.     * @brief         numberpicker.h
  5.     * @author        Deng Zhimao
  6.     * @email         <a href="mailto:1252699831@qq.com" target="_blank">1252699831@qq.com</a>
  7.     * @net            <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  8.     * @date           2021-05-14
  9.     *******************************************************************/
  10. 1   #ifndef NUMBERPICKER_H
  11. 2   #define NUMBERPICKER_H
  12. 3  
  13. 4   #include <QMainWindow>
  14. 5   #include <QPropertyAnimation>
  15. 6  
  16. 7   class NumberPicker : public QWidget
  17. 8   {
  18. 9       Q_OBJECT
  19. 10
  20. 11      Q_PROPERTY(int deviation READ readDeviation WRITE setDeviation )
  21. 12  public:
  22. 13      NumberPicker(QWidget *parent = nullptr);
  23. 14      ~NumberPicker();
  24. 15
  25. 16      /* 设置最大值与最小值的范围 */
  26. 17      void setRange(int min, int max);
  27. 18
  28. 19      /* 读取当前值 */
  29. 20      int readValue();
  30. 21
  31. 22  protected:
  32. 23      void mousePressEvent(QMouseEvent *);
  33. 24
  34. 25      void mouseMoveEvent(QMouseEvent *);
  35. 26
  36. 27      void mouseReleaseEvent(QMouseEvent *);
  37. 28
  38. 29      void wheelEvent(QWheelEvent *);
  39. 30
  40. 31      void paintEvent(QPaintEvent *);
  41. 32
  42. 33  public:
  43. 34      /* 描绘数字 */
  44. 35      void paintNum(QPainter &painter, int num, int deviation);
  45. 36
  46. 37      /* 使选中的数字回到屏幕中间 */
  47. 38      void homing();
  48. 39
  49. 40      /* 鼠标移动偏移量,默认为0 */
  50. 41      int readDeviation();
  51. 42
  52. 43      /* 设置偏移量 */
  53. 44      void setDeviation(int n);
  54. 45
  55. 46      /* 设置字体大小 */
  56. 47      void setNumSize(int);
  57. 48
  58. 49      /* 设置间隔大小 */
  59. 50      void setInterval(int);
  60. 51
  61. 52      /* 设置分格数量,一般设置为3、5、7... */
  62. 53      void setDevide(int);
  63. 54
  64. 55      /* 设置数字颜色,设置rgb的数值 */
  65. 56      void setNumberColor(QRgb rgb);
  66. 57
  67. 58      /* 设置当前值 */
  68. 59      void setValue(int value);
  69. 60
  70. 61  signals:
  71. 62
  72. 63      void currentValueChanged(int value);
  73. 64
  74. 65      void deviationChange(int deviation);
  75. 66
  76. 67  private:
  77. 68      /* 最小值 */
  78. 69      int minRange;
  79. 70
  80. 71      /* 最大值 */
  81. 72      int maxRange;
  82. 73
  83. 74      /* 当前选中的值 */
  84. 75      int currentValue;
  85. 76
  86. 77      /* 鼠标是否按下 */
  87. 78      bool isDragging;
  88. 79
  89. 80      /* 偏移量,记录鼠标按下后移动的垂直距离 */
  90. 81      int deviation;
  91. 82
  92. 83      /* 鼠标按下的垂直位置 */
  93. 84      int mouseSrcPos;
  94. 85
  95. 86      /* 数字大小 */
  96. 87      int numSize;
  97. 88
  98. 89      /* 动画 */
  99. 90      QPropertyAnimation *homingAni;
  100. 91
  101. 92      /* 间隔大小 */
  102. 93      int interval;
  103. 94
  104. 95      /* 分格数量 */
  105. 96      int devide;
  106. 97
  107. 98      /* 数字颜色 */
  108. 99      QColor numberColor;
  109. 100 };
  110. 101 #endif // NUMBERPICKER_H
复制代码


        数字选择器的源文件“numberpicker.cpp”代码如下。
  
  1.   /******************************************************************
  2.     Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
  3.     * @projectName   NumberPicker
  4.     * @brief         numberpicker.cpp
  5.     * @author        Deng Zhimao
  6.     * @email         <a href="mailto:1252699831@qq.com" target="_blank">1252699831@qq.com</a>
  7.     * @net            <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  8.     * @date           2021-05-14
  9.     *******************************************************************/
  10. 1   #include <QMouseEvent>
  11. 2   #include <QDebug>
  12. 3   #include "numberpicker.h"
  13. 4   #include <QPainter>
  14. 5  
  15. 6   NumberPicker::NumberPicker(QWidget *parent) :
  16. 7       /* 最小值默认为0 */
  17. 8       minRange(0),
  18. 9  
  19. 10      /* 最大值默认60 */
  20. 11      maxRange(60),
  21. 12
  22. 13      /* 当前值默认0 */
  23. 14      currentValue(0),
  24. 15
  25. 16      /* 按下标志位为假 */
  26. 17      isDragging(false),
  27. 18
  28. 19      /* 默认偏移量为0 */
  29. 20      deviation(0),
  30. 21
  31. 22      /* 数值越大 */
  32. 23      numSize(15),
  33. 24
  34. 25      /* 间隔为1 */
  35. 26      interval(1),
  36. 27
  37. 28      /* 默认分成3格 */
  38. 29      devide(3),
  39. 30
  40. 31      /* 默认颜色黑色 */
  41. 32      numberColor(0, 0, 0)
  42. 33  {
  43. 34      setParent(parent);
  44. 35      setMinimumSize(50, 150);
  45. 36      homingAni = new QPropertyAnimation(this, "deviation");
  46. 37      homingAni->setDuration(300);
  47. 38      homingAni->setEasingCurve(QEasingCurve::OutQuad);
  48. 39  }
  49. 40
  50. 41  NumberPicker::~NumberPicker()
  51. 42  {
  52. 43
  53. 44  }
  54. 45
  55. 46  void NumberPicker::setRange(int min, int max)
  56. 47  {
  57. 48      minRange = min;
  58. 49      maxRange = max;
  59. 50      if (currentValue < min) {
  60. 51          currentValue = min;
  61. 52      }
  62. 53      if (currentValue > max) {
  63. 54          currentValue = max;
  64. 55      }
  65. 56      repaint();
  66. 57  }
  67. 58
  68. 59  int NumberPicker::readValue()
  69. 60  {
  70. 61      return currentValue;
  71. 62  }
  72. 63
  73. 64  void NumberPicker::mousePressEvent(QMouseEvent *e)
  74. 65  {
  75. 66      homingAni->stop();
  76. 67      isDragging = true;
  77. 68      mouseSrcPos = e->pos().y();
  78. 69      QWidget::mousePressEvent(e);
  79. 70  }
  80. 71
  81. 72  void NumberPicker::mouseMoveEvent(QMouseEvent *e)
  82. 73  {
  83. 74      if (isDragging){
  84. 75          deviation = e->pos().y() - mouseSrcPos;
  85. 76
  86. 77          /* 若移动速度过快,则进行限制 */
  87. 78          if (deviation > (height() - 1) / devide) {
  88. 79              deviation = (height() - 1) / devide;
  89. 80          } else if (deviation < -(height() - 1) / devide) {
  90. 81              deviation = -( height() - 1) / devide;
  91. 82          }
  92. 83
  93. 84          emit deviationChange(deviation / ((height() - 1) / devide));
  94. 85          repaint();
  95. 86      }
  96. 87  }
  97. 88
  98. 89  void NumberPicker::mouseReleaseEvent(QMouseEvent *)
  99. 90  {
  100. 91      if (isDragging) {
  101. 92          isDragging = false;
  102. 93          homing();
  103. 94      }
  104. 95  }
  105. 96
  106. 97  void NumberPicker::wheelEvent(QWheelEvent *e)
  107. 98  {
  108. 99      if (e->delta() > 0) {
  109. 100         deviation = (this->height() - 1) / devide;
  110. 101     } else {
  111. 102         deviation = -(this->height() - 1) / devide;
  112. 103     }
  113. 104
  114. 105     homing();
  115. 106     repaint();
  116. 107 }
  117. 108
  118. 109 void NumberPicker::paintEvent(QPaintEvent *)
  119. 110 {
  120. 111     QPainter painter(this);
  121. 112     painter.setRenderHint(QPainter::Antialiasing, true);
  122. 113     int Height = height() - 1;
  123. 114
  124. 115     if (deviation >= Height / devide && currentValue > minRange ) {
  125. 116         mouseSrcPos += Height / devide;
  126. 117         deviation -= Height / devide;
  127. 118         currentValue -= interval;
  128. 119     }
  129. 120
  130. 121     if (deviation <= -Height / devide && currentValue < maxRange ) {
  131. 122         mouseSrcPos -= Height / devide;
  132. 123         deviation += Height / devide;
  133. 124         currentValue += interval;
  134. 125     }
  135. 126
  136. 127     if (qAbs(int(currentValue)) >= int(maxRange))
  137. 128         currentValue = minRange;
  138. 129
  139. 130     paintNum(painter, qAbs(int(currentValue + maxRange) % maxRange),
  140. 131              deviation);
  141. 132
  142. 133     paintNum(painter,
  143. 134              qAbs((currentValue - interval + maxRange) % maxRange),
  144. 135              deviation - Height / devide);
  145. 136
  146. 137     paintNum(painter,
  147. 138              qAbs((currentValue + interval + maxRange) % maxRange),
  148. 139              deviation + Height / devide);
  149. 140
  150. 141     for (int i = 2; i <= devide / 2; ++i) {
  151. 142         if (qAbs(currentValue - interval * i) >= minRange) {
  152. 143             paintNum(painter,
  153. 144                      qAbs((currentValue - interval * i + maxRange)
  154. 145                           % maxRange),
  155. 146                      deviation - Height / devide * i);
  156. 147         }
  157. 148
  158. 149         if (qAbs(currentValue + interval * i) <= maxRange) {
  159. 150             paintNum(painter,
  160. 151                      qAbs((currentValue + interval * i + maxRange)
  161. 152                           % maxRange),
  162. 153                      deviation + Height / devide * i);
  163. 154         }
  164. 155     }
  165. 156 }
  166. 157
  167. 158 void NumberPicker::paintNum(QPainter &painter, int num, int deviation)
  168. 159 {
  169. 160     int Width = width() - 1;
  170. 161     int Height = height() - 1;
  171. 162
  172. 163     /* 偏移量越大,数字越小 */
  173. 164     //int size = (Height - qAbs(deviation)) / numSize;
  174. 165     int size = (Height - qAbs(deviation)) * numSize / 80;
  175. 166     int transparency = 255 - 255 * qAbs(deviation) / Height;
  176. 167     int height = Height / devide;
  177. 168     int y = Height / 2 + deviation - height / 2;
  178. 169
  179. 170     QFont font;
  180. 171     font.setPixelSize(size);
  181. 172     painter.setFont(font);
  182. 173     painter.setPen(QColor(numberColor.red(),
  183. 174                           numberColor.green(),
  184. 175                           numberColor.blue(),
  185. 176                           transparency));
  186. 177
  187. 178     if ( y >= 0 && y + height < Height) {
  188. 179         //painter.drawRect(0, y, Width, height);
  189. 180         if (num < 10)
  190. 181             painter.drawText(QRectF(0, y, Width, height),
  191. 182                              Qt::AlignCenter,
  192. 183                              "0" + QString::number(num, 'f', 0));
  193. 184         else
  194. 185             painter.drawText(QRectF(0, y, Width, height),
  195. 186                              Qt::AlignCenter,
  196. 187                              QString::number(num, 'f', 0));
  197. 188     }
  198. 189 }
  199. 190
  200. 191 void NumberPicker::homing()
  201. 192 {
  202. 193     if (deviation > height() / 10) {
  203. 194         homingAni->setStartValue((height() - 1 ) / 8 - deviation);
  204. 195         homingAni->setEndValue(0);
  205. 196         currentValue -= interval;
  206. 197     } else if (deviation > -height() / 10) {
  207. 198         homingAni->setStartValue(deviation);
  208. 199         homingAni->setEndValue(0);
  209. 200     } else if (deviation < -height() / 10) {
  210. 201         homingAni->setStartValue(-(height() - 1) / 8 - deviation);
  211. 202         homingAni->setEndValue(0);
  212. 203         currentValue += interval;
  213. 204     }
  214. 205
  215. 206     emit currentValueChanged(currentValue);
  216. 207     homingAni->start();
  217. 208 }
  218. 209
  219. 210 int NumberPicker::readDeviation()
  220. 211 {
  221. 212     return deviation;
  222. 213 }
  223. 214
  224. 215 void NumberPicker::setDeviation(int n)
  225. 216 {
  226. 217     deviation = n;
  227. 218     repaint();
  228. 219 }
  229. 220
  230. 221 void NumberPicker::setNumSize(int size)
  231. 222 {
  232. 223     numSize = size;
  233. 224     repaint();
  234. 225 }
  235. 226
  236. 227 void NumberPicker::setInterval(int n)
  237. 228 {
  238. 229     interval = n;
  239. 230     repaint();
  240. 231 }
  241. 232
  242. 233 void NumberPicker::setDevide(int n)
  243. 234 {
  244. 235     devide = n;
  245. 236     repaint();
  246. 237 }
  247. 238
  248. 239 void NumberPicker::setNumberColor(QRgb rgb)
  249. 240 {
  250. 241     numberColor.setRgb(rgb);
  251. 242     repaint();
  252. 243 }
  253. 244
  254. 245 void NumberPicker::setValue(int value)
  255. 246 {
  256. 247     if (value < minRange || value > maxRange) {
  257. 248         qDebug()<<"数值设置必须在"<<minRange
  258. 249                <<"和"<<maxRange<<"之间"<<endl;
  259. 250         return;
  260. 251     }
  261. 252     currentValue = value;
  262. 253     repaint();
  263. 254 }
复制代码


        开关按钮的效果如下。运行时有动画效果,类似IOS手册里的开关按钮一样。
数据库33017.png

        开关按钮的头文件“switchbutton.h”代码如下。
  
  1.   /******************************************************************
  2.     Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
  3.     * @projectName   17_sqlite_example
  4.     * @brief         switchbutton.h
  5.     * @author        Deng Zhimao
  6.     * @email         <a href="mailto:1252699831@qq.com" target="_blank">1252699831@qq.com</a>
  7.     * @net            <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  8.     * @date           2021-05-14
  9.     *******************************************************************/
  10. 1   #ifndef SWITCHBUTTON_H
  11. 2   #define SWITCHBUTTON_H
  12. 3
  13. 4   #include <QWidget>
  14. 5   #include <QTimer>
  15. 6
  16. 7   class SwitchButton : public QWidget
  17. 8   {
  18. 9       Q_OBJECT
  19. 10
  20. 11  public:
  21. 12      explicit SwitchButton(QWidget *parent = nullptr);
  22. 13
  23. 14      /* 返回开关状态 - 打开:true 关闭:false */
  24. 15      bool isToggled() const;
  25. 16
  26. 17      /* 设置开关状态 */
  27. 18      void setToggle(bool checked);
  28. 19
  29. 20      /* 设置背景颜色 */
  30. 21      void setBackgroundColor(QColor color);
  31. 22
  32. 23      /* 设置选中颜色 */
  33. 24      void setCheckedColor(QColor color);
  34. 25
  35. 26      /* 设置不可用颜色 */
  36. 27      void setDisbaledColor(QColor color);
  37. 28
  38. 29  protected:
  39. 30      /* 绘制开关 */
  40. 31      void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
  41. 32
  42. 33      /* 鼠标按下事件 */
  43. 34      void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
  44. 35
  45. 36      /* 鼠标释放事件 - 切换开关状态、发射toggled()信号 */
  46. 37      void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
  47. 38
  48. 39      /* 大小改变事件 */
  49. 40      void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE;
  50. 41
  51. 42      /* 缺省大小 */
  52. 43      QSize sizeHint() const Q_DECL_OVERRIDE;
  53. 44      QSize minimumSizeHint() const Q_DECL_OVERRIDE;
  54. 45
  55. 46  signals:
  56. 47      /* 状态改变时,发射信号 */
  57. 48      void toggled(bool checked);
  58. 49
  59. 50  private slots:
  60. 51      /* 状态切换时,用于产生滑动效果 */
  61. 52      void onTimeout();
  62. 53
  63. 54  private:
  64. 55      /* 是否选中 */
  65. 56      bool m_bChecked;
  66. 57      
  67. 58      /* 背景颜色 */
  68. 59      QColor m_background;
  69. 60      
  70. 61      /* 选中颜色 */
  71. 62      QColor m_checkedColor;
  72. 63      
  73. 64      /* 不可用颜色 */
  74. 65      QColor m_disabledColor;
  75. 66      
  76. 67      /* 拇指颜色 */
  77. 68      QColor m_thumbColor;
  78. 69      
  79. 70      /* 圆角 */
  80. 71      qreal m_radius;
  81. 72      
  82. 73      /* x点坐标 */
  83. 74      qreal m_nX;
  84. 75      
  85. 76      /* y点坐标 */
  86. 77      qreal m_nY;
  87. 78      
  88. 79      /* 高度 */
  89. 80      qint16 m_nHeight;
  90. 81      
  91. 82      /* 外边距 */
  92. 83      qint16 m_nMargin;
  93. 84      
  94. 85      /* 定时器 */
  95. 86      QTimer m_timer;
  96. 87  };
  97. 88  #endif // SWITCHBUTTON_H
复制代码

        开关按钮的源文件“switchbutton.cpp”代码如下。
  1.    /******************************************************************
  2.     Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
  3.     * @projectName   17_sqlite_example
  4.     * @brief         switchbutton.cpp
  5.     * @author        Deng Zhimao
  6.     * @email         <a href="mailto:1252699831@qq.com" target="_blank">1252699831@qq.com</a>
  7.     * @net            <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  8.     * @date           2021-05-14
  9.     *******************************************************************/
  10. 1   #include "switchbutton.h"
  11. 2  
  12. 3   #include <QPainter>
  13. 4   #include <QMouseEvent>
  14. 5  
  15. 6   SwitchButton::SwitchButton(QWidget *parent)
  16. 7       : QWidget(parent),
  17. 8         m_bChecked(false),
  18. 9         m_background(Qt::gray),
  19. 10        m_checkedColor(34, 131, 246),
  20. 11        m_disabledColor(190, 190, 190),
  21. 12        m_thumbColor(Qt::gray),
  22. 13        m_radius(12.5),
  23. 14        m_nHeight(16),
  24. 15        m_nMargin(3)
  25. 16  {
  26. 17      /* 鼠标滑过光标形状 - 手型 */
  27. 18      setCursor(Qt::PointingHandCursor);
  28. 19
  29. 20      /* 连接信号槽 */
  30. 21      connect(&m_timer, SIGNAL(timeout()),
  31. 22              this, SLOT(onTimeout()));
  32. 23  }
  33. 24
  34. 25  /* 绘制开关 */
  35. 26  void SwitchButton::paintEvent(QPaintEvent *event)
  36. 27  {
  37. 28      Q_UNUSED(event)
  38. 29
  39. 30      QPainter painter(this);
  40. 31      painter.setPen(Qt::NoPen);
  41. 32      painter.setRenderHint(QPainter::Antialiasing);
  42. 33
  43. 34      QPainterPath path;
  44. 35      QColor background;
  45. 36      QColor thumbColor;
  46. 37      qreal dOpacity;
  47. 38      /* 可用状态 */
  48. 39      if (isEnabled()) {
  49. 40          /* 打开状态 */
  50. 41          if (m_bChecked) {
  51. 42              background = m_checkedColor;
  52. 43              thumbColor = m_checkedColor;
  53. 44              dOpacity = 0.600;
  54. 45              /* 关闭状态 */
  55. 46          } else {
  56. 47              background = m_background;
  57. 48              thumbColor = m_thumbColor;
  58. 49              dOpacity = 0.800;
  59. 50          }
  60. 51          /* 不可用状态 */
  61. 52      } else {
  62. 53          background = m_background;
  63. 54          dOpacity = 0.260;
  64. 55          thumbColor = m_disabledColor;
  65. 56      }
  66. 57      /* 绘制大椭圆 */
  67. 58      painter.setBrush(background);
  68. 59      painter.setOpacity(dOpacity);
  69. 60      path.addRoundedRect(QRectF(m_nMargin,
  70. 61                                 m_nMargin, width() - 2 * m_nMargin,
  71. 62                                 height() - 2 * m_nMargin),
  72. 63                          m_radius, m_radius);
  73. 64      painter.drawPath(path.simplified());
  74. 65
  75. 66      /* 绘制小椭圆 */
  76. 67      painter.setBrush(thumbColor);
  77. 68      painter.setOpacity(1.0);
  78. 69      painter.drawEllipse(QRectF(m_nX - (m_nHeight / 2),
  79. 70                                 m_nY - (m_nHeight / 2),
  80. 71                                 height(),
  81. 72                                 height()));
  82. 73  }
  83. 74
  84. 75  /* 鼠标按下事件 */
  85. 76  void SwitchButton::mousePressEvent(QMouseEvent *event)
  86. 77  {
  87. 78      if (isEnabled()) {
  88. 79          if (event->buttons() & Qt::LeftButton) {
  89. 80              event->accept();
  90. 81          } else {
  91. 82              event->ignore();
  92. 83          }
  93. 84      }
  94. 85  }
  95. 86
  96. 87  /* 鼠标释放事件 - 切换开关状态、发射toggled()信号 */
  97. 88  void SwitchButton::mouseReleaseEvent(QMouseEvent *event)
  98. 89  {
  99. 90      if (isEnabled()) {
  100. 91          if ((event->type() == QMouseEvent::MouseButtonRelease)
  101. 92                  && (event->button() == Qt::LeftButton)) {
  102. 93              event->accept();
  103. 94              m_bChecked = !m_bChecked;
  104. 95              emit toggled(m_bChecked);
  105. 96              m_timer.start(10);
  106. 97          } else {
  107. 98              event->ignore();
  108. 99          }
  109. 100     }
  110. 101 }
  111. 102
  112. 103 /* 大小改变事件 */
  113. 104 void SwitchButton::resizeEvent(QResizeEvent *event)
  114. 105 {
  115. 106     m_nX = m_nHeight / 2;
  116. 107     m_nY = m_nHeight / 2;
  117. 108     QWidget::resizeEvent(event);
  118. 109 }
  119. 110
  120. 111 /* 默认大小 */
  121. 112 QSize SwitchButton::sizeHint() const
  122. 113 {
  123. 114     return minimumSizeHint();
  124. 115 }
  125. 116
  126. 117 /* 最小大小 */
  127. 118 QSize SwitchButton::minimumSizeHint() const
  128. 119 {
  129. 120     return QSize(2 * (m_nHeight + m_nMargin),
  130. 121                  m_nHeight + 2 * m_nMargin);
  131. 122 }
  132. 123
  133. 124 /* 切换状态 - 滑动 */
  134. 125 void SwitchButton::onTimeout()
  135. 126 {
  136. 127     if (m_bChecked) {
  137. 128         m_nX += 1;
  138. 129         if (m_nX >= width() - m_nHeight - m_nHeight / 2 ) {
  139. 130             m_timer.stop();
  140. 131             m_nX -= 1;
  141. 132         }
  142. 133     } else {
  143. 134         m_nX -= 1;
  144. 135         if (m_nX <= m_nHeight / 2) {
  145. 136             m_timer.stop();
  146. 137             m_nX += 1;
  147. 138         }
  148. 139     }
  149. 140     update();
  150. 141 }
  151. 142
  152. 143 /* 返回开关状态 - 打开:true 关闭:false */
  153. 144 bool SwitchButton::isToggled() const
  154. 145 {
  155. 146     return m_bChecked;
  156. 147 }
  157. 148
  158. 149 /* 设置开关状态 */
  159. 150 void SwitchButton::setToggle(bool checked)
  160. 151 {
  161. 152     m_bChecked = checked;
  162. 153     m_timer.start(10);
  163. 154 }
  164. 155
  165. 156 /* 设置背景颜色 */
  166. 157 void SwitchButton::setBackgroundColor(QColor color)
  167. 158 {
  168. 159     m_background = color;
  169. 160 }
  170. 161
  171. 162 /* 设置选中颜色 */
  172. 163 void SwitchButton::setCheckedColor(QColor color)
  173. 164 {
  174. 165     m_checkedColor = color;
  175. 166 }
  176. 167
  177. 168 /* 设置不可用颜色 */
  178. 169 void SwitchButton::setDisbaledColor(QColor color)
  179. 170 {
  180. 171     m_disabledColor = color;
  181. 172 }
复制代码


13.2.1.1 程序运行效果
点击程序正下方的“+”按钮则开始添加闹钟数据,根据当前系统的时间或者滑动选择闹钟的时间,点击确认即新增一条闹钟数据。
数据库40423.png

        修改或者删除数据,点击要修改闹钟的数据项目,弹出的对话框,可以重新设置闹钟或者删除闹钟。并保存记录到数据库里,下次运行打开时会从数据库里读取保存的闹钟数据。
数据库40505.png

13.2.2 数据库表格(QTableView显示)
本小节设计一个生活中的例子,使用数据库修改/查询员工的编号、姓名、年龄、性别与照片信息。
本例将数据库的内容显示到QTableView上。如果只是简单的显示数据库的内容到QTableView上,可以使用下面的方法,此方法QTableView上可以看到员工的编号、姓名、年龄、性别信息,同时可以双击表格进行项修改,修改完成将自动保存到数据库里。
  1. 1     /* 初始化表格模型 */
  2. 2     QSqlTableModel *model = new QSqlTableModel(this, sqlDatabase);
  3. 3
  4. 4     /* 设置要选中的表格名称 */
  5. 5     model->setTable("employee");
  6. 6     /* 如果有修改则同步修改到数据库,
  7. 7      * 注意这个规则需要与tabview这样的控件才生效,
  8. 8      * 因为tabview可以直接编辑表里的内容 */
  9. 9     model->setEditStrategy(QSqlTableModel::OnFieldChange);
  10. 10    /* 成功则返回true,查看数据库里是否有employee这个表格 */
  11. 11    model->select();
  12. 12    /* 设置表格的头信息,若不设置则显示数据库里的英文字段头信息 */
  13. 13    model->setHeaderData(model->fieldIndex("id"),
  14. 14                         Qt::Horizontal, tr("编号"));
  15. 15    model->setHeaderData(model->fieldIndex("name"),
  16. 16                         Qt::Horizontal, tr("姓名"));
  17. 17    model->setHeaderData(model->fieldIndex("age"),
  18. 18                         Qt::Horizontal, tr("年龄"));
  19. 19    model->setHeaderData(model->fieldIndex("sex"),
  20. 20                         Qt::Horizontal, tr("性别"));
  21. 21
  22. 22    QTableView *view = new QTableView;
  23. 23
  24. 24    /* 设置表格的模型为model */
  25. 25    view->setModel(model);
  26. 26    /* 不显示图片路径信息行 */
  27. 27    view->hideColumn(4);
  28. 28    /* 表格居中 */
  29. 29    setCentralWidget(view);
  30. 30    return;
复制代码
数据库41759.png

        上面的程序可以修改数据库的内容也可以查看。但是看不到员工的照片信息。本例就讲解如何将数据库数据显示到QTableView上,及查看选择的员工项的全部信息显示。介绍Qt如何使用数据库存储照片的信息。我们知道数据库类型有个BLOB数据类型可以用于存储照片信息。但是本例并不那样做,当数据库数据很多时,将照片(二进制数据)存储到数据库里就不是一个明智的选择了。大字段数据会加重数据库的负担,拖慢数据库,数据库文件越小访问肯定越快,数据库也不用遍历那么多内容,或者加载那么大的数据到内存里,造成响应不及时等。计算机可能处理速度愉快,但是对于普通的单核和多核ARM开发板来说速度可能会跟不上啊!所以照片最好是存储照片的路径。照片路径属于字符串文本,不会占用太多空间。
好了现在上例子,例子就是查询员工的编号、姓名、年龄、性别与照片信息,简单实用好理解,不复杂。
本例目的:用QTableView显示数据库表的数据,显示员工的信息。
例18_sqlite_table,查询数据表(难度:一般)。项目路径为Qt/2/ 18_sqlite_table。
项目文件18_sqlite_table文件第一行添加的代码部分如下。
18_sqlite_table.pro编程后的代码
  1. 1   QT       += core gui sql
  2. 2
  3. 3   greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
  4. 4
  5. 5   CONFIG += c++11
  6. 6
  7. 7   # The following define makes your compiler emit warnings if you use
  8. 8   # any Qt feature that has been marked deprecated (the exact warnings
  9. 9   # depend on your compiler). Please consult the documentation of the
  10. 10  # deprecated API in order to know how to port your code away from it.
  11. 11  DEFINES += QT_DEPRECATED_WARNINGS
  12. 12
  13. 13  # You can also make your code fail to compile if it uses deprecated APIs.
  14. 14  # In order to do so, uncomment the following line.
  15. 15  # You can also select to disable deprecated APIs only up to a certain version of Qt.
  16. 16  #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
  17. 17
  18. 18  SOURCES += \
  19. 19      main.cpp \
  20. 20      mainwindow.cpp
  21. 21
  22. 22  HEADERS += \
  23. 23      mainwindow.h
  24. 24
  25. 25  # Default rules for deployment.
  26. 26  qnx: target.path = /tmp/${TARGET}/bin
  27. 27  else: unix:!android: target.path = /opt/${TARGET}/bin
  28. 28  !isEmpty(target.path): INSTALLS += target
  29. 29
  30. 30  RESOURCES +=
复制代码

在头文件“mainwindow.h”具体代码如下。
mainwindow.h编程后的代码
  1.    /******************************************************************
  2.     Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
  3.     * @projectName   18_sqlite_table
  4.     * @brief         mainwindow.h
  5.     * @author        Deng Zhimao
  6.     * @email         <a href="mailto:1252699831@qq.com" target="_blank">1252699831@qq.com</a>
  7.     * @net            <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  8.     * @date           2021-05-18
  9.     *******************************************************************/
  10. 1   #ifndef MAINWINDOW_H
  11. 2   #define MAINWINDOW_H
  12. 3
  13. 4   #include <QSqlDatabase>
  14. 5   #include <QSqlQuery>
  15. 6   #include <QMainWindow>
  16. 7   #include <QLabel>
  17. 8   #include <QSqlTableModel>
  18. 9   #include <QHBoxLayout>
  19. 10  #include <QVBoxLayout>
  20. 11  #include <QGridLayout>
  21. 12  #include <QTableView>
  22. 13  #include <QComboBox>
  23. 14  #include <QLineEdit>
  24. 15  #include <QDataWidgetMapper>
  25. 16  #include <QSqlQueryModel>
  26. 17  #include <QItemSelectionModel>
  27. 18  #include <QSpinBox>
  28. 19
  29. 20  class MainWindow : public QMainWindow
  30. 21  {
  31. 22      Q_OBJECT
  32. 23
  33. 24  public:
  34. 25      MainWindow(QWidget *parent = nullptr);
  35. 26      ~MainWindow();
  36. 27
  37. 28  private:
  38. 29
  39. 30      /* 数据库连接类 */
  40. 31      QSqlDatabase sqlDatabase;
  41. 32
  42. 33      /* 用于查询数据 */
  43. 34      QSqlQueryModel *sqlQueryModel;
  44. 35
  45. 36      /* 数据映射 */
  46. 37      QDataWidgetMapper *dataWidgetMapper;
  47. 38
  48. 39      /* 选择模型 */
  49. 40      QItemSelectionModel * itemSelectionModel;
  50. 41
  51. 42      /* 水平布局 */
  52. 43      QHBoxLayout *hBoxLayout[2];
  53. 44
  54. 45      /* 垂直布局 */
  55. 46      QVBoxLayout *vBoxLayout;
  56. 47
  57. 48      /* 网格布局 */
  58. 49      QGridLayout *gridLayout;
  59. 50
  60. 51      /* 用于显示的表格*/
  61. 52      QTableView *tableView;
  62. 53
  63. 54      /* 主Widget */
  64. 55      QWidget *mainWidget;
  65. 56
  66. 57      /* 底部容器 */
  67. 58      QWidget *bottomWidget;
  68. 59
  69. 60      /* 底部网格布局容器 */
  70. 61      QWidget *gridWidget;
  71. 62
  72. 63      /* 照片容器 */
  73. 64      QWidget *photoWidget;
  74. 65
  75. 66      /* Label,用于显示照片 */
  76. 67      QLabel *imageLabel;
  77. 68
  78. 69      /* Label,底部显示文本 */
  79. 70      QLabel *label[4];
  80. 71
  81. 72      /* 性别下拉选择框,选择信息 */
  82. 73      QComboBox *comboBox;
  83. 74
  84. 75      /* 数值选择框,[0, 100] */
  85. 76      QSpinBox *spinBox[2];
  86. 77
  87. 78      /* 单行输入框  */
  88. 79      QLineEdit *lineEdit;
  89. 80
  90. 81  private slots:
  91. 82      /* 表格当前行变化执行的槽函数 */
  92. 83      void on_currentRowChanged(const QModelIndex&, const QModelIndex&);
  93. 84  };
  94. 85  #endif // MAINWINDOW_H
  95. 86
复制代码

        头文件主要声明布局用的类和数据库,重要关注是QSqlDatabase、QSqlQueryModel 、QdataWidgetMapper和QItemSelectionModel。这里声明的是全局变量。
在源文件“mainwindow.cpp”具体代码如下。
mainwindow.cpp编程后的代码
  1.     /******************************************************************
  2.     Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
  3.     * @projectName   18_sqlite_table
  4.     * @brief         mainwindow.cpp
  5.     * @author        Deng Zhimao
  6.     * @email         <a href="mailto:1252699831@qq.com" target="_blank">1252699831@qq.com</a>
  7.     * @net            <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  8.     * @date           2021-05-18
  9.     *******************************************************************/
  10. 1   #include "mainwindow.h"
  11. 2   #include <QDebug>
  12. 3   #include <QHeaderView>
  13. 4   #include <QSqlError>
  14. 5   #include <QApplication>
  15. 6   #include <QSqlRecord>
  16. 7  
  17. 8   MainWindow::MainWindow(QWidget *parent)
  18. 9       : QMainWindow(parent)
  19. 10  {
  20. 11      /* 设置主窗体的显示位置与大小 */
  21. 12      this->setGeometry(0, 0, 800, 480);
  22. 13      
  23. 14      /* 查看本机可用的数据库驱动 */
  24. 15      QStringList drivers = QSqlDatabase::drivers();
  25. 16      foreach(QString driver, drivers) {
  26. 17          qDebug()<<driver;
  27. 18      }
  28. 19      
  29. 20      /* 以QSQLITE驱动方式打开或者创建数据库 */
  30. 21      sqlDatabase = QSqlDatabase::addDatabase("QSQLITE");
  31. 22      sqlDatabase.setDatabaseName("employee.db");
  32. 23      /* 以open的方式打开employee.db数据库,则会创建一个employee.db */
  33. 24      if (!sqlDatabase.open())
  34. 25          qDebug()<<"连接数据库错误"<<sqlDatabase.lastError()<<endl;
  35. 26      else
  36. 27          qDebug()<<"连接数据库成功"<<endl;
  37. 28      
  38. 29      QSqlQuery query(sqlDatabase);
  39. 30      /* 使用指令式创建表 */
  40. 31      query.exec("create table employee (id int primary key, name vchar(10), "
  41. 32                 "age int, sex vchar(3), photo text)");
  42. 33      
  43. 34      QStringList photoPath;
  44. 35      /* 当前可执行程序的路径 */
  45. 36      QString path(QApplication::applicationDirPath());
  46. 37      photoPath<< path + "/photos/啊万.jpg"<< path + "/photos/啊棠.jpg";
  47. 38      
  48. 39      /* 以指令的方式插入数据,如果数据已经存在则不会成功不能插入 */
  49. 40      query.exec(tr("insert into employee values(1, '啊万', 27, '男', '%1')").arg(photoPath[0]));
  50. 41      query.exec(tr("insert into employee values(2, '啊棠', 28, '男', '%1')").arg(photoPath[1]));
  51. 42      
  52. 43      //    /* 初始化表格模型 */
  53. 44      //    QSqlTableModel *model = new QSqlTableModel(this, sqlDatabase);
  54. 45      
  55. 46      //    /* 设置要选中的表格名称 */
  56. 47      //    model->setTable("employee");
  57. 48      //    /* 如果有修改则同步修改到数据库,
  58. 49      //     * 注意这个规则需要与tabview这样的控件才生效,
  59. 50      //     * 因为tabview可以直接编辑表里的内容 */
  60. 51      //    model->setEditStrategy(QSqlTableModel::OnFieldChange);
  61. 52      //    /* 成功则返回true,查看数据库里是否有employee这个表格 */
  62. 53      //    model->select();
  63. 54      //    /* 设置表格的头信息,若不设置则显示数据库里的英文字段头信息 */
  64. 55      //    model->setHeaderData(model->fieldIndex("id"),
  65. 56      //                         Qt::Horizontal, tr("编号"));
  66. 57      //    model->setHeaderData(model->fieldIndex("name"),
  67. 58      //                         Qt::Horizontal, tr("姓名"));
  68. 59      //    model->setHeaderData(model->fieldIndex("age"),
  69. 60      //                         Qt::Horizontal, tr("年龄"));
  70. 61      //    model->setHeaderData(model->fieldIndex("sex"),
  71. 62      //                         Qt::Horizontal, tr("性别"));
  72. 63      
  73. 64      //    QTableView *view = new QTableView;
  74. 65      
  75. 66      //    /* 设置表格的模型为model */
  76. 67      //    view->setModel(model);
  77. 68      //    /* 不显示图片路径信息行 */
  78. 69      //    view->hideColumn(4);
  79. 70      //    /* 表格居中 */
  80. 71      //    setCentralWidget(view);
  81. 72      //    return;
  82. 73      
  83. 74      /* QSqlQueryModel适合用于查询数据,不能修改数据 */
  84. 75      sqlQueryModel = new QSqlQueryModel(this);
  85. 76      
  86. 77      /* 选择编号,姓名,年龄和性别的内容,显示到tableView上,
  87. 78       * 图片最后通过数据选择再读取Label上 */
  88. 79      sqlQueryModel->setQuery("select id, name, age, sex from employee");
  89. 80      
  90. 81      if (sqlQueryModel->lastError().isValid())
  91. 82          qDebug()<<"选择数据失败!"<<endl;
  92. 83      
  93. 84      sqlQueryModel->setHeaderData(0, Qt::Horizontal, "编号");
  94. 85      sqlQueryModel->setHeaderData(1, Qt::Horizontal, "姓名");
  95. 86      sqlQueryModel->setHeaderData(2, Qt::Horizontal, "年龄");
  96. 87      sqlQueryModel->setHeaderData(3, Qt::Horizontal, "性别");
  97. 88      
  98. 89      tableView = new QTableView();
  99. 90      tableView->setModel(sqlQueryModel);
  100. 91      
  101. 92      /* 设置显示平均分列 */
  102. 93      tableView->horizontalHeader()
  103. 94              ->setSectionResizeMode(QHeaderView::Stretch);
  104. 95      
  105. 96      mainWidget = new QWidget();
  106. 97      bottomWidget = new QWidget();
  107. 98      gridWidget = new QWidget();
  108. 99      photoWidget = new QWidget();
  109. 100     imageLabel = new QLabel();
  110. 101     
  111. 102     /* 设置照片属性 */
  112. 103     imageLabel->setScaledContents(true);
  113. 104     imageLabel->setMaximumSize(200, 200);
  114. 105     
  115. 106     
  116. 107     vBoxLayout = new QVBoxLayout();
  117. 108     hBoxLayout[0] = new QHBoxLayout();
  118. 109     hBoxLayout[1] = new QHBoxLayout();
  119. 110     gridLayout = new QGridLayout();
  120. 111     
  121. 112     for (int i = 0; i < 4; i++)
  122. 113         label = new QLabel();
  123. 114     
  124. 115     for (int i = 0; i < 2; i++) {
  125. 116         spinBox = new QSpinBox();
  126. 117         spinBox->setRange(1, 100);
  127. 118     }
  128. 119     
  129. 120     comboBox = new QComboBox();
  130. 121     comboBox->addItem("男");
  131. 122     comboBox->addItem("女");
  132. 123     
  133. 124     lineEdit = new QLineEdit();
  134. 125     
  135. 126     bottomWidget->setMinimumHeight(this->height() / 2 - 30);
  136. 127     gridWidget->setMaximumWidth(this->width() / 2 - 30);
  137. 128     
  138. 129     /* 垂直布局 */
  139. 130     vBoxLayout->addWidget(tableView);
  140. 131     vBoxLayout->addWidget(bottomWidget);
  141. 132     
  142. 133     mainWidget->setLayout(vBoxLayout);
  143. 134     setCentralWidget(mainWidget);
  144. 135     
  145. 136     /* 水平布局 */
  146. 137     hBoxLayout[0]->addWidget(gridWidget);
  147. 138     hBoxLayout[0]->addWidget(photoWidget);
  148. 139     bottomWidget->setLayout(hBoxLayout[0]);
  149. 140     
  150. 141     QStringList list;
  151. 142     list<<"姓名:"<<"编号:"<<"年龄:"<<"性别:";
  152. 143     
  153. 144     /* 网格布局 */
  154. 145     for (int i = 0; i < 4; i++) {
  155. 146         gridLayout->addWidget(label, i, 0);
  156. 147         label->setText(list);
  157. 148         switch (i) {
  158. 149         case 0:
  159. 150             gridLayout->addWidget(lineEdit, i, 1);
  160. 151             break;
  161. 152         case 1:
  162. 153             gridLayout->addWidget(spinBox[0], i, 1);
  163. 154             break;
  164. 155         case 2:
  165. 156             gridLayout->addWidget(spinBox[1], i, 1);
  166. 157             break;
  167. 158         case 3:
  168. 159             gridLayout->addWidget(comboBox, i, 1);
  169. 160             break;
  170. 161         default:
  171. 162             break;
  172. 163         }
  173. 164     }
  174. 165     
  175. 166     gridWidget->setLayout(gridLayout);
  176. 167     hBoxLayout[1]->addWidget(imageLabel);
  177. 168     photoWidget->setLayout(hBoxLayout[1]);
  178. 169     
  179. 170     itemSelectionModel = new QItemSelectionModel(sqlQueryModel);
  180. 171     tableView->setSelectionModel(itemSelectionModel);
  181. 172     
  182. 173     /* 信号槽连接,表示表中行数据变化时,触发槽函数 */
  183. 174     connect(itemSelectionModel,
  184. 175             SIGNAL(currentRowChanged(QModelIndex, QModelIndex)),
  185. 176             this,
  186. 177             SLOT(on_currentRowChanged(QModelIndex, QModelIndex)));
  187. 178     
  188. 179     dataWidgetMapper = new QDataWidgetMapper(this);
  189. 180     /* 设置为自动提交 */
  190. 181     dataWidgetMapper->setSubmitPolicy(QDataWidgetMapper::AutoSubmit);
  191. 182     dataWidgetMapper->setModel(sqlQueryModel);
  192. 183     /* 创建数据映射,将前面的数据库内容映射到控件上 */
  193. 184     dataWidgetMapper->addMapping(lineEdit, 1);
  194. 185     dataWidgetMapper->addMapping(spinBox[0], 0);
  195. 186     dataWidgetMapper->addMapping(spinBox[1], 2);
  196. 187     dataWidgetMapper->addMapping(comboBox, 3);
  197. 188 }
  198. 189
  199. 190 MainWindow::~MainWindow()
  200. 191 {
  201. 192     /* 关闭数据库 */
  202. 193     sqlDatabase.close();
  203. 194 }
  204. 195
  205. 196 void MainWindow::on_currentRowChanged(const QModelIndex ¤t,
  206. 197                                       const QModelIndex &previous)
  207. 198 {
  208. 199     Q_UNUSED(previous)
  209. 200     /* 更新数据映射行号,初始化时映射到第0行 */
  210. 201     dataWidgetMapper->setCurrentModelIndex(current);
  211. 202     /* 获取当前行号 */
  212. 203     int row =  itemSelectionModel->currentIndex().row();
  213. 204     /* 获取当前模型记录 */
  214. 205     QSqlRecord record = sqlQueryModel->record(row);
  215. 206     /* 获取id信息 */
  216. 207     int id = record.value("id").toInt();
  217. 208     QSqlQuery query;
  218. 209     /* 使用bindValue绑定prepare里语句的值,需要使用":",":"是占位符 */
  219. 210     query.prepare("select photo from employee where id = :ID");
  220. 211     query.bindValue(":ID", id);
  221. 212     query.exec();
  222. 213     /* 返回到选择的第一条记录,因为id是唯一的,也只有一条记录 */
  223. 214     query.first();
  224. 215     
  225. 216     /* 获取字段为photo的值,也就是存储照片的路径 */
  226. 217     QVariant temp = query.value("photo");
  227. 218     if (!temp.isValid()) {
  228. 219         qDebug()<<"数据无效!"<<endl;
  229. 220         return;
  230. 221     }
  231. 222     
  232. 223     /* 清空图片显示 */
  233. 224     imageLabel->clear();
  234. 225     
  235. 226     QImage image(temp.toString());
  236. 227     
  237. 228     if (image.isNull()) {
  238. 229         qDebug()<<"未找到"<<temp.toString()<<endl;
  239. 230         return;
  240. 231     }
  241. 232     
  242. 233     /* 显示照片 */
  243. 234     imageLabel->setPixmap(QPixmap::fromImage(image));
  244. 235 }
复制代码

        第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. 1   #include "mainwindow.h"
  2. 2
  3. 3   #include <QApplication>
  4. 4
  5. 5   int main(int argc, char *argv[])
  6. 6   {
  7. 7       QApplication a(argc, argv);
  8. 8       MainWindow w;
  9. 9       w.show();
  10. 10      return a.exec();
  11. 11  }
复制代码

13.2.2.1 程序运行效果
       运行本例时,先点击构建,构建完成后将本项目下的整个photos文件夹拷贝到构建出来的build-18_sqlite_table-Desktop_Qt_5_12_9_GCC_64bit-Debug目录下。因为本程序会从数据库里读取出员工的照片路径信息,所以需要提前将照片文件夹放至可执行程序同一级目录下。点击表中的项,当切换员工的信息里,我们可以看到左下角被映射的内容发生了改变,变成了当前选择行的员工信息,右下角的照片头像也变成了该员工的照片信息。本例实现的就是从数据库里取数据并显示到QTableView及搭配其他控件使用的例子。
       员工啊万的信息:
数据库55753.png



        员工啊棠信息:

数据库55770.png

回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-20 12:49

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表