C/C++ Qt 資料庫與Chart實現歷史資料展示

lyshark發表於2021-12-11

在前面的博文中具體介紹了QChart元件是如何繪製各種通用的二維圖形的,本章內容將繼續延申一個新的知識點,通過資料庫儲存某一段時間節點資料的走向,當使用者通過編輯框提交查詢記錄時,程式自動過濾出該時間節點下所有的資料,並將該資料動態繪製到圖形元件內,實現動態查詢圖形的功能。

首先通過如下程式碼,建立Times表,表內記錄有某個主機某個時間節點下的數值:

#include <QCoreApplication>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <QSqlRecord>
#include <iostream>
#include <QStringList>
#include <QString>
#include <QVariant>
#include <QDebug>
#include <QDateTime>
#include <QTime>

// 初始化資料庫
// https://www.cnblogs.com/lyshark
void InitSql()
{
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");

    db.setDatabaseName("lyshark.db");
    if (!db.open())
    {
           std::cout << db.lastError().text().toStdString()<< std::endl;
           return;
    }

   // 執行SQL建立表
   db.exec("DROP TABLE Times");
   db.exec("CREATE TABLE Times ("
                   "id INTEGER PRIMARY KEY AUTOINCREMENT, "
                   "address VARCHAR(64) NOT NULL, "
                   "datetime VARCHAR(128) NOT NULL, "
                   "value INTEGER NOT NULL"
           ")"
        );

   db.commit();
   db.close();
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    InitSql();
    return a.exec();
}

資料庫結構如下:

接著編寫一個模擬插入資料的案例,該案例每一秒向資料庫內插入一條記錄,我們執行一段時間。

#include <QCoreApplication>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <QSqlRecord>
#include <iostream>
#include <QStringList>
#include <QString>
#include <QVariant>
#include <QDebug>
#include <QDateTime>
#include <QTime>

// 延時函式
void Sleep(int msec)
{
    QTime dieTime = QTime::currentTime().addMSecs(msec);
    while(QTime::currentTime() < dieTime)
        QCoreApplication::processEvents(QEventLoop::AllEvents,100);
}
// 生成隨機數
int GetRandom()
{
    int num = qrand() % 100;
    return num;
}

// 插入資料
void InsertSQL()
{
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName("lyshark.db");
     if (!db.open())
     {
            std::cout << db.lastError().text().toStdString()<< std::endl;
            return;
     }

     for(int index=0;index <99999;index++)
     {
        QString address = QString("192.168.1.100");
        QDateTime curDateTime = QDateTime::currentDateTime();
        QString date_time = curDateTime.toString("yyyy-MM-dd hh:mm:ss");
        int value = GetRandom();

        QString run_sql = QString("INSERT INTO Times(id,address,datetime,value) VALUES (%1,'%2','%3',%4);")
                                  .arg(index).arg(address).arg(date_time).arg(value);
        std::cout << "執行插入語句: " << run_sql.toStdString() << std::endl;

        db.exec(run_sql);
        db.commit();
        Sleep(1000);
     }
     db.close();
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
    InsertSQL();
    return a.exec();
}

執行插入程式,統計一段時間 從 2021-12-11 15:34:162021-12-11 15:40:04 停止,表內記錄如下:

如果我們需要查詢某一個時間節點下的資料,例如查詢2021-12-11 15:35:00 - 2021-12-11 15:37:00的資料可以這樣寫SQL:

#include <QCoreApplication>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <QSqlRecord>
#include <iostream>
#include <QStringList>
#include <QString>
#include <QVariant>
#include <QDebug>
#include <QDateTime>
#include <QTime>

// 輸出資料
// https://www.cnblogs.com/lyshark
void SelectSQL()
{
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName("lyshark.db");
     if (!db.open())
     {
            std::cout << db.lastError().text().toStdString()<< std::endl;
            return;
     }

    // 查詢資料
    QSqlQuery query("SELECT * FROM Times;",db);
    QSqlRecord rec = query.record();

    // 迴圈所有記錄
    while(query.next())
    {
        // 判斷當前記錄是否有效
        if(query.isValid())
        {
            int id_value = query.value(rec.indexOf("id")).toInt();
            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();

            if(date_time.toStdString() >= "2021-12-11 15:35:00" && date_time.toStdString() <="2021-12-11 15:37:00")
            {
                std::cout << "value: " << this_value << std::endl;
            }
        }
    }
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    SelectSQL();
    return a.exec();
}

這樣就可以將該區間內所有的資料全部過濾出來了:

將過濾引數與QChart元件結合即可實現動態繪圖效果,繪製UI介面如下:

當使用者點選查詢時,直接從資料庫內取出資料,並將其動態更新到Chart元件內即可,實現程式碼如下:

#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <QSqlRecord>
#include <iostream>
#include <QStringList>
#include <QString>
#include <QVariant>
#include <QDebug>
#include <QDateTime>
#include <QTime>

// 初始化Chart圖表
void MainWindow::InitChart()
{
    // 建立圖表的各個部件
    QChart *chart = new QChart();

    // 將Chart新增到ChartView
    ui->graphicsView->setChart(chart);
    ui->graphicsView->setRenderHint(QPainter::Antialiasing);

    // 隱藏圖例
    chart->legend()->hide();

    // 設定圖表主題色
    ui->graphicsView->chart()->setTheme(QChart::ChartTheme(1));

    // 建立曲線序列
    QLineSeries *series0 = new QLineSeries();

    // 序列新增到圖表
    chart->addSeries(series0);

    // 建立座標軸
    QValueAxis *axisX = new QValueAxis;    // X軸
    axisX->setRange(1, 100);               // 設定座標軸範圍
    axisX->setLabelFormat("%d %");         // 設定X軸格式
    axisX->setMinorTickCount(5);           // 設定X軸刻度

    QValueAxis *axisY = new QValueAxis;    // Y軸
    axisY->setRange(0, 100);               // Y軸範圍
    axisY->setMinorTickCount(4);           // s設定Y軸刻度

    // 設定X於Y軸資料集
    chart->setAxisX(axisX, series0);   // 為序列設定座標軸
    chart->setAxisY(axisY, series0);
}

// 為序列生成資料
void MainWindow::SetData()
{
    // 獲取指標
    QLineSeries *series0=(QLineSeries *)ui->graphicsView->chart()->series().at(0);

    // 清空圖例
    series0->clear();

    // 連結資料庫
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName("lyshark.db");
    if (!db.open())
    {
        return;
    }

    // 查詢資料
    QSqlQuery query("SELECT * FROM Times;",db);
    QSqlRecord rec = query.record();

    // 賦予資料
    qreal t=0,intv=1;

    // 迴圈所有記錄
    while(query.next())
    {
        // 判斷當前記錄是否有效
        // https://www.cnblogs.com/lyshark
        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 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();

            // 只允許查詢小於180秒的記錄
            uint sub_time = etime - stime;
            if(sub_time <= 180)
            {
                // 查詢指定區間內的資料
                if(date_time.toStdString() >= start_user_time.toStdString() && date_time.toStdString() <= end_user_time.toStdString())
                {
                    // std::cout << "區間內的資料: " << this_value << std::endl;
                    series0->append(t,this_value);
                    t+=intv;
                }
            }
            else
            {
                std::cout << "查詢範圍超出定義." << std::endl;
                return;
            }
        }
    }
}

// 將新增的widget控制元件件提升為QChartView類
MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    InitChart();

    // 初始化時間元件
    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");
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    SetData();
}

查詢效果如下所示:

相關文章