# Qt) Designer로 UI 구성하기(8) - Tab5SensorDatabase

mommers·2026년 4월 11일

QT

목록 보기
14/15

이전 글 : Qt) Designer로 UI 구성하기(7) - Tab4SensorChart

이전 글에서 소켓으로 수신한 센서 데이터를 실시간 그래프로 표시하는 Tab4를 구현했습니다.

이번 글에서는 SQLite 데이터베이스를 활용해 센서 데이터를 저장하고, 저장된 데이터를 조회하여 테이블과 그래프로 표시하는 Tab5SensorDatabase를 구현하는 과정을 정리합니다.


1. .pro 파일에 sql 모듈 추가

QSqlDatabase, QSqlQuery 등 데이터베이스 관련 클래스를 사용하기 위해 .pro 파일에 sql 모듈을 추가합니다.

QT += widgets network charts sql

2. Tab5SensorDatabase 클래스 생성

AiotClient 프로젝트 우클릭 → Add New → Qt → Qt Widgets Designer Form Class → Widget 선택

Class name을 Tab5SensorDatabase로 입력하면 아래 파일이 자동 생성됩니다.

  • tab5sensordatabase.h
  • tab5sensordatabase.cpp
  • tab5sensordatabase.ui

3. Tab5SensorDatabase UI 구성

tab5sensordatabase.ui를 Qt Designer에서 열고 아래와 같이 위젯을 배치합니다.

=================================================================
| pDateTimeEditFrom  |  pDateTimeEditTo  | [조회] | [삭제]      |
|-----------------------------------------------------------------|
|                            |                                   |
|     pTBsensor              |     pChartViewLayout              |
|  (ID/날짜/조도/온도/습도)    |      (차트 표시 영역)             |
|                            |                                   |
=================================================================

주요 위젯 objectName

위젯objectName역할
QDateTimeEditpDateTimeEditFrom조회 시작 시각
QDateTimeEditpDateTimeEditTo조회 종료 시각
QPushButtonpPBDbSearchDB 조회
QPushButtonpPBDbDelete테이블/차트 초기화
QTableWidgetpTBsensor조회 결과 테이블 표시
QWidgetpChartViewLayout차트 뷰를 추가할 레이아웃 영역

4. tab5sensordatabase.h

Tab4와 차트 관련 멤버 변수는 동일하며, 데이터베이스 관련 멤버로 QSqlDatabase가 추가됩니다.

#ifndef TAB5SENSORDATABASE_H
#define TAB5SENSORDATABASE_H

#include <QWidget>
#include <QChartView>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <qsqlerror.h>
#include <QLineSeries>
#include <QDateTimeAxis>
#include <QDate>
#include <QTime>

namespace Ui {
class Tab5SensorDatabase;
}

class Tab5SensorDatabase : public QWidget
{
    Q_OBJECT

public:
    explicit Tab5SensorDatabase(QWidget *parent = nullptr);
    ~Tab5SensorDatabase();
    void updateLastDateTimeSql(bool);

private slots:
    void tab5RecvDataSlot(QStringList&);
    void on_pPBDbDelete_clicked();
    void on_pPBDbSearch_clicked();

private:
    Ui::Tab5SensorDatabase *ui;
    QSqlDatabase qSqlDatabase;
    QLineSeries *illuLine;
    QLineSeries *humiLine;
    QLineSeries *tempLine;
    QChart *pQChart;
    QChartView *pQChartView;
    QDateTimeAxis *pQDateTimeAxis;
    QDateTime firstDateTime;
    QDateTime lastDateTime;
};

#endif // TAB5SENSORDATABASE_H

5. tab5sensordatabase.cpp

5-1. 생성자 - DB 연결 및 테이블 생성

생성자에서 SQLite DB를 열고 sensor_tb 테이블을 생성합니다.
테이블이 이미 존재하면 create table 쿼리는 실패하지만 이후 동작에는 영향이 없습니다.

