Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍Charts
组件与QSql
数据库组件的常用方法及灵活运用。
在之前的文章中详细介绍了关于QCharts
绘图组件的使用方式,本章将继续延续这个知识点,通过使用QSql
数据库模块动态的读取某一个时间节点上的数据,当用户点击查询数据时则动态的输出该事件节点的所有数据,并将数据绘制到图形组件内,实现动态查询图形的功能。
首先我们需要生成一些测试数据,在文章课件中有一个InitDatabase
案例,该案例中通过QSql
组件动态创建一个Times
表,该表中有三个字段分别记录了主机IP地址、时间、以及数据,并动态的想表中插入一些随机测试数据,读者可运行这段程序并等待十分钟以上,此时数据库database.sqlite3
中将会出现如下所示的数据集;
再来看下主窗体是如何设计的,左侧使用一个ComboBox
下拉选择框,右侧使用两个可自由调节的Date/TimeEdit
组件,最底部则是一个graphicsView
绘图组件,如下图;
由于涉及到IP地址的选择,所以在MainWindow
主构造函数中我们需要对ComboBox
组件进行初始化,在初始化时我们需要打开数据库并将数据库中的Times
表,并查询到address
字段,这里在查询语句中使用DISTINCT
语句,该语句是用于在SQL查询中选择唯一值的关键字,它能够确保查询的结果集中每个列的值都是唯一的。
SELECT DISTINCT address FROM Times;
在代码中,上述查询的目的是从 “Times” 表中选择唯一的 “address” 列的值。如果 “Times” 表中有多个行具有相同的 “address” 值,DISTINCT
会确保在结果中只返回一个该值,以避免重复。
当具备了这条语句那么查询唯一值将变得非常容易,当查询到对应值只有只需要通过comboBox->addItem
即可将唯一的IP地址追加到组件中,如下代码所示;
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); InitLineChart(); QDateTime curDateTime = QDateTime::currentDateTime(); ui->dateTimeEdit_Start->setDateTime(curDateTime); ui->dateTimeEdit_End->setDateTime(curDateTime); ui->dateTimeEdit_Start->setDisplayFormat("yyyy-MM-dd hh:mm:ss" ); ui->dateTimeEdit_End->setDisplayFormat("yyyy-MM-dd hh:mm:ss" ); db = QSqlDatabase::addDatabase("QSQLITE" ); db.setDatabaseName("database.sqlite3" ); if (!db.open()) { std ::cout << db.lastError().text().toStdString() << std ::endl ; return ; } QSqlQuery query; if (query.exec("SELECT DISTINCT address FROM Times;" )) { QSet<QString> uniqueAddresses; while (query.next()) { QString data_name = query.value(0 ).toString(); uniqueAddresses.insert(data_name); } ui->comboBox->clear(); foreach (const QString &uniqueAddress, uniqueAddresses) { ui->comboBox->addItem(uniqueAddress); } } else { std ::cout << query.lastError().text().toStdString() << std ::endl ; } }
接着来看下如何实现InitLineChart()
绘图函数,绘图部分由于我们不需要直接绘制所以这里可以先初始化折线图表,等待后期添加数据绘制即可,这段代码的实现如下所示;
首先,创建一个QChart
对象,代表整个图表,并将其添加到QGraphicsView
中。随后,通过隐藏图例提高图表的美观度。接着,创建一个QLineSeries
对象,表示折线图中的数据序列,并将其添加到图表中。为确保正确显示,创建了X轴和Y轴的坐标轴对象,并设置了范围、格式和刻度。最后,将X轴和Y轴与折线序列关联,以便在图表中显示数据。这段代码实现了一个简单的折线图的初始化,为进一步添加和展示数据提供了基础。
void MainWindow::InitLineChart () { QChart *chart = new QChart(); ui->graphicsView_line->setChart(chart); ui->graphicsView_line->setRenderHint(QPainter::Antialiasing); chart->legend()->hide(); QLineSeries *series0 = new QLineSeries(); chart->addSeries(series0); QValueAxis *axisX = new QValueAxis; axisX->setRange(1 , 100 ); axisX->setLabelFormat("%d %" ); axisX->setMinorTickCount(5 ); QValueAxis *axisY = new QValueAxis; axisY->setRange(0 , 100 ); axisY->setMinorTickCount(4 ); chart->setAxisX(axisX, series0); chart->setAxisY(axisY, series0); }
当界面中的按钮被点击后,事件触发时执行,其主要功能是从数据库中查询记录并根据用户在界面上选择的设备地址、起始时间和结束时间条件,筛选符合条件的数据,并将其显示在折线图中。
首先,获取折线图对象和数据库查询结果的指针,然后清空折线序列准备接收新的数据。通过遍历数据库查询结果,获取每条记录的字段值,同时获取用户输入的查询条件。计算时间差并限制查询范围在3600秒内,然后判断记录是否在指定的时间范围内,并将符合条件的数据点添加到折线序列中。如果查询范围超出定义,输出错误消息。
void MainWindow::on_pushButton_clicked () { QLineSeries *series0=(QLineSeries *)ui->graphicsView_line->chart()->series().at(0 ); series0->clear(); QSqlQuery query ("SELECT * FROM Times;" ,db) ; QSqlRecord rec = query.record(); qreal t=0 ,intv=1 ; while (query.next()) { if (query.isValid()) { QString address_value = query.value(rec.indexOf("address" )).toString(); QString date_time = query.value(rec.indexOf("datetime" )).toString(); int this_value = query.value(rec.indexOf("value" )).toInt(); QString address_user = ui->comboBox->currentText(); QString start_user_time = ui->dateTimeEdit_Start->text(); QString end_user_time = ui->dateTimeEdit_End->text(); QDateTime start_timet = QDateTime::fromString(start_user_time, "yyyy-MM-dd hh:mm:ss" ); QDateTime end_timet = QDateTime::fromString(end_user_time, "yyyy-MM-dd hh:mm:ss" ); uint stime = start_timet.toTime_t(); uint etime = end_timet.toTime_t(); uint sub_time = etime - stime; if (sub_time <= 3600 ) { if (date_time.toStdString() >= start_user_time.toStdString() && date_time.toStdString() <= end_user_time.toStdString() && address_value == address_user ) { series0->append(t,this_value); t+=intv; } } else { std ::cout << "查询范围超出定义." << std ::endl ; return ; } } } }
这段代码实现了通过用户输入条件查询数据库,并动态更新折线图的功能,用于在界面上显示符合条件的数据趋势。
至此数据库与绘图组件的联动效果就实现了,其实很容易理解,因为是一个案例并没有包含任何复杂的功能这也是为了方便功能的展示,读者可自行运行并查询一个区间内的折线图,如下所示;
附件下载 SelectChart.zip