数据库就是保存数据的文件。可以存储大量数据,包括插入数据、更新数据、截取数据等。用专业术语来说,数据库是“按照数据结构来组织、存储和管理数据的仓库”。
什么时候需要数据库?在嵌入式里,存储大量数据,或者记录数据,就需要用到数据库。比如手机的闹钟就使用到了数据库,我们设置的闹钟数据将会保存到数据库里,闹钟程序运行时会从数据库里读取出上次保存的闹钟数据。如果没有数据库,则闹钟程序关机了数据不保存在物理储存设备里,下次运行闹钟时就没有上次设置的闹钟数据,这显然是不合理的。所以我们需要用到数据库。想要在项目中使用Qt SQL模块,需要在项目配置文件里添加QT += core gui sql
Qt SQL模块为数据库提供了编程支持,Qt支持很多种常见的数据库,如MySQL、Oracle、MS SQL Server、SQLite等。Qt SQL模块里包含了很多个类,可以轻松实现数据库的连接、执行SQL语句,获取数据库里的数据与界面显示等功能,一般数据与界面之间会采用Model/View架构,很方便的显示数据界面和操作数据库。在嵌入式里,一般常用的数据库就是Sqlite3。SQLite是非常小的,是轻量级的,完全配置时小于400KB,省略可选功能配置时小于250KB。SQLite是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的SQL数据库引擎。它是一个零配置的数据库,这意味着与其他数据库不一样,您不需要在系统中配置。就像其他数据库SQLite引擎不是一个独立的进程,可以按应用程序需求进行静态或动态连接。SQLite可以直接访问其存储文件。
pro文件
QT += core gui sqlgreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += c++11# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0SOURCES += \main.cpp \mainwindow.cpp \numberpicker.cpp \switchbutton.cppHEADERS += \mainwindow.h \numberpicker.h \switchbutton.h# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += targetRESOURCES += \res.qrc
main.cpp
#include "mainwindow.h"
#include <QApplication>
#include <QFile>
int main(int argc, char *argv[])
{QApplication a(argc, argv);/* 指定文件 */QFile file(":/style.qss");/* 判断文件是否存在 */if (file.exists() ) {/* 以只读的方式打开 */file.open(QFile::ReadOnly);/* 以字符串的方式保存读出的结果 */QString styleSheet = QLatin1String(file.readAll());/* 设置全局样式 */qApp->setStyleSheet(styleSheet);/* 关闭文件 */file.close();}MainWindow w;w.show();return a.exec();
}
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QSqlDatabase>
#include <QSqlQuery>
#include <QMainWindow>
#include <QDialog>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPushButton>
#include <QListWidget>
#include <QLabel>
#include <QTime>
#include <QSqlTableModel>
#include "numberpicker.h"
#include "switchbutton.h"class NumberPicker;
class SwitchButton;/* ListWiget项结构体 */
struct ItemObjectInfo {/* 闹钟开关 */SwitchButton *switchButton;/* Widget容器 */QWidget *widget;/* 水平布局 */QHBoxLayout *hBoxLayout;
};class MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private:/* 数据库连接类 */QSqlDatabase sqlDatabase;/* 数据库操作模型 */QSqlTableModel *model;/* 时针选择器 */NumberPicker *hourPicker;/* 分钟选择器 */NumberPicker *minutePicker;/* 弹出选择时间对话框 */QDialog *alarmDialog;/* 水平布局 */QHBoxLayout *hBoxLayout[3];/* 垂直布局 */QVBoxLayout *vBoxLayout[2];/* 显示闹钟列表 */QListWidget *listWidget;/* 主Widget */QWidget *mainWidget;/* 底部Wiget */QWidget *bottomWidget;/* 弹出对话框布局窗口选择时间容器 */QWidget *timeWidget;/* 弹出对话框布局窗口按钮容器 */QWidget *btWidget;/* 添加闹钟按钮 */QPushButton *addAlarm;/* 确认按钮 */QPushButton *yesButton;/* 取消按钮 */QPushButton *cancelButton;/* listWiget项信息存储 */QVector<ItemObjectInfo> itemObjectInfo;private slots:/* 添加闹钟按钮被点击 */void addAlarmClicked();/* 列表被点击 */void listWidgetItemClicked(QListWidgetItem *);/* 确认按钮被点击 */void yesButtonClicked();/* 取消按钮被点击 */void cancelButtonClicked();/* 开关按钮点击 */void switchButtonClicked(bool);
};
#endif // MAINWINDOW_H
MainWindow.cpp
#include "mainwindow.h"
#include <QDebug>
#include <QSqlError>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{/* 设置主窗体的显示位置与大小 */this->setGeometry(0, 0, 800, 480);/* 查看本机可用的数据库驱动 */QStringList drivers = QSqlDatabase::drivers();foreach(QString driver, drivers) {qDebug()<<driver;}/* 以QSQLITE驱动方式打开或者创建数据库 */sqlDatabase = QSqlDatabase::addDatabase("QSQLITE");sqlDatabase.setDatabaseName("alarm.db");/* 以open的方式打开alarm.db数据库,则会创建一个alarm.db */if (!sqlDatabase.open())qDebug()<<"连接数据库错误"<<sqlDatabase.lastError()<<endl;elseqDebug()<<"连接数据库成功"<<endl;QSqlQuery query(sqlDatabase);/* 使用指令式创建表 */query.exec("create table alarm (id int primary key, time vchar(15), flag vchar(5))");/* 以指令的方式插入数据 *///query.exec("insert into alarm values(0, '06:00', 'false')");model = new QSqlTableModel(this, sqlDatabase);/* 模型设置表的名字,需要与数据库的表的名字相同 */model->setTable("alarm");/* 如果有修改则同步修改到数据库,* 注意这个规则需要与tabview这样的控件才生效,* 因为tabview可以直接编辑表里的内容 */model->setEditStrategy(QSqlTableModel::OnFieldChange);/* 成功则返回true,查看数据库里是否有alarm这个表格 */model->select();/* 如果数据表数据为空,则添加两个闹钟 */if (model->rowCount() == 0) {/* 插入一行 */model->insertRow(model->rowCount());/* 在该行插入数据 */model->setData(model->index(0, 0), 1);model->setData(model->index(0, 1), "06:00");model->setData(model->index(0, 2), "false");/* 插入数据后记得提交 */model->submit();/* 再插入一行 */model->insertRow(model->rowCount());model->setData(model->index(1, 0), 2);model->setData(model->index(1, 1), "18:00");model->setData(model->index(1, 2), "true");/* 提交 */model->submit();}hourPicker = new NumberPicker(this);hourPicker->setRange(0, 24);minutePicker = new NumberPicker(this);minutePicker->setRange(0, 60);/* 标签,用于显示时&分 */QLabel *label[3];label[0] = new QLabel();label[1] = new QLabel();label[2] = new QLabel();QFont font;font.setBold(true);font.setPixelSize(10);QPalette pal;pal.setBrush(QPalette::WindowText, QColor(0, 0, 0));label[0]->setFont(font);label[1]->setFont(font);label[2]->setFont(font);label[0]->setText(" ");label[1]->setText("时");label[2]->setText("分");/* 主布局初始化 */listWidget = new QListWidget();mainWidget = new QWidget();bottomWidget = new QWidget();alarmDialog = new QDialog(this);timeWidget = new QWidget();btWidget = new QWidget();addAlarm = new QPushButton();yesButton = new QPushButton();cancelButton = new QPushButton();vBoxLayout[0] = new QVBoxLayout();vBoxLayout[1] = new QVBoxLayout();hBoxLayout[0] = new QHBoxLayout();hBoxLayout[1] = new QHBoxLayout();hBoxLayout[2] = new QHBoxLayout();addAlarm->setMaximumSize(84, 84);addAlarm->setObjectName("addAlarm");addAlarm->setMinimumSize(84, 84);bottomWidget->setMinimumHeight(84);bottomWidget->setMaximumHeight(84);yesButton->setText("确认");cancelButton->setText("取消");yesButton->setMaximumSize(100, 50);yesButton->setMinimumSize(100, 50);cancelButton->setMinimumSize(100, 50);cancelButton->setMaximumSize(100, 50);btWidget->setMaximumHeight(70);btWidget->setMinimumHeight(70);alarmDialog->setMinimumSize(300, 300);alarmDialog->setMaximumSize(300, 300);alarmDialog->setModal(true);yesButton->setObjectName("yesButton");cancelButton->setObjectName("cancelButton");/* 主布局 */vBoxLayout[0]->addWidget(listWidget);vBoxLayout[0]->addWidget(bottomWidget);vBoxLayout[0]->setContentsMargins(0, 0, 0, 0);mainWidget->setLayout(vBoxLayout[0]);setCentralWidget(mainWidget);/* 底部按钮布局 */hBoxLayout[0]->addWidget(addAlarm);hBoxLayout[0]->setContentsMargins(0, 0, 0, 0);bottomWidget->setLayout(hBoxLayout[0]);/* 对话框布局 */vBoxLayout[1]->addWidget(timeWidget);vBoxLayout[1]->addWidget(btWidget);vBoxLayout[1]->setContentsMargins(0, 0, 0, 0);alarmDialog->setLayout(vBoxLayout[1]);hBoxLayout[1]->addWidget(label[0]);hBoxLayout[1]->addWidget(hourPicker);hBoxLayout[1]->addWidget(label[1]);hBoxLayout[1]->addWidget(minutePicker);hBoxLayout[1]->addWidget(label[2]);hBoxLayout[1]->setContentsMargins(0, 0, 0, 0);timeWidget->setLayout(hBoxLayout[1]);hBoxLayout[2]->addWidget(yesButton);hBoxLayout[2]->addWidget(cancelButton);btWidget->setLayout(hBoxLayout[2]);/* 打印出闹钟数据库里的信息 */for (int i = 0; i < model->rowCount(); i++) {for (int j = 0; j < 3; j++) {QModelIndex qindex = model->index(i, j);switch (j) {case 0:qDebug()<<"第"<<model->data(qindex).toInt()<<"行数据";break;case 1:listWidget->addItem(model->data(qindex).toString());qDebug()<<"闹钟时间为:"<<model->data(qindex).toString();break;case 2:qDebug()<<"闹钟状态为:"<<model->data(qindex).toString()<<endl;if (model->data(qindex).toString() != "true")listWidget->item(i)->setTextColor(QColor(22, 22, 22, 60));elselistWidget->item(i)->setTextColor(QColor(22, 22, 22, 225));break;default:break;}}}/* 在列表里添加闹钟开关 */for (int i = 0; i < model->rowCount(); i++) {ItemObjectInfo info;info.widget = new QWidget();info.switchButton = new SwitchButton();info.hBoxLayout = new QHBoxLayout();info.switchButton->setMaximumSize(55, 30);info.switchButton->setMinimumSize(55, 30);info.hBoxLayout->setContentsMargins(0, 0, 0, 0);info.hBoxLayout->setAlignment(Qt::AlignRight);info.hBoxLayout->addWidget(info.switchButton);info.widget->setLayout(info.hBoxLayout);listWidget->setItemWidget(listWidget->item(i),info.widget);itemObjectInfo.append(info);/* 连接信号槽 */connect(info.switchButton,SIGNAL(toggled(bool)),this,SLOT(switchButtonClicked(bool)));/* 获取数据库里的闹钟开关状态 */QModelIndex qindex = model->index(i, 2);if (model->data(qindex).toBool())/* 设置列表里的闹钟开关按钮状态 */info.switchButton->setToggle(true);}/* 按钮 */connect(addAlarm, SIGNAL(clicked()), this,SLOT(addAlarmClicked()));connect(yesButton, SIGNAL(clicked()), this,SLOT(yesButtonClicked()));connect(cancelButton, SIGNAL(clicked()), this,SLOT(cancelButtonClicked()));/* 列表 */connect(listWidget,SIGNAL(itemClicked(QListWidgetItem*)),this,SLOT(listWidgetItemClicked(QListWidgetItem*)));
}MainWindow::~MainWindow()
{/* 关闭数据库 */sqlDatabase.close();
}void MainWindow::addAlarmClicked()
{/* 选择时间对话框里显示当前系统时间 */hourPicker->setValue(QTime::currentTime().hour());minutePicker->setValue(QTime::currentTime().minute());/* 取消按钮显示文本为"取消" */cancelButton->setText("取消");/* 如果是点击添加闹钟的按钮,则设置闹钟列表的索引index为-1 */listWidget->setCurrentRow(-1);/* 显示对话框 */alarmDialog->show();
}void MainWindow::listWidgetItemClicked(QListWidgetItem *item)
{/* 从被点击项里获取闹钟数据 */QStringList list =listWidget->item(listWidget->row(item))->text().split(":");/* 选择时间对话框里显示被选择项的时间 */hourPicker->setValue(list.at(0).toInt());minutePicker->setValue(list.at(1).toInt());/* 取消按钮显示文本为"删除" */cancelButton->setText("删除");/* 显示闹钟选择对话框 */alarmDialog->show();/* 作用使其失去选择 */listWidget->clearSelection();
}void MainWindow::yesButtonClicked()
{/* 获取数值选择值的数据,转为字符串 */QString hour;QString minute;if (hourPicker->readValue() < 10)hour = "0" + QString::number(hourPicker->readValue()) + ":";elsehour = QString::number(hourPicker->readValue()) + ":";if (minutePicker->readValue() < 10)minute = "0" + QString::number(minutePicker->readValue());elseminute = QString::number(minutePicker->readValue());/* 如果不是选中闹钟列表的数据 */if (listWidget->currentRow() == -1) {/* 插入一行数据,闹钟时间为选择的闹钟时间 */int row = model->rowCount();/* 插入数据到数据库 */model->insertRow(row);model->setData(model->index(row, 0), row + 1);model->setData(model->index(row, 1), hour + minute);model->setData(model->index(row, 2), "true");model->submit();/* 添加闹钟到列表 */listWidget->addItem(hour + minute);/* 添加到容器 */ItemObjectInfo info;info.widget = new QWidget();info.switchButton = new SwitchButton();info.hBoxLayout = new QHBoxLayout();info.switchButton->setMaximumSize(55, 30);info.switchButton->setMinimumSize(55, 30);info.hBoxLayout->setContentsMargins(0, 0, 0, 0);info.hBoxLayout->setAlignment(Qt::AlignRight);info.hBoxLayout->addWidget(info.switchButton);info.widget->setLayout(info.hBoxLayout);info.switchButton->setToggle(true);/* 连接信号槽 */connect(info.switchButton, SIGNAL(toggled(bool)), this,SLOT(switchButtonClicked(bool)));listWidget->setItemWidget(listWidget->item(listWidget->count() - 1),info.widget);itemObjectInfo.append(info);} else {/* 修改数据(更新闹钟数据) */int row = listWidget->currentRow();model->setData(model->index(row, 0), row + 1);model->setData(model->index(row, 1), hour + minute);model->setData(model->index(row, 2), "true");model->submit();/* 设置当前项的闹钟文本 */listWidget->currentItem()->setText(hour + minute);}/* 再确保提交 */if (model->isDirty())model->submitAll();/* 关闭对话框 */alarmDialog->close();
}void MainWindow::cancelButtonClicked()
{if (cancelButton->text() == "删除") {/* 删除数据库整一行数据 */model->removeRow(listWidget->currentRow());model->submit();/* 执行上面语句 */model->select();itemObjectInfo.remove(listWidget->currentRow());listWidget->takeItem(listWidget->currentRow());}/* 再确保提交 */if (model->isDirty())model->submitAll();/* 关闭对话框 */alarmDialog->close();
}/* 当点击闹钟开关时,将闹钟开关状态同步更新到数据库里 */
void MainWindow::switchButtonClicked(bool checked)
{listWidget->clearSelection();SwitchButton *button = (SwitchButton *)sender();for (int i = 0; i < itemObjectInfo.count(); i++) {if (button == itemObjectInfo.at(i).switchButton) {if (checked) {model->setData(model->index(i, 2), "true");listWidget->item(i)->setTextColor(QColor(22, 22, 22, 225));} else {model->setData(model->index(i, 2), "false");listWidget->item(i)->setTextColor(QColor(22, 22, 22, 60));}model->submit();break;}}
}
numberpicker.h
#ifndef NUMBERPICKER_H
#define NUMBERPICKER_H#include <QMainWindow>
#include <QPropertyAnimation>class NumberPicker : public QWidget
{Q_OBJECTQ_PROPERTY(int deviation READ readDeviation WRITE setDeviation )
public:NumberPicker(QWidget *parent = nullptr);~NumberPicker();/* 设置最大值与最小值的范围 */void setRange(int min, int max);/* 读取当前值 */int readValue();protected:void mousePressEvent(QMouseEvent *);void mouseMoveEvent(QMouseEvent *);void mouseReleaseEvent(QMouseEvent *);void wheelEvent(QWheelEvent *);void paintEvent(QPaintEvent *);public:/* 描绘数字 */void paintNum(QPainter &painter, int num, int deviation);/* 使选中的数字回到屏幕中间 */void homing();/* 鼠标移动偏移量,默认为0 */int readDeviation();/* 设置偏移量 */void setDeviation(int n);/* 设置字体大小 */void setNumSize(int);/* 设置间隔大小 */void setInterval(int);/* 设置分格数量,一般设置为3、5、7... */void setDevide(int);/* 设置数字颜色,设置rgb的数值 */void setNumberColor(QRgb rgb);/* 设置当前值 */void setValue(int value);signals:void currentValueChanged(int value);void deviationChange(int deviation);private:/* 最小值 */int minRange;/* 最大值 */int maxRange;/* 当前选中的值 */int currentValue;/* 鼠标是否按下 */bool isDragging;/* 偏移量,记录鼠标按下后移动的垂直距离 */int deviation;/* 鼠标按下的垂直位置 */int mouseSrcPos;/* 数字大小 */int numSize;/* 动画 */QPropertyAnimation *homingAni;/* 间隔大小 */int interval;/* 分格数量 */int devide;/* 数字颜色 */QColor numberColor;
};
#endif // NUMBERPICKER_H
numberpicker.cpp
#include <QMouseEvent>
#include <QDebug>
#include "numberpicker.h"
#include <QPainter>NumberPicker::NumberPicker(QWidget *parent) :/* 最小值默认为0 */minRange(0),/* 最大值默认60 */maxRange(60),/* 当前值默认0 */currentValue(0),/* 按下标志位为假 */isDragging(false),/* 默认偏移量为0 */deviation(0),/* 数值越大 */numSize(15),/* 间隔为1 */interval(1),/* 默认分成3格 */devide(3),/* 默认颜色黑色 */numberColor(0, 0, 0)
{setParent(parent);setMinimumSize(50, 150);homingAni = new QPropertyAnimation(this, "deviation");homingAni->setDuration(300);homingAni->setEasingCurve(QEasingCurve::OutQuad);
}NumberPicker::~NumberPicker()
{}void NumberPicker::setRange(int min, int max)
{minRange = min;maxRange = max;if (currentValue < min) {currentValue = min;}if (currentValue > max) {currentValue = max;}repaint();
}int NumberPicker::readValue()
{return currentValue;
}void NumberPicker::mousePressEvent(QMouseEvent *e)
{homingAni->stop();isDragging = true;mouseSrcPos = e->pos().y();QWidget::mousePressEvent(e);
}void NumberPicker::mouseMoveEvent(QMouseEvent *e)
{if (isDragging){deviation = e->pos().y() - mouseSrcPos;/* 若移动速度过快,则进行限制 */if (deviation > (height() - 1) / devide) {deviation = (height() - 1) / devide;} else if (deviation < -(height() - 1) / devide) {deviation = -( height() - 1) / devide;}emit deviationChange(deviation / ((height() - 1) / devide));repaint();}
}void NumberPicker::mouseReleaseEvent(QMouseEvent *)
{if (isDragging) {isDragging = false;homing();}
}void NumberPicker::wheelEvent(QWheelEvent *e)
{if (e->delta() > 0) {deviation = (this->height() - 1) / devide;} else {deviation = -(this->height() - 1) / devide;}homing();repaint();
}void NumberPicker::paintEvent(QPaintEvent *)
{QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing, true);int Height = height() - 1;if (deviation >= Height / devide && currentValue > minRange ) {mouseSrcPos += Height / devide;deviation -= Height / devide;currentValue -= interval;/* 负数处理 */if (currentValue < 0)currentValue = maxRange + currentValue;}if (deviation <= -Height / devide && currentValue < maxRange ) {mouseSrcPos -= Height / devide;deviation += Height / devide;currentValue += interval;}if (qAbs(int(currentValue)) >= int(maxRange))currentValue = minRange;paintNum(painter, qAbs(int(currentValue + maxRange) % maxRange),deviation);paintNum(painter,qAbs((currentValue - interval + maxRange) % maxRange),deviation - Height / devide);paintNum(painter,qAbs((currentValue + interval + maxRange) % maxRange),deviation + Height / devide);for (int i = 2; i <= devide / 2; ++i) {if (qAbs(currentValue - interval * i) >= minRange) {paintNum(painter,qAbs((currentValue - interval * i + maxRange)% maxRange),deviation - Height / devide * i);}if (qAbs(currentValue + interval * i) <= maxRange) {paintNum(painter,qAbs((currentValue + interval * i + maxRange)% maxRange),deviation + Height / devide * i);}}
}void NumberPicker::paintNum(QPainter &painter, int num, int deviation)
{int Width = width() - 1;int Height = height() - 1;/* 偏移量越大,数字越小 *///int size = (Height - qAbs(deviation)) / numSize;int size = (Height - qAbs(deviation)) * numSize / 80;int transparency = 255 - 255 * qAbs(deviation) / Height;int height = Height / devide;int y = Height / 2 + deviation - height / 2;QFont font;font.setPixelSize(size);painter.setFont(font);painter.setPen(QColor(numberColor.red(),numberColor.green(),numberColor.blue(),transparency));if ( y >= 0 && y + height < Height) {//painter.drawRect(0, y, Width, height);if (num < 10)painter.drawText(QRectF(0, y, Width, height),Qt::AlignCenter,"0" + QString::number(num, 'f', 0));elsepainter.drawText(QRectF(0, y, Width, height),Qt::AlignCenter,QString::number(num, 'f', 0));}
}void NumberPicker::homing()
{if (deviation > height() / 10) {homingAni->setStartValue((height() - 1 ) / 8 - deviation);homingAni->setEndValue(0);currentValue -= interval;} else if (deviation > -height() / 10) {homingAni->setStartValue(deviation);homingAni->setEndValue(0);} else if (deviation < -height() / 10) {homingAni->setStartValue(-(height() - 1) / 8 - deviation);homingAni->setEndValue(0);currentValue += interval;}emit currentValueChanged(currentValue);homingAni->start();
}int NumberPicker::readDeviation()
{return deviation;
}void NumberPicker::setDeviation(int n)
{deviation = n;repaint();
}void NumberPicker::setNumSize(int size)
{numSize = size;repaint();
}void NumberPicker::setInterval(int n)
{interval = n;repaint();
}void NumberPicker::setDevide(int n)
{devide = n;repaint();
}void NumberPicker::setNumberColor(QRgb rgb)
{numberColor.setRgb(rgb);repaint();
}void NumberPicker::setValue(int value)
{if (value < minRange || value > maxRange) {qDebug()<<"数值设置必须在"<<minRange<<"和"<<maxRange<<"之间"<<endl;return;}currentValue = value;repaint();
}