QT学习开发笔记(数据库之实用时钟)
数据库
数据库是什么?简易言之,就是保存数据的文件。可以存储大量数据,包括插入数据、更
新数据、截取数据等。用专业术语来说,数据库是“按照数据结构来组织、存储和管理数据的
仓库”。是一个长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集合。
什么时候需要数据库?在嵌入式里,存储大量数据,或者记录数据,就需要用到数据库。
举个简单的例子,比如手机的闹钟就使用到了数据库,我们设置的闹钟数据将会保存到数据库
里,闹钟程序运行时会从数据库里读取出上次保存的闹钟数据。如果没有数据库,则闹钟程序
关机了数据不保存在物理储存设备里,下次运行闹钟时就没有上次设置的闹钟数据,这显然是
不合理的。所以我们需要用到数据库。
本章认为读者已经基本了解数据库,已经对数据库有一定的认识,如果没有对数据库了解,
请自行学习,毕竟本书是讲 Qt 的,不是讲数据库,数据库知识很多,而我们只是讲解 Qt 怎么
去用数据库,对数据库的简单操作!目的就是在 Qt 里使用数据库!
想要在项目中使用 Qt SQL 模块,需要在项目配置文件里添加如下语句。
QT += core gui sql
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,如需要
其他类型数据库,请自行移植与学习!
应用实例
本章不讲解数据库中的语法,本书认为读者是已经数据库语法有一定了解的了,请知悉!
详细声明请看本章前言!本章前言。
Model(模型),复杂的事情往往可以简单化,Qt 提供了 QSqlDatabase 类用于建立数据库的
连接,往往以指定加载的数据库驱动,然后设置数据库的登录参数,如主机地址,用户名、登
录密码等。这些都是服务器类型的数据库所需要做的操作。恰好单机型(本地数据库类型)的
Sqlite3 数据库不需要设置登录参数就可以方便的打开数据库进行操作了。在 QSqlDatabase 连接
数据库后,用 QSqlTableModel 从数据库里读取出表格模型,然后通过 Qt 的 QTableView 类显示
数据库的内容在我们面前。需要对数据库的数据进行修改可以使用 QSqlQuery,或者直接修改
QSqlTableModel 对象,修改里面的模型数据即可!Qt 对数据库的基本操作流程大概是这样子,
当然 Qt 提供了很多操作数据库的类,我们只讲解基本的与常用的就已经足够了。下面用个图示
来再对上面的操作稍微了解一下。
实用闹钟(非 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 编程后的代码
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 <QSqlDatabase> 5 #include <QSqlQuery> 6 #include <QMainWindow> 7 #include <QDialog> 8 #include <QHBoxLayout> 9 #include <QVBoxLayout> 10 #include <QPushButton> 11 #include <QListWidget> 12 #include <QLabel> 13 #include <QTime> 14 #include <QSqlTableModel> 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> 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 <QDebug> 3 #include <QSqlError> 4 5 MainWindow::MainWindow(QWidget *parent) 6 : QMainWindow(parent) 7 { 8 /* 设置主窗体的显示位置与大小 */ 9 this->setGeometry(0, 0, 800, 480); 11 /* 查看本机可用的数据库驱动 */ 12 QStringList drivers = QSqlDatabase::drivers(); 13 foreach(QString driver, drivers) { 14 qDebug()<<driver; 15 } 16 17 /* 以 QSQLITE 驱动方式打开或者创建数据库 */ 18 sqlDatabase = QSqlDatabase::addDatabase("QSQLITE"); 19 sqlDatabase.setDatabaseName("alarm.db"); 20 /* 以 open 的方式打开 alarm.db 数据库,则会创建一个 alarm.db */ 21 if (!sqlDatabase.open()) 22 qDebug()<<"连接数据库错误"<<sqlDatabase.lastError()<<endl; 23 else 24 qDebug()<<"连接数据库成功"<<endl; 25 26 QSqlQuery query(sqlDatabase); 27 /* 使用指令式创建表 */ 28 query.exec("create table alarm (id int primary key, time vchar(15),
flag vchar(5))"); 29 /* 以指令的方式插入数据 */ 30 //query.exec("insert into alarm values(0, '06:00', 'false')"); 31 32 model = new QSqlTableModel(this, sqlDatabase); 33 34 /* 模型设置表的名字,需要与数据库的表的名字相同 */ 35 model->setTable("alarm"); 36 37 /* 如果有修改则同步修改到数据库,
38 * 注意这个规则需要与 tabview 这样的控件才生效,
39 * 因为 tabview 可以直接编辑表里的内容 */ 40 model->setEditStrategy(QSqlTableModel::OnFieldChange); 41 42 /* 成功则返回 true,查看数据库里是否有 alarm 这个表格 */ 43 model->select(); 44 45 /* 如果数据表数据为空,则添加两个闹钟 */ 46 if (model->rowCount() == 0) { 47 /* 插入一行 */ 48 model->insertRow(model->rowCount()); 49 /* 在该行插入数据 */ 50 model->setData(model->index(0, 0), 1); 51 model->setData(model->index(0, 1), "06:00"); 52 model->setData(model->index(0, 2), "false"); 53 /* 插入数据后记得提交 */ 54 model->submit(); 55 56 /* 再插入一行 */ 57 model->insertRow(model->rowCount()); 58 model->setData(model->index(1, 0), 2); 59 model->setData(model->index(1, 1), "18:00"); 60 model->setData(model->index(1, 2), "true"); 61 /* 提交 */ 62 model->submit(); 63 } 64 65 hourPicker = new NumberPicker(this); 66 hourPicker->setRange(0, 24); 67 68 minutePicker = new NumberPicker(this); 69 minutePicker->setRange(0, 60); 70 71 /* 标签,用于显示时&分 */ 72 QLabel *label[3]; 73 label[0] = new QLabel(); 74 label[1] = new QLabel(); 75 label[2] = new QLabel(); 76 77 QFont font; 78 font.setBold(true); 79 font.setPixelSize(10); 80 QPalette pal; 81 pal.setBrush(QPalette::WindowText, QColor(0, 0, 0)); 82 83 label[0]->setFont(font); 84 label[1]->setFont(font); 85 label[2]->setFont(font); 86 87 label[0]->setText(" "); 88 label[1]->setText("时"); 89 label[2]->setText("分"); 90 91 /* 主布局初始化 */ 92 listWidget = new QListWidget(); 93 mainWidget = new QWidget(); 94 bottomWidget = new QWidget(); 95 alarmDialog = new QDialog(this); 96 timeWidget = new QWidget(); 97 btWidget = new QWidget(); 98 addAlarm = new QPushButton(); 99 yesButton = new QPushButton(); 100 cancelButton = new QPushButton(); 101 vBoxLayout[0] = new QVBoxLayout(); 102 vBoxLayout[1] = new QVBoxLayout(); 103 hBoxLayout[0] = new QHBoxLayout(); 104 hBoxLayout[1] = new QHBoxLayout(); 105 hBoxLayout[2] = new QHBoxLayout(); 106 107 addAlarm->setMaximumSize(84, 84); 108 addAlarm->setObjectName("addAlarm"); 109 addAlarm->setMinimumSize(84, 84); 110 bottomWidget->setMinimumHeight(84); 111 bottomWidget->setMaximumHeight(84); 112 yesButton->setText("确认"); 113 cancelButton->setText("取消"); 114 yesButton->setMaximumSize(100, 50); 115 yesButton->setMinimumSize(100, 50); 116 cancelButton->setMinimumSize(100, 50); 117 cancelButton->setMaximumSize(100, 50); 118 btWidget->setMaximumHeight(70); 119 btWidget->setMinimumHeight(70); 120 alarmDialog->setMinimumSize(300, 300); 121 alarmDialog->setMaximumSize(300, 300); 122 alarmDialog->setModal(true); 123 yesButton->setObjectName("yesButton"); 124 cancelButton->setObjectName("cancelButton"); 125
126 /* 主布局 */ 127 vBoxLayout[0]->addWidget(listWidget); 128 vBoxLayout[0]->addWidget(bottomWidget); 129 vBoxLayout[0]->setContentsMargins(0, 0, 0, 0); 130 131 mainWidget->setLayout(vBoxLayout[0]); 132 133 setCentralWidget(mainWidget); 134 135 /* 底部按钮布局 */ 136 hBoxLayout[0]->addWidget(addAlarm); 137 hBoxLayout[0]->setContentsMargins(0, 0, 0, 0); 138 bottomWidget->setLayout(hBoxLayout[0]); 139 140 /* 对话框布局 */ 141 vBoxLayout[1]->addWidget(timeWidget); 142 vBoxLayout[1]->addWidget(btWidget); 143 vBoxLayout[1]->setContentsMargins(0, 0, 0, 0); 144 alarmDialog->setLayout(vBoxLayout[1]); 145 146 hBoxLayout[1]->addWidget(label[0]); 147 hBoxLayout[1]->addWidget(hourPicker); 148 hBoxLayout[1]->addWidget(label[1]); 149 hBoxLayout[1]->addWidget(minutePicker); 150 hBoxLayout[1]->addWidget(label[2]); 151 hBoxLayout[1]->setContentsMargins(0, 0, 0, 0); 152 timeWidget->setLayout(hBoxLayout[1]); 153 154 hBoxLayout[2]->addWidget(yesButton); 155 hBoxLayout[2]->addWidget(cancelButton); 156 157 btWidget->setLayout(hBoxLayout[2]); 158 159 /* 打印出闹钟数据库里的信息 */ 160 for (int i = 0; i < model->rowCount(); i++) { 161 for (int j = 0; j < 3; j++) { 162 QModelIndex qindex = model->index(i, j); 163 switch (j) { 164 case 0: 165 qDebug()<<"第"<<model->data(qindex).toInt()<<"行数据"; 166 break; 167 case 1: 168 listWidget->addItem(model->data(qindex).toString()); 169 qDebug()<<"闹钟时间为:"<<model->data(qindex).toString(); 170 break; 171 case 2: 172 qDebug()<<"闹钟状态为:" 173 <<model->data(qindex).toString()<<endl; 174 if (model->data(qindex).toString() != "true") 175 listWidget->item(i) 176 ->setTextColor(QColor(22, 22, 22, 60)); 177 else 178 listWidget->item(i) 179 ->setTextColor(QColor(22, 22, 22, 225)); 180 break; 181 default: 182 break; 183 } 184 } 185 } 186 187 /* 在列表里添加闹钟开关 */ 188 for (int i = 0; i < model->rowCount(); i++) { 189 ItemObjectInfo info; 190 info.widget = new QWidget(); 191 info.switchButton = new SwitchButton(); 192 info.hBoxLayout = new QHBoxLayout(); 193 info.switchButton->setMaximumSize(55, 30); 194 info.switchButton->setMinimumSize(55, 30); 195 info.hBoxLayout->setContentsMargins(0, 0, 0, 0); 196 info.hBoxLayout->setAlignment(Qt::AlignRight); 197 info.hBoxLayout->addWidget(info.switchButton); 198 info.widget->setLayout(info.hBoxLayout); 199 listWidget->setItemWidget(listWidget->item(i), 200 info.widget); 201 itemObjectInfo.append(info); 202 203 /* 连接信号槽 */ 204 connect(info.switchButton, 205 SIGNAL(toggled(bool)), 206 this, 207 SLOT(switchButtonClicked(bool))); 208 209 /* 获取数据库里的闹钟开关状态 */ 210 QModelIndex qindex = model->index(i, 2); 211 if (model->data(qindex).toBool()) 212 /* 设置列表里的闹钟开关按钮状态 */ 213 info.switchButton->setToggle(true); 214 } 215 216 /* 按钮 */ 217 connect(addAlarm, SIGNAL(clicked()), this, 218 SLOT(addAlarmClicked())); 219 220 connect(yesButton, SIGNAL(clicked()), this, 221 SLOT(yesButtonClicked())); 222 223 connect(cancelButton, SIGNAL(clicked()), this, 224 SLOT(cancelButtonClicked())); 225 226 /* 列表 */ 227 connect(listWidget, 228 SIGNAL(itemClicked(QListWidgetItem*)), 229 this, 230 SLOT(listWidgetItemClicked(QListWidgetItem*))); 231 } 232 233 MainWindow::~MainWindow() 234 { 235 /* 关闭数据库 */ 236 sqlDatabase.close(); 237 } 238 239 void MainWindow::addAlarmClicked() 240 { 241 /* 选择时间对话框里显示当前系统时间 */ 242 hourPicker->setValue(QTime::currentTime().hour()); 243 minutePicker->setValue(QTime::currentTime().minute()); 244 245 /* 取消按钮显示文本为"取消" */ 246 cancelButton->setText("取消"); 247 248 /* 如果是点击添加闹钟的按钮,则设置闹钟列表的索引 index 为-1 */ 249 listWidget->setCurrentRow(-1); 250 251 /* 显示对话框 */ 252 alarmDialog->show(); 253 } 254 255 void MainWindow::listWidgetItemClicked(QListWidgetItem *item) 256 { 257 /* 从被点击项里获取闹钟数据 */ 258 QStringList list = 259
listWidget->item(listWidget->row(item))->text().split(":"); 260 261 /* 选择时间对话框里显示被选择项的时间 */ 262 hourPicker->setValue(list.at(0).toInt()); 263 minutePicker->setValue(list.at(1).toInt()); 264 265 /* 取消按钮显示文本为"删除" */ 266 cancelButton->setText("删除"); 267 268 /* 显示闹钟选择对话框 */ 269 alarmDialog->show(); 270 271 /* 作用使其失去选择 */ 272 listWidget->clearSelection(); 273 } 274 275 void MainWindow::yesButtonClicked() 276 { 277 /* 获取数值选择值的数据,转为字符串 */ 278 QString hour; 279 QString minute; 280 281 if (hourPicker->readValue() < 10) 282 hour = "0" + QString::number(hourPicker->readValue()) + ":"; 283 else 284 hour = QString::number(hourPicker->readValue()) + ":"; 285 286 if (minutePicker->readValue() < 10) 287 minute = "0" + QString::number(minutePicker->readValue()); 288 else 289 minute = QString::number(minutePicker->readValue()); 290 291 /* 如果不是选中闹钟列表的数据 */ 292 if (listWidget->currentRow() == -1) { 293 /* 插入一行数据,闹钟时间为选择的闹钟时间 */ 294 int row = model->rowCount(); 295 296 /* 插入数据到数据库 */ 297 model->insertRow(row); 298 model->setData(model->index(row, 0), row + 1); 299 model->setData(model->index(row, 1), hour + minute); 300 model->setData(model->index(row, 2), "true"); 301 model->submit(); 302 303 /* 添加闹钟到列表 */ 304 listWidget->addItem(hour + minute); 305 306 /* 添加到容器 */ 307 ItemObjectInfo info; 308 info.widget = new QWidget(); 309 info.switchButton = new SwitchButton(); 310 info.hBoxLayout = new QHBoxLayout(); 311 info.switchButton->setMaximumSize(55, 30); 312 info.switchButton->setMinimumSize(55, 30); 313 info.hBoxLayout->setContentsMargins(0, 0, 0, 0); 314 info.hBoxLayout->setAlignment(Qt::AlignRight); 315 info.hBoxLayout->addWidget(info.switchButton); 316 info.widget->setLayout(info.hBoxLayout); 317 info.switchButton->setToggle(true); 318 319 /* 连接信号槽 */ 320 connect(info.switchButton, SIGNAL(toggled(bool)), this, 321 SLOT(switchButtonClicked(bool))); 322 323 listWidget->setItemWidget( 324 listWidget->item(listWidget->count() - 1), 325 info.widget); 326 itemObjectInfo.append(info); 327 } else { 328 /* 修改数据(更新闹钟数据) */ 329 int row = listWidget->currentRow(); 330 model->setData(model->index(row, 0), row + 1); 331 model->setData(model->index(row, 1), hour + minute); 332 model->setData(model->index(row, 2), "true"); 333 model->submit(); 334 335 /* 设置当前项的闹钟文本 */ 336 listWidget->currentItem()->setText(hour + minute); 337 } 338 339 /* 再确保提交 */ 340 if (model->isDirty()) 341 model->submitAll(); 342 343 /* 关闭对话框 */ 344 alarmDialog->close(); 345 } 346 347 void MainWindow::cancelButtonClicked() 348 { 349 if (cancelButton->text() == "删除") { 350 /* 删除数据库整一行数据 */ 351 model->removeRow(listWidget->currentRow()); 352 model->submit(); 353 /* 执行上面语句 */ 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 < itemObjectInfo.count(); i++) { 375 if (button == itemObjectInfo.at(i).switchButton) { 376 if (checked) { 377 model->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 <QApplication> 4 #include <QFile> 5 6 int main(int argc, char *argv[]) 7 { 8 QApplication a(argc, argv); 9 /* 指定文件 */ 10 QFile file(":/style.qss"); 11 12 /* 判断文件是否存在 */ 13 if (file.exists() ) { 14 /* 以只读的方式打开 */ 15 file.open(QFile::ReadOnly); 16 /* 以字符串的方式保存读出的结果 */ 17 QString styleSheet = QLatin1String(file.readAll()); 18 /* 设置全局样式 */ 19 qApp->setStyleSheet(styleSheet); 20 /* 关闭文件 */ 21 file.close(); 22 } 23 24 MainWindow w; 25 w.show(); 26 return a.exec(); 27 }
style.qss 样式文件如下。素材已经在源码处提供。注意下面的 style.qss 不能有注释!
1 QListWidget { 2 font-size: 30px; 3 outline:none; 4 } 5 6 QListWidget::item:active { 7 background: transparent; 8 } 9 10 QListWidget::item { 11 height:80; 12 } 13 14 QListWidget::item:selected:hover { 15 background:#22222222; 16 } 17 18 QListWidget::item:selected { 19 background:transparent; 20 color:#ee222222; 21 } 22 23 QPushButton#addAlarm { 24 border-image:url(:/icons/addalarm1.png); 25 background:transparent; 26 outline: none; 27 } 28 29 QPushButton#addAlarm:hover { 30 border-image:url(:/icons/addalarm2.png); 31 } 32 33 QPushButton#yesButton { 34 border: 1px solid #22222222; 35 border-radius: 25px; 36 background:#22222222; 37 outline:none; 38 } 39 40 QPushButton#yesButton:pressed { 41 background:#44222222; 42 color:white; 43 } 44 45 QPushButton#cancelButton { 46 border: 1px solid #22222222; 47 border-radius: 25px; 48 background:#22222222; 49 outline:none; 50 } 51 52 QPushButton#cancelButton:pressed { 53 background:#44222222; 54 color:white; 55 } 56 57 QScrollBar:vertical { 58 width:30px; 59 background:rgba(255, 255, 255, 100%) 60 } 61 62 QScrollBar::handle:vertical { 63 width:30px; 64 background:rgba(200, 200, 200, 20%); 65 border-radius:15px; 66 } 67 68 QScrollBar::add-line:vertical { 69 width:0px; height:0px; 70 } 71 QScrollBar::sub-line:vertical { 72 width:0px; 73 height:0px; 74 } 75 QScrollBar::handle:vertical:hover { 76 width:30px; 77 background:rgba(200, 200, 200, 80%); 78 border-radius:15px; 79 } 80 QScrollBar::add-page:vertical,QScrollBar::sub-page:vertical { 81 background:rgba(255, 255, 255, 100%) 82 }
其中数字选择器与闹钟开关按钮的代码,代码笔者参考了一些博客优化后写成的代码。有
兴趣可以细读代码,不作为本章注释讲解的代码。
数字选择器的作用是选择闹钟的时间,效果如下,通过上下滑动可以选择数字作为时钟的
时针和分针数据。通过点击对话框确认后存储到数据库里。
数字选择器的头文件“numberpicker.h”代码如下。
/****************************************************************** 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 <QMainWindow> 5 #include <QPropertyAnimation> 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 <QMouseEvent> 2 #include <QDebug> 3 #include "numberpicker.h" 4 #include <QPainter> 5 6 NumberPicker::NumberPicker(QWidget *parent) : 7 /* 最小值默认为 0 */ 8 minRange(0), 9 10 /* 最大值默认 60 */ 11 maxRange(60), 12 13 /* 当前值默认 0 */ 14 currentValue(0), 15 16 /* 按下标志位为假 */ 17 isDragging(false), 18 19 /* 默认偏移量为 0 */ 20 deviation(0), 21 22 /* 数值越大 */ 23 numSize(15), 24 25 /* 间隔为 1 */ 26 interval(1), 27 28 /* 默认分成 3 格 */ 29 devide(3), 30 31 /* 默认颜色黑色 */ 32 numberColor(0, 0, 0) 33 { 34 setParent(parent); 35 setMinimumSize(50, 150); 36 homingAni = new QPropertyAnimation(this, "deviation"); 37 homingAni->setDuration(300); 38 homingAni->setEasingCurve(QEasingCurve::OutQuad); 39 } 40 41 NumberPicker::~NumberPicker() 42 { 43 44 } 45 46 void NumberPicker::setRange(int min, int max) 47 { 48 minRange = min; 49 maxRange = max; 50 if (currentValue < min) { 51 currentValue = min; 52 } 53 if (currentValue > max) { 54 currentValue = max; 55 }
56 repaint(); 57 } 58 59 int NumberPicker::readValue() 60 { 61 return currentValue; 62 } 63 64 void NumberPicker::mousePressEvent(QMouseEvent *e) 65 { 66 homingAni->stop(); 67 isDragging = true; 68 mouseSrcPos = e->pos().y(); 69 QWidget::mousePressEvent(e); 70 } 71 72 void NumberPicker::mouseMoveEvent(QMouseEvent *e) 73 { 74 if (isDragging){ 75 deviation = e->pos().y() - mouseSrcPos; 76 77 /* 若移动速度过快,则进行限制 */ 78 if (deviation > (height() - 1) / devide) { 79 deviation = (height() - 1) / devide; 80 } else if (deviation < -(height() - 1) / devide) { 81 deviation = -( height() - 1) / devide; 82 } 83 84 emit deviationChange(deviation / ((height() - 1) / devide)); 85 repaint(); 86 } 87 } 88 89 void NumberPicker::mouseReleaseEvent(QMouseEvent *) 90 { 91 if (isDragging) { 92 isDragging = false; 93 homing(); 94 } 95 } 96 97 void NumberPicker::wheelEvent(QWheelEvent *e) 98 { 99 if (e->delta() > 0) { 100 deviation = (this->height() - 1) / devide; 101 } else { 102 deviation = -(this->height() - 1) / devide; 103 } 104 105 homing(); 106 repaint(); 107 } 108 109 void NumberPicker::paintEvent(QPaintEvent *) 110 { 111 QPainter painter(this); 112 painter.setRenderHint(QPainter::Antialiasing, true); 113 int Height = height() - 1; 114
115 if (deviation >= Height / devide && currentValue > minRange ) { 116 mouseSrcPos += Height / devide; 117 deviation -= Height / devide; 118 currentValue -= interval; 119 /* 负数处理 */ 120 if (currentValue < 0) 121 currentValue = maxRange + currentValue; 122 } 123 124 if (deviation <= -Height / devide && currentValue < maxRange ) { 125 mouseSrcPos -= Height / devide; 126 deviation += Height / devide; 127 currentValue += interval; 128 } 129 130 if (qAbs(int(currentValue)) >= int(maxRange)) 131 currentValue = minRange; 132 133 paintNum(painter, qAbs(int(currentValue + maxRange) % maxRange), 134 deviation); 135 136 paintNum(painter, 137 qAbs((currentValue - interval + maxRange) % maxRange), 138 deviation - Height / devide); 139 140 paintNum(painter, 141 qAbs((currentValue + interval + maxRange) % maxRange), 142 deviation + Height / devide); 143 144 for (int i = 2; i <= devide / 2; ++i) { 145 if (qAbs(currentValue - interval * i) >= minRange) { 146 paintNum(painter, 147 qAbs((currentValue - interval * i + maxRange) 148 % maxRange), 149 deviation - Height / devide * i); 150 } 151 152 if (qAbs(currentValue + interval * i) <= maxRange) { 153 paintNum(painter, 154 qAbs((currentValue + interval * i + maxRange) 155 % maxRange), 156 deviation + Height / devide * i); 157 } 158 } 159 } 160 161 void NumberPicker::paintNum(QPainter &painter, int num, int deviation) 162 { 163 int Width = width() - 1; 164 int Height = height() - 1; 165 166 /* 偏移量越大,数字越小 */ 167 //int size = (Height - qAbs(deviation)) / numSize; 168 int size = (Height - qAbs(deviation)) * numSize / 80; 169 int transparency = 255 - 255 * qAbs(deviation) / Height; 170 int height = Height / devide; 171 int y = Height / 2 + deviation - height / 2; 172 173 QFont font; 174 font.setPixelSize(size); 175 painter.setFont(font); 176 painter.setPen(QColor(numberColor.red(), 177 numberColor.green(), 178 numberColor.blue(), 179 transparency)); 180 181 if ( y >= 0 && y + height < Height) { 182 //painter.drawRect(0, y, Width, height); 183 if (num < 10) 184 painter.drawText(QRectF(0, y, Width, height), 185 Qt::AlignCenter, 186 "0" + QString::number(num, 'f', 0)); 187 else 188 painter.drawText(QRectF(0, y, Width, height), 189 Qt::AlignCenter, 190 QString::number(num, 'f', 0)); 191 } 192 } 193 194 void NumberPicker::homing() 195 { 196 if (deviation > height() / 10) { 197 homingAni->setStartValue((height() - 1 ) / 8 - deviation); 198 homingAni->setEndValue(0); 199 currentValue -= interval; 200 } else if (deviation > -height() / 10) { 201 homingAni->setStartValue(deviation); 202 homingAni->setEndValue(0); 203 } else if (deviation < -height() / 10) { 204 homingAni->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 < minRange || value > maxRange) { 251 qDebug()<<"数值设置必须在"<<minRange 252 <<"和"<<maxRange<<"之间"<<endl; 253 return; 254 } 255 currentValue = value; 256 repaint(); 257 }
开关按钮的效果如下。运行时有动画效果,类似 IOS 手册里的开关按钮一样。
开关按钮的头文件“switchbutton.h”代码如下。
/****************************************************************** Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved. * @projectName 17_sqlite_example * @brief switchbutton.h * @author Deng Zhimao * @email 1252699831@qq.com * @net www.openedv.com * @date 2021-05-14 *******************************************************************/ 1 #ifndef SWITCHBUTTON_H 2 #define SWITCHBUTTON_H 3 4 #include <QWidget> 5 #include <QTimer> 6 7 class SwitchButton : public QWidget 8 {
9 Q_OBJECT 10 11 public: 12 explicit SwitchButton(QWidget *parent = nullptr); 13 14 /* 返回开关状态 - 打开:true 关闭:false */ 15 bool isToggled() const; 16 17 /* 设置开关状态 */ 18 void setToggle(bool checked); 19 20 /* 设置背景颜色 */ 21 void setBackgroundColor(QColor color); 22 23 /* 设置选中颜色 */ 24 void setCheckedColor(QColor color); 25 26 /* 设置不可用颜色 */ 27 void setDisbaledColor(QColor color); 28 29 protected: 30 /* 绘制开关 */ 31 void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; 32 33 /* 鼠标按下事件 */ 34 void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; 35 36 /* 鼠标释放事件 - 切换开关状态、发射 toggled()信号 */ 37 void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; 38 39 /* 大小改变事件 */ 40 void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE; 41 42 /* 缺省大小 */ 43 QSize sizeHint() const Q_DECL_OVERRIDE; 44 QSize minimumSizeHint() const Q_DECL_OVERRIDE; 45 46 signals: 47 /* 状态改变时,发射信号 */ 48 void toggled(bool checked); 49 50 private slots: 51 /* 状态切换时,用于产生滑动效果 */ 52 void onTimeout(); 53 54 private: 55 /* 是否选中 */ 56 bool m_bChecked; 57 58 /* 背景颜色 */ 59 QColor m_background; 60 61 /* 选中颜色 */ 62 QColor m_checkedColor; 63 64 /* 不可用颜色 */ 65 QColor m_disabledColor; 66 67 /* 拇指颜色 */ 68 QColor m_thumbColor; 69 70 /* 圆角 */ 71 qreal m_radius; 72 73 /* x 点坐标 */ 74 qreal m_nX; 75 76 /* y 点坐标 */ 77 qreal m_nY; 78 79 /* 高度 */ 80 qint16 m_nHeight; 81 82 /* 外边距 */ 83 qint16 m_nMargin; 84 85 /* 定时器 */ 86 QTimer m_timer; 87 }; 88 #endif // SWITCHBUTTON_H
开关按钮的源文件“switchbutton.cpp”代码如下。
/****************************************************************** Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName 17_sqlite_example * @brief switchbutton.cpp * @author Deng Zhimao * @email 1252699831@qq.com * @net www.openedv.com * @date 2021-05-14 *******************************************************************/ 1 #include "switchbutton.h" 2 3 #include <QPainter> 4 #include <QMouseEvent> 5 6 SwitchButton::SwitchButton(QWidget *parent) 7 : QWidget(parent), 8 m_bChecked(false), 9 m_background(Qt::gray), 10 m_checkedColor(34, 131, 246), 11 m_disabledColor(190, 190, 190), 12 m_thumbColor(Qt::gray), 13 m_radius(12.5), 14 m_nHeight(16), 15 m_nMargin(3) 16 { 17 /* 鼠标滑过光标形状 - 手型 */ 18 setCursor(Qt::PointingHandCursor); 19 20 /* 连接信号槽 */ 21 connect(&m_timer, SIGNAL(timeout()), 22 this, SLOT(onTimeout())); 23 } 24 25 /* 绘制开关 */ 26 void SwitchButton::paintEvent(QPaintEvent *event) 27 { 28 Q_UNUSED(event) 29 30 QPainter painter(this); 31 painter.setPen(Qt::NoPen); 32 painter.setRenderHint(QPainter::Antialiasing); 33 34 QPainterPath path; 35 QColor background; 36 QColor thumbColor; 37 qreal dOpacity; 38 /* 可用状态 */ 39 if (isEnabled()) { 40 /* 打开状态 */ 41 if (m_bChecked) { 42 background = m_checkedColor; 43 thumbColor = m_checkedColor; 44 dOpacity = 0.600; 45 /* 关闭状态 */ 46 } else { 47 background = m_background; 48 thumbColor = m_thumbColor; 49 dOpacity = 0.800; 50 } 51 /* 不可用状态 */ 52 } else { 53 background = m_background; 54 dOpacity = 0.260; 55 thumbColor = m_disabledColor; 56 } 57 /* 绘制大椭圆 */ 58 painter.setBrush(background); 59 painter.setOpacity(dOpacity); 60 path.addRoundedRect(QRectF(m_nMargin, 61 m_nMargin, width() - 2 * m_nMargin, 62 height() - 2 * m_nMargin), 63 m_radius, m_radius); 64 painter.drawPath(path.simplified()); 65 66 /* 绘制小椭圆 */ 67 painter.setBrush(thumbColor); 68 painter.setOpacity(1.0); 69 painter.drawEllipse(QRectF(m_nX - (m_nHeight / 2), 70 m_nY - (m_nHeight / 2), 71 height(), 72 height())); 73 } 74 75 /* 鼠标按下事件 */ 76 void SwitchButton::mousePressEvent(QMouseEvent *event) 77 { 78 if (isEnabled()) { 79 if (event->buttons() & Qt::LeftButton) { 80 event->accept(); 81 } else { 82 event->ignore(); 83 } 84 } 85 } 86 87 /* 鼠标释放事件 - 切换开关状态、发射 toggled()信号 */ 88 void SwitchButton::mouseReleaseEvent(QMouseEvent *event) 89 { 90 if (isEnabled()) { 91 if ((event->type() == QMouseEvent::MouseButtonRelease) 92 && (event->button() == Qt::LeftButton)) { 93 event->accept(); 94 m_bChecked = !m_bChecked; 95 emit toggled(m_bChecked); 96 m_timer.start(10); 97 } else { 98 event->ignore(); 99 } 100 } 101 } 102 103 /* 大小改变事件 */ 104 void SwitchButton::resizeEvent(QResizeEvent *event) 105 { 106 m_nX = m_nHeight / 2; 107 m_nY = m_nHeight / 2; 108 QWidget::resizeEvent(event); 109 } 110 111 /* 默认大小 */ 112 QSize SwitchButton::sizeHint() const 113 { 114 return minimumSizeHint(); 115 } 116 117 /* 最小大小 */ 118 QSize SwitchButton::minimumSizeHint() const 119 { 120 return QSize(2 * (m_nHeight + m_nMargin), 121 m_nHeight + 2 * m_nMargin); 122 } 123 124 /* 切换状态 - 滑动 */125 void SwitchButton::onTimeout() 126 { 127 if (m_bChecked) { 128 m_nX += 1; 129 if (m_nX >= width() - m_nHeight - m_nHeight / 2 ) { 130 m_timer.stop(); 131 m_nX -= 1; 132 } 133 } else { 134 m_nX -= 1; 135 if (m_nX <= m_nHeight / 2) { 136 m_timer.stop(); 137 m_nX += 1; 138 } 139 } 140 update(); 141 } 142 143 /* 返回开关状态 - 打开:true 关闭:false */ 144 bool SwitchButton::isToggled() const 145 { 146 return m_bChecked; 147 } 148 149 /* 设置开关状态 */ 150 void SwitchButton::setToggle(bool checked) 151 { 152 m_bChecked = checked; 153 m_timer.start(10); 154 } 155 156 /* 设置背景颜色 */ 157 void SwitchButton::setBackgroundColor(QColor color) 158 { 159 m_background = color; 160 } 161 162 /* 设置选中颜色 */ 163 void SwitchButton::setCheckedColor(QColor color) 164 { 165 m_checkedColor = color; 166 } 167 168 /* 设置不可用颜色 */
169 void SwitchButton::setDisbaledColor(QColor color) 170 { 171 m_disabledColor = color; 172 }
程序运行效果
点击程序正下方的“+”按钮则开始添加闹钟数据,根据当前系统的时间或者滑动选择闹
钟的时间,点击确认即新增一条闹钟数据。
修改或者删除数据,点击要修改闹钟的数据项目,弹出的对话框,可以重新设置闹钟或者
删除闹钟。并保存记录到数据库里,下次运行打开时会从数据库里读取保存的闹钟数据。
相关文章:

QT学习开发笔记(数据库之实用时钟)
数据库 数据库是什么?简易言之,就是保存数据的文件。可以存储大量数据,包括插入数据、更 新数据、截取数据等。用专业术语来说,数据库是“按照数据结构来组织、存储和管理数据的 仓库”。是一个长期存储在计算机内的、有组织的、…...

Docker常规安装简介
总体步骤 搜索镜像拉取镜像查看镜像启动镜像,服务端口映射停止容器移除容器 案例 安装tomcat docker hub上面查找tomcat镜像,docker search tomcat从docker hub上拉取tomcat镜像到本地 docker pull tomcatdocker images查看是否有拉取到的tomcat 使用tomcat镜像创…...

Python - PyQT5 - ui文件转为py文件
在QTdesigner图形化编辑工具中,有些控件我们是可以直接在编辑界面进行编辑的,有些是不可以编辑的,只能通过Python代码进行编辑,不过总体来说,所有能够通过图形化编辑界面可以编辑的,都可以通过Python语言实…...
分布式事务和分布式锁
1、关于分布式锁的了解? 原理:控制分布式系统有序的去对共享资源进行操作,通过互斥来保持一致性。 具备的条件: ①分布式环境下,一个方法在同一时间只能被一个机器的一个线程执行 ②高可用的获取锁和释放锁 ③高性能…...

JAVA-4-[Spring框架]基于XML方式的Bean管理
1 Spring IOC容器 (1)IOC底层原理 (2)IOC接口(BeanFactory) (3)IOC操作Bean管理(基于XML) (4)IOC操作Bean管理(基于注释) 1.1 IOC概念 (1)控制反转(Inversion of Control),把对象的创建和对象之间的调用过程,交给Spring进行管理。 (2)使用IOC目的&…...

路科验证UVM入门与进阶详解实验0
一.代码编译 首先创建新项目,导入lab0 的UVM文件; 针对uvm_compile文件,先进行编译; module uvm_compile;// NOTE:: it is necessary to import uvm package and macrosimport uvm_pkg::*;include "uvm_macros.svh"in…...

Linux之Shell编程(1)
文章目录前言一、Shell是什么二、Shell脚本的执行方式脚本的常用执行方式三、Shell的变量Shell变量介绍shell变量的定义四、设置环境变量基本语法快速入门五、位置参数变量介绍●基本语法●位置参数变量六、预定义变量基本介绍基本语法七、运算符基本介绍基本语法前言 为什么要…...

Java问题诊断工具——JVisualVM
这篇文章源自一次加班改bug的惨痛经历[,,_,,]:3负责的一个项目占用不断增加,差点搞崩服务器(╥﹏╥)……一下子有点懵,不能立刻确定是哪里导致的问题,所以决定好好研究下这个之前一直被我忽视的问题诊断工具🔧——JVisualVM嘿嘿我…...

Python3实现简单的车牌检测
导语Hi,好久不见~~~两周没写东西了,从简单的开始,慢慢提高文章水准吧,下一个月开始时间就会比较充裕了~~~利用Python实现简单的车牌检测算法~~~让我们愉快地开始吧~~~相关文件网盘下载链接: https://pan.baidu.com/s/1iJmXCheJoWq…...

基于支持向量机SVM多因子测量误差预测,支持向量机MATLAB代码编程实现
目录 支持向量机SVM的详细原理 SVM的定义 SVM理论 SVM应用实例,SVM的测量误差预测 代码 结果分析 展望 支持向量机SVM的详细原理 SVM的定义 支持向量机(support vector machines, SVM)是一种二分类模型,它的基本模型是定义在特…...

新农具时代,拼多多的进击与本分
这几年,乡村振兴被频频提及,核心就是经济振兴。但经济振兴,不能只靠政府,更要靠企业,政府引导、企业主导才能真正让农民、农村、农业长期受益。企业中,被寄予厚望的是电商企业。甚至,电商成为了…...

质量工具之故障树分析FTA(2) - FTA的基本概念
关键词:问题解决、故障树、故障树分析、FTA、可靠性、鱼骨图、根本原因分析 前文我们已经详细介绍了FTA的历史。 我们在工作中碰到一个问题,可以利用的问题解决工具有很多,故障树分析FTA就是其中之一。 但是FTA毕竟是相对复杂较难掌握的工具…...
《高质量C/C++编程》读书笔记二
文章目录前言三、命名规则四、表达式和基本语句if语句循环语句五、常量前言 这本书是林锐博士写的关于C/C编程规范的一本书,我打算写下一系列读书笔记,当然我并不打算全盘接收这本书中的内容。 良好的编程习惯,规范的编程风格可以提高代码…...

常用的美颜滤镜sdk算法
本文主要介绍常见的美颜滤镜SDK算法,包括 SRGB、 HSL、 Lab、 JPEG、 TIFF等。本文不会过多介绍算法原理,只是列举一些在实际项目中用到的滤镜效果,如: 1.色彩空间变换 2.颜色范围调节 3.色彩平衡调节 4.灰度级调节 5.色相/饱和度…...

动态SQL必知必会
动态SQL必知必会1、什么是动态SQL2、为什么使用动态SQL3、动态SQL的标签4、if 标签-单标签判断5、choose标签-多条件分支判断6、set 标签-修改语句7、foreach标签7.1 批量查询7.2 批量删除7.3 批量添加8、模糊分页查询1、什么是动态SQL 动态 SQL 是 MyBatis 的强大特性之一。如…...

DML编程控制
id生成策略 模型类: Data TableName("tbl_user") public class User {TableId(type IdType.AUTO)TableId(type IdType.NONE)TableId(type IdType.INPUT)TableId(type IdType.ASSIGN_ID)TableId(type IdType.ASSIGN_UUID)private Long id;private String name;T…...
关于肺结节实时的目标检测
目录 1. 对屏幕固定区域的检测 1.1 代码 1.2 结果展示 2. video 检测 2.1 代码 2.2 展示...

利用 Rainbond 云原生平台简化 Kubernetes 业务问题排查
Kubernetes 已经成为了云原生时代基础设施的事实标准,越来越多的应用系统在 Kubernetes 环境中运行。Kubernetes 已经依靠其强大的自动化运维能力解决了业务系统的大多数运行维护问题,然而还是要有一些状况是需要运维人员去手动处理的。那么和传统运维相…...
C++中的future和promise使用方法
future和promise C11中std::future提供了一种访问异步操作结果的机制。异步操作不能马上就获取操作结果,只能在未来某个时候获取,但可以以同步等待的方式来获取结果,可以通过查询future的状态(future_status)来获取异…...

Vue项目创建
一.Axios简介 1、Axios是什么? Axios是一个基于promise的HTTP库,类似于jQuery的ajax,用于http请求。可以应用于浏览器端和node.js,既可以用于客户端,也可以用于node.js编写的服务端 安装使用 1.下载axios npm inst…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...

如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...
MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释
以Module Federation 插件详为例,Webpack.config.js它可能的配置和含义如下: 前言 Module Federation 的Webpack.config.js核心配置包括: name filename(定义应用标识) remotes(引用远程模块࿰…...
书籍“之“字形打印矩阵(8)0609
题目 给定一个矩阵matrix,按照"之"字形的方式打印这个矩阵,例如: 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为:1,…...

算法—栈系列
一:删除字符串中的所有相邻重复项 class Solution { public:string removeDuplicates(string s) {stack<char> st;for(int i 0; i < s.size(); i){char target s[i];if(!st.empty() && target st.top())st.pop();elsest.push(s[i]);}string ret…...