Tab5SensorDatabase::Tab5SensorDatabase(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Tab5SensorDatabase)
{
    ui->setupUi(this);

    // SQLite DB 연결
    qSqlDatabase = QSqlDatabase::addDatabase("QSQLITE");
    qSqlDatabase.setDatabaseName("aiot.db");
    if(qSqlDatabase.open())
        qDebug() << "success open sqlDatabse";
    else
        qDebug() << "failed to open sqlDatabse";

    // 테이블 생성
    QString strQuery = "create table sensor_tb ("
                       "name varchar(10),"
                       "date DATETIME primary key,"
                       "illu varchar(10),"
                       "temp varchar(10),"
                       "humi varchar(10))";
    QSqlQuery QSqlQuery;
    if(QSqlQuery.exec(strQuery))
        qDebug() << "Create Table";

    // 차트 초기화 (Tab4와 동일)
    illuLine = new QLineSeries(this);
    illuLine->setName("조도");
    // ... (Tab4와 동일한 QPen 설정 생략)

    pQChart = new QChart();
    pQChart->addSeries(illuLine);
    pQChart->addSeries(humiLine);
    pQChart->addSeries(tempLine);
    pQChart->createDefaultAxes();
    pQChart->axes(Qt::Vertical).constFirst()->setRange(0, 100);

    pQChartView = new QChartView(pQChart);
    pQDateTimeAxis = new QDateTimeAxis;
    pQDateTimeAxis->setFormat("hh:mm");

    updateLastDateTimeSql(false);

    ui->pChartViewLayout->layout()->addWidget(pQChartView);
    pQChartView->chart()->setAxisX(pQDateTimeAxis, illuLine);
    pQChartView->chart()->setAxisX(pQDateTimeAxis, humiLine);
    pQChartView->chart()->setAxisX(pQDateTimeAxis, tempLine);
}

aiot.db 파일은 앱 실행 시 자동으로 생성됩니다.


5-2. updateLastDateTimeSql - X축 시간 범위 업데이트

Tab4의 updateLastDateTime과 달리, Tab5에서는 UI의 pDateTimeEditFrom, pDateTimeEditTo 값을 기준으로 X축 범위를 설정합니다.

void Tab5SensorDatabase::updateLastDateTimeSql(bool bFlag)
{
    QDateTime firstDateTime;
    QDateTime lastDateTime;
    QDateTime fromDateTime = ui->pDateTimeEditFrom->dateTime();
    QDateTime toDateTime = ui->pDateTimeEditTo->dateTime();

    if(!bFlag)
    {
        firstDateTime = fromDateTime;
        lastDateTime = toDateTime;
    }
    else
    {
        firstDateTime = ui->pDateTimeEditFrom->dateTime();
        lastDateTime = QDateTime::currentDateTime().addSecs(60);
    }
    pQDateTimeAxis->setRange(firstDateTime, lastDateTime);
}

5-3. tab5RecvDataSlot - 수신 데이터 DB 저장 및 차트 추가

소켓으로 데이터를 수신하면 DB에 INSERT하고 차트에도 실시간으로 추가합니다.
INSERT 성공 시 pDateTimeEditTo를 현재 시각으로 업데이트합니다.

void Tab5SensorDatabase::tab5RecvDataSlot(QStringList& strList)
{
    QDateTime dateTime = QDateTime::currentDateTime();
    QString name = strList[1];
    QString strIllu = strList[3];
    QString strTemp = strList[4];
    QString strHumi = strList[5];

    if(lastDateTime.toSecsSinceEpoch() < dateTime.toSecsSinceEpoch())
        updateLastDateTimeSql(true);

    // DB INSERT
    QString strQuery = "insert into sensor_tb(name, date, illu, temp, humi) values('"
                     + name + "' , '"
                     + dateTime.toString("yyyy/MM/dd hh:mm:ss") + "' , '"
                     + strIllu + "' , '"
                     + strTemp + "' , '"
                     + strHumi + "' ) ";
    QSqlQuery qSqlQuery;
    if(qSqlQuery.exec(strQuery))
    {
        qDebug() << "Insert Query OK";
        ui->pDateTimeEditTo->setDateTime(dateTime);
        pQDateTimeAxis->setMax(dateTime);
    }

    // 차트에 실시간 추가
    illuLine->append(dateTime.toMSecsSinceEpoch(), strIllu.toInt());
    humiLine->append(dateTime.toMSecsSinceEpoch(), strTemp.toDouble());
    tempLine->append(dateTime.toMSecsSinceEpoch(), strHumi.toDouble());
}

5-4. 조회 버튼 - DB SELECT 및 테이블/차트 갱신

조회 버튼을 누르면 DB에서 pDateTimeEditFrom ~ pDateTimeEditTo 범위의 데이터를 SELECT하여 pTBsensor에 표시하고 차트도 갱신합니다.

void Tab5SensorDatabase::on_pPBDbSearch_clicked()
{
    // DB에서 가장 오래된 데이터 시각을 찾아 From에 설정
    QSqlQuery minQuery;
    if(minQuery.exec("select min(date) from sensor_tb") && minQuery.next())
    {
        QString strMinDate = minQuery.value(0).toString();
        QDateTime minDateTime = QDateTime::fromString(strMinDate, "yyyy/MM/dd hh:mm:ss");
        if(minDateTime.isValid())
        {
            ui->pDateTimeEditFrom->setDateTime(minDateTime);
            ui->pDateTimeEditTo->setDateTime(QDateTime::currentDateTime());
        }
    }

    illuLine->clear();
    humiLine->clear();
    tempLine->clear();
    updateLastDateTimeSql(false);

    // 범위 조건으로 SELECT
    QString strFromDateTime = ui->pDateTimeEditFrom->dateTime().toString("yyyy/MM/dd hh:mm:ss");
    QString strToDateTime = ui->pDateTimeEditTo->dateTime().toString("yyyy/MM/dd hh:mm:ss");
    QString strQuery = "select * from sensor_tb where '"
                     + strFromDateTime + "' <= date AND date < '"
                     + strToDateTime + "' ";

    int rowCount = 0;
    QSqlQuery qSqlQuery;
    if(qSqlQuery.exec(strQuery))
    {
        qDebug() << "Select Query Ok";
        while(qSqlQuery.next())
        {
            rowCount++;
            ui->pTBsensor->setRowCount(rowCount);

            QTableWidgetItem *pId   = new QTableWidgetItem();
            QTableWidgetItem *pDate = new QTableWidgetItem();
            QTableWidgetItem *pIllu = new QTableWidgetItem();
            QTableWidgetItem *pTemp = new QTableWidgetItem();
            QTableWidgetItem *pHumi = new QTableWidgetItem();

            pId->setText(qSqlQuery.value("name").toString());
            pDate->setText(qSqlQuery.value("date").toString());
            pIllu->setText(qSqlQuery.value("illu").toString());
            pTemp->setText(qSqlQuery.value("temp").toString());
            pHumi->setText(qSqlQuery.value("humi").toString());

            ui->pTBsensor->setItem(rowCount-1, 0, pId);
            ui->pTBsensor->setItem(rowCount-1, 1, pDate);
            ui->pTBsensor->setItem(rowCount-1, 2, pIllu);
            ui->pTBsensor->setItem(rowCount-1, 3, pTemp);
            ui->pTBsensor->setItem(rowCount-1, 4, pHumi);

            QDateTime xValue = QDateTime::fromString(pDate->text(), "yyyy/MM/dd hh:mm:ss");
            illuLine->append(xValue.toMSecsSinceEpoch(), pIllu->text().toInt());
            humiLine->append(xValue.toMSecsSinceEpoch(), pHumi->text().toDouble());
            tempLine->append(xValue.toMSecsSinceEpoch(), pTemp->text().toDouble());
        }

        ui->pTBsensor->resizeColumnToContents(0);
        ui->pTBsensor->resizeColumnToContents(1);
        ui->pTBsensor->resizeColumnToContents(2);
        ui->pTBsensor->resizeColumnToContents(3);
        ui->pTBsensor->resizeColumnToContents(4);
    }
}

5-5. 삭제 버튼 - 테이블/차트 초기화

void Tab5SensorDatabase::on_pPBDbDelete_clicked()
{
    ui->pTBsensor->clearContents();
    illuLine->clear();
    humiLine->clear();
    tempLine->clear();
}

삭제 버튼은 화면에 표시된 테이블과 차트만 초기화합니다. DB에 저장된 데이터는 유지됩니다.


6. mainwidget에 Tab5 추가 및 소켓 연동

mainwidget.h

#include <tab5sensordatabase.h>

Tab5SensorDatabase *pTab5SensorDatabase;

mainwidget.cpp

pTab5SensorDatabase = new Tab5SensorDatabase(ui->pTab5);
ui->pTab5->setLayout(pTab5SensorDatabase->layout());

connect(pTab2SocketClient, SIGNAL(tab5RecvDataSig(QStringList&)),
        pTab5SensorDatabase, SLOT(tab5RecvDataSlot(QStringList&)));

7. Tab4 vs Tab5 비교

Tab4Tab5
데이터 저장없음 (메모리)SQLite DB 저장
X축 기준현재 시각 자동DateTimeEdit 기준
조회 기능없음DB SELECT + 테이블 표시
초기화차트만테이블 + 차트

전체 흐름 요약

.pro에 sql 모듈 추가 (QT += sql)
    ↓
Tab5SensorDatabase 클래스 생성 (Qt Widgets Designer Form Class)
    ↓
UI 구성 (DateTimeEdit x2 + 조회/삭제 버튼 + QTableWidget + 차트 영역)
    ↓
생성자 : SQLite DB 연결 → sensor_tb 테이블 생성 → 차트 초기화
    ↓
tab5RecvDataSlot : 수신 데이터 DB INSERT → 차트 실시간 추가
    ↓
조회 버튼 : DB SELECT → QTableWidget 표시 → 차트 갱신
    ↓
삭제 버튼 : 화면 테이블/차트 초기화 (DB 데이터 유지)
    ↓
mainwidget에 Tab5 추가 및 Tab2 수신 시그널 연결
profile
임베디드 개발자가 되기 위해 공부중입니다!

0개의 댓글