LAB/QT_C++

QT 6 Programing [Chapter 13. Stream - Chapter 16. Model and View]

it-lab-0130 2024. 10. 8. 15:41

13. Stream

여기에서 Stream 이란 데이터를 특정 변수에 Write/Read 를 쉽게 하기 위한 방법을 말한다.

QDataStream 은 Binary 데이터를 Write/Read 하는데 사용하며 QTextStream 은 Text 기반의 데이터를 Write/Read 하는데 사용된다.

 

[QDataStream 상에 데이터를 Write/Read 하는 예제]

encoding( ) 함수에서는 quint32, quint8, quint32 타입의 데이터를 QDataStream을 이용해 QByteArray 에 저장할 것이다. 각 변수에는 123, 124, 125가 저장할 것이다.

#include <QCoreApplication>
#include <QIODevice>
#include <QDataStream>
#include <QDebug>

QByteArray encoding()
{
    quint32 value1 = 123;
    quint8  value2 = 124;
    quint32 value3 = 125;

    QByteArray outData;
    QDataStream outStream(&outData, QIODevice::WriteOnly);

    outStream << value1;
    outStream << value2;
    outStream << value3;

    qDebug() << "outData size : " << outData.size() << " Bytes";

    return outData;
}

void decoding(QByteArray _data)
{
    QByteArray inData = _data;

    quint32 inValue1 = 0;
    quint8  inValue2 = 0;
    quint32 inValue3 = 0;

    QDataStream inStream(&inData, QIODevice::ReadOnly);

    inStream >> inValue1; // 123
    inStream >> inValue2; // 124
    inStream >> inValue3; // 125

    qDebug("[First : %d] [Second : %d] [Third : %d]"
                , inValue1, inValue2, inValue3 );
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QByteArray encData = encoding();
    decoding(encData);

    return a.exec();
}

 

 

[QTextStream 상에 데이터를 Write/Read 하는 예제]

 

writeData( ) 함수에서는 QByteArray 에 저장된 값을 QTextStream 을 이용해 Write 할 것 이다.

readData( ) 함수에서는 반대로 QByteArray 에 저장된 값을 Read 할 것이다.

#include <QCoreApplication>
#include <QIODevice>
#include <QTextStream>
#include <QDebug>

QByteArray writeData(QByteArray _data)
{
    QByteArray temp = _data;

    QByteArray outData;
    QTextStream outStream(&outData, QIODevice::WriteOnly);

    for(qsizetype i = 0 ; i < temp.size() ; i++) {
        outStream << temp.at(i);
    }

    outStream.flush();

    return outData;
}

void readData(QByteArray _data)
{
    QTextStream outStream(&_data, QIODevice::ReadOnly);

    QByteArray inData;
    for(qsizetype i = 0 ; i < _data.size() ; i++)
    {
        char data;
        outStream >> data;
        inData.append(data);
    }

    qDebug("READ DATA : [%s]", inData.data());
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QByteArray retData = writeData("Hello world."); // 12 Bytes
    qDebug() << "WRITE DATA size : " << retData.size() << " Bytes";

    readData(retData);

    return a.exec();
}

=> writeData 함수에서는 첫 번째 인자로 “Hello world” 를 인자로 넘겨 줄 것이다. 이 문자열은 QByteArray 에 저장된다. 저장 된 이 데이터를 QTextStream 을 이용해QByteArray 에 저장한다. 그리고 readData( ) 함수에는 “Hello world.” 가 저장되어 있는 QByteArray 를 QTextStream 을 이용해 데이터를 읽어 올 것이다. 따라서 readData( ) 함수의 마지막 라인에서 “Hello world”를 출력할 것이다.

 

 


 

15. Qt Property

Qt에서 제공하는 Property System 은 C++에서 제공하는 Property System 과 비슷하다. Property는 객체에서 값을 설정하고 가져오는 경우에 사용된다.

[person.h]

#ifndef PERSON_H
#define PERSON_H

#include <QObject>

class Person : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString name MEMBER m_name READ getName WRITE setName
               NOTIFY nameChanged)
	// getName( ) 과 setName( ) 멤버 함수를 Q_PROPERTY 매크로에 등록하였다.
public:
    explicit Person(QObject *parent = nullptr);

    QString getName() const
    {
        return m_name;
    }

    void setName(const QString &n)
    {
        m_name = n;
        emit nameChanged(n);
    }

private:
    QString m_name;

signals:
    void nameChanged(const QString &n);

public slots:

};

#endif // PERSON_H

 

[widget.h]

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include "person.h"

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget();

public slots:
    void buttonPressed();
    void nameChanged(const QString &n);

private:
    Ui::Widget *ui;

    Person *goodman;

};

#endif // WIDGET_H

 

[person.cpp]

#include "person.h"

Person::Person(QObject *parent) : QObject(parent)
{
}

 

[widget.cpp]

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include "person.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    connect(ui->pushButton, &QPushButton::pressed,
            this,           &Widget::buttonPressed);

    goodman = new Person();
    connect(goodman, &Person::nameChanged,
            this,    &Widget::nameChanged);
}

void Widget::buttonPressed()
{
    QString name = ui->leName->text();
    goodman->setProperty("name", name);
}

void Widget::nameChanged(const QString &n)
{
    qDebug() << Q_FUNC_INFO << "Name Changed : " << n;

    QVariant myName = goodman->property("name");
    qDebug() << "My name is " << myName.toString();
}
// getName( ) 와 setName( ) 멤버 함수를 접근해 name 변수 값을 얻어오거나 설정할 수 있지만 
// Q_PROPERTY 매크로를 사용하면 setProperty( ) 와 property( ) 를 사용해 값을 얻어오거나 설정할 수 있다.

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

 

[main.cpp]

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();

    return a.exec();
}

 

 

 

[QPROPERTY  다양한 옵션]

Q_PROPERTY(type name
(READ getFunction [WRITE setFunction] |
MEMBER memberName [(READ getFunction |
WRITE setFunction)] )
[RESET resetFunction]
[NOTIFY notifySignal]
[REVISION int]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
[CONSTANT]
[FINAL])

=> MEMBER 키워드는 READ 키워드로부터 읽어 들일 값을 지정할 수 있다.

 

 

예를 들어 위 의 예제 소스코드에서 private 에서 선언한 m_name 변수를 다음과 같이 지정할 수 있다.

class Person : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString name MEMBER m_name READ getName WRITE setName)
    ...

 

 

Q_PROPERTY 매크로가 제공하는 키워드 중 NOTIFY 키워드는 시그널 지정할 수 있다.

class Person : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString name MEMBER m_name READ getName WRITE setName
    NOTIFY nameChanged)
    
    public:
        explicit Person(QObject *parent = nullptr);
        QString getName() const {
        return m_name;
}
void setName(const QString &n)
{
	m_name = n;
	emit nameChanged(n);
}
private:
	QString m_name;
signals:
	void nameChanged(const QString &n);
...

 

[Change] 버튼을 클릭하면 Person클래스의 setName( ) 멤버 함수를 QObject 클래스에서 제공하는 setProperty( ) 멤버 함
수를 이용해 값을 변경 한다.


 

16. Model and View

 

** QListWidget 은 QListView와 UI가 동일하다. 하지만 QListWidget 과 QListView 는 데이터를 삽입/수정/삭제 하는데 차이가 있다.

 

[QListWidget]

클래스 이름의 마지막에 View 대신, Widget 이라는 단어를 사용한 클래스들은 아래 그림에서 보는 것과 같이 데이터를 직접 삽입/수정/삭제 할 수 있는 멤버 함수를 제공한다.

 

[QListView]

QListView, QTableView, QTreeView 클래스와 같이 마지막에 View 라는 단어를 사용하는 위젯 클래스들은 각각의 멤버 함수를 사용해 데이터를 삽입/수정/삭제 하지 않고 Model 클래스라는 매개체를 이용해 데이터를 삽입/수정/삭제 할 수 있다.

** 이 방식을 사용할 경우 QListView 를 사용하든지 QTableView 를 사용하든지 동일한 Model을 사용할 수 있다는 장점을 가지고 있다.

기능 QListWidget QListView
데이터 관리 자체 저장 Model/View
사용 편의성 간편 복잡
기능 기본적인 기능 다양한 기능
성능 아이템 수가 적을 때 유리 아이템 수가 많을 때 유리

 

Qt 에서 제공하는 Model/View 는 다음 그림에서 보는 것과 같이 Delegate를 이용해 데이터를 핸들링 할 수 있다.

예를 들어 View 위젯 상에 표시된 데이터 항목 중 특정 항목을 마우스로 더블 클릭해 데이터를 수정하기 위해서 이벤트를 발생해야 하는데 Model/View 에서는 이러한 이벤트를 처리 하기 위해 Delegate를 사용할 수 있다.

 

** QSqlQueryModel 클래스를 이용하면 SQL 문을 직접 쿼리(QUERY) 할 수 있는 멤버 함수를 제공한다.따라서 별도의 데이터베이스 쿼리로 가져온 데이터를 편집 후에 Model을 이용해도 되지만 QSqlQueryModel 클래스를 이용하면 직접 데이터를 삽입할 수 있다. 이외에도 다음 그림에서 보는 것과 같이 다양한 Model 클래스를 제공한다.

 

QListView 클래스 

 

QString 데이터 타입의 단순한 데이터 리스트를 관리할 있는 기능을 제공한다.

 

[widget.h]

#ifndef WIDGET_H
#define WIDGET_H

#include <QApplication>
#include <QVBoxLayout>
#include <QSplitter>
#include <QFileSystemModel>
#include <QTreeView>
#include <QListView>
#include <QLabel>
#include <QStringListModel>


class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
};

#endif // WIDGET_H

 

[main.cpp]

#include <QtWidgets/QApplication>

#include "widget.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();

    return a.exec();
}

 

[widget.cpp]

#include "widget.h" // widget.h 헤더 파일을 포함합니다. 이 파일에는 Widget 클래스의 정의가 들어있습니다.

Widget::Widget(QWidget *parent)  // Widget 클래스의 생성자입니다. 부모 위젯을 인자로 받습니다.
    : QWidget(parent) // 부모 클래스인 QWidget의 생성자를 호출합니다.
{
    resize(600, 300); // 위젯의 크기를 600x300 픽셀로 설정합니다.

    QStringList strList; // QStringList는 QString(문자열)을 저장하는 리스트입니다.
    strList << "Monday" << "Tuesday" << "Wedneday"  // strList에 요일 문자열을 추가합니다.
            << "Thurday" << "Friday"; 

    QAbstractItemModel *model = new QStringListModel(strList); // QStringListModel은 QStringList를 데이터로 사용하는 모델입니다.
                                                            // QAbstractItemModel은 Qt의 Model/View 프레임워크에서 모델을 나타내는 추상 클래스입니다.

    QListView *view = new QListView(); // QListView는 리스트 형태로 데이터를 표시하는 위젯입니다.
    view->setModel(model); // view에 model을 설정합니다. 이제 view는 model의 데이터를 표시합니다.

    QModelIndex index = model->index(3, 0); // model에서 3번째 행, 0번째 열에 해당하는 인덱스를 가져옵니다.
    QString text = model->data(index, Qt::DisplayRole).toString(); // 해당 인덱스의 데이터를 문자열로 변환합니다. Qt::DisplayRole은 표시할 데이터를 나타냅니다.

    QLabel *lbl = new QLabel(""); // QLabel은 텍스트를 표시하는 위젯입니다.
    lbl->setText(text); // lbl에 text를 설정합니다. 즉, "Thursday"가 표시됩니다.

    QVBoxLayout *lay = new QVBoxLayout(); // QVBoxLayout은 위젯을 수직으로 배치하는 레이아웃입니다.
    lay->addWidget(view); // view를 레이아웃에 추가합니다.
    lay->addWidget(lbl); // lbl을 레이아웃에 추가합니다.

    setLayout(lay); // 위젯의 레이아웃을 lay로 설정합니다.
}

Widget::~Widget() // Widget 클래스의 소멸자입니다.
{

}

 

QTreeView 와 QListView 클래스를 이용한 예제

 

파일로부터 데이터를 읽어와 Model 에 저장한 다음 Model 을 View 와 연결 해 파일 시스템을 표시하는 예제이다.

[widget.cpp]

#include "widget.h" // widget.h 헤더 파일을 포함합니다. 이 파일에는 Widget 클래스의 정의가 들어있습니다.

Widget::Widget(QWidget *parent)  // Widget 클래스의 생성자입니다. 부모 위젯을 인자로 받습니다.
    : QWidget(parent) // 부모 클래스인 QWidget의 생성자를 호출합니다.
{
    resize(600, 300); // 위젯의 크기를 600x300 픽셀로 설정합니다.
    QSplitter *splitter = new QSplitter(this); 
    // QSplitter는 위젯을 분할하여 크기를 조절할 수 있도록 하는 위젯입니다.
    
    // Model class 
    QFileSystemModel *model = new QFileSystemModel; 
    // QFileSystemModel은 파일 시스템의 파일 및 디렉토리 정보를 제공하는 모델입니다.
    model->setRootPath(QDir::currentPath()); 
    // 모델의 루트 경로를 현재 경로로 설정합니다.
    
    // View widget - QTreeView
    QTreeView *tree = new QTreeView(splitter); 
    // QTreeView는 트리 형태로 데이터를 표시하는 위젯입니다. splitter를 부모 위젯으로 설정합니다.
    tree->setModel(model); 
    // tree에 model을 설정합니다. 이제 tree는 파일 시스템을 트리 형태로 표시합니다.
    tree->setRootIndex(model->index(QDir::currentPath())); 
    // tree의 루트 인덱스를 현재 경로로 설정합니다.
    
    // View widget - QListView
    QListView *list = new QListView(splitter); // QListView는 리스트 형태로 데이터를 표시하는 위젯입니다. splitter를 부모 위젯으로 설정합니다.
    list->setModel(model); // list에 model을 설정합니다. 이제 list는 파일 시스템을 리스트 형태로 표시합니다.
    list->setRootIndex(model->index(QDir::currentPath())); // list의 루트 인덱스를 현재 경로로 설정합니다.
    
    QVBoxLayout *layout = new QVBoxLayout(); // QVBoxLayout은 위젯을 수직으로 배치하는 레이아웃입니다.
    layout->addWidget(splitter); // splitter를 레이아웃에 추가합니다.
    setLayout(layout); // 위젯의 레이아웃을 layout으로 설정합니다.
}

Widget::~Widget() // Widget 클래스의 소멸자입니다.
{
}

좌측은 QTreeView 클래스를 이용해 파일시스템으로부터 가져온 데이터를 표시하였다.
우측의 QListView 위젯은 현재 디렉토리에 존재하는 파일과 디렉토리를 표시하였다.

 

 

QTableView 클래스

 

표 형태의 데이터를 표시하는데 적합한 위젯이다.

 

[widget.h]

#ifndef WIDGET_H
#define WIDGET_H

#include <QAbstractItemModel>
#include <QStandardItemModel>
#include <QStandardItem>
#include <QVBoxLayout>
#include <QDateTime>
#include <QTableView>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:

};

#endif // WIDGET_H


[widget.cpp]

#include "widget.h" // widget.h 헤더 파일을 포함합니다. 이 파일에는 Widget 클래스의 정의가 들어있습니다.
#include <QDate> // QDate 클래스를 사용하기 위해 헤더 파일을 포함합니다.

Widget::Widget(QWidget *parent)  // Widget 클래스의 생성자입니다. 부모 위젯을 인자로 받습니다.
    : QWidget(parent) // 부모 클래스인 QWidget의 생성자를 호출합니다.
{
    QStandardItemModel *model = new QStandardItemModel(0, 3); // QStandardItemModel은 표 형태의 데이터를 표현하는 모델입니다.
        // 0행 3열의 모델을 생성합니다.

    // 헤더 설정
    model->setHeaderData(0, Qt::Horizontal, QObject::tr("Subject")); // 0번째 열의 헤더를 "Subject"로 설정합니다.
    model->setHeaderData(1, Qt::Horizontal, QObject::tr("Description")); // 1번째 열의 헤더를 "Description"으로 설정합니다.
    model->setHeaderData(2, Qt::Horizontal, QObject::tr("Date")); // 2번째 열의 헤더를 "Date"로 설정합니다.

    // 세로 헤더 설정
    model->setVerticalHeaderItem(0, new QStandardItem("Col 1")); // 0번째 행의 세로 헤더를 "Col 1"로 설정합니다.
    model->setVerticalHeaderItem(1, new QStandardItem("Col 2")); // 1번째 행의 세로 헤더를 "Col 2"로 설정합니다.

    // 데이터 설정
    model->setData(model->index(0, 0), "Monitor"); // (0, 0) 위치에 "Monitor" 데이터를 설정합니다.
    model->setData(model->index(0, 1), "LCD"); // (0, 1) 위치에 "LCD" 데이터를 설정합니다.
    model->setData(model->index(0, 2), QDate(2030, 10, 4)); // (0, 2) 위치에 QDate(2030, 10, 4) 데이터를 설정합니다.

    model->setData(model->index(1, 0), "CPU"); // (1, 0) 위치에 "CPU" 데이터를 설정합니다.
    model->setData(model->index(1, 1), "Samsung"); // (1, 1) 위치에 "Samsung" 데이터를 설정합니다.
    model->setData(model->index(1, 2), QDate(2030, 10, 4)); // (1, 2) 위치에 QDate(2030, 10, 4) 데이터를 설정합니다.

    QTableView *table = new QTableView(); // QTableView는 표 형태로 데이터를 표시하는 위젯입니다.
    table->setModel(model); // table에 model을 설정합니다. 이제 table은 model의 데이터를 표 형태로 표시합니다.

    QVBoxLayout *lay = new QVBoxLayout(); // QVBoxLayout은 위젯을 수직으로 배치하는 레이아웃입니다.
    lay->addWidget(table); // table을 레이아웃에 추가합니다.

    setLayout(lay); // 위젯의 레이아웃을 lay로 설정합니다.
}

Widget::~Widget() // Widget 클래스의 소멸자입니다.
{

}


[main.cpp]

#include <QtWidgets/QApplication>
#include "widget.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();

    return a.exec();
}

 

 

QTableWidget

 

[checkboxhead.h]

#ifndef CHECKBOXHEADER_H  // 헤더 파일 중복 포함 방지
#define CHECKBOXHEADER_H

#include <QHeaderView> // QHeaderView 클래스를 사용하기 위해 헤더 파일을 포함합니다.
#include <QObject> // QObject 클래스를 사용하기 위해 헤더 파일을 포함합니다.
#include <QPainter> // QPainter 클래스를 사용하기 위해 헤더 파일을 포함합니다.
#include <QMouseEvent> // QMouseEvent 클래스를 사용하기 위해 헤더 파일을 포함합니다.

class CheckBoxHeader : public QHeaderView // QHeaderView 클래스를 상속받는 CheckBoxHeader 클래스를 정의합니다.
{
    Q_OBJECT // Qt의 메타 오브젝트 시스템을 사용하기 위한 매크로입니다.

public: // public 멤버
    CheckBoxHeader(Qt::Orientation orientation, // 생성자: 헤더의 방향(수평 또는 수직)을 지정합니다.
                  QWidget* parent = nullptr); // 부모 위젯을 지정합니다.

    bool isChecked() const { return isChecked_; } // 체크 박스가 체크되었는지 여부를 반환합니다.
    void setIsChecked(bool val); // 체크 박스의 체크 상태를 설정합니다.

signals: // 시그널: 다른 객체에 이벤트를 알리기 위한 메커니즘입니다.
    void checkBoxClicked(bool state); // 체크 박스가 클릭되었을 때 방출되는 시그널입니다.

protected: // protected 멤버: 파생 클래스에서 접근 가능합니다.
    void paintSection(QPainter* painter, // 섹션을 그리는 함수입니다.
                      const QRect& rect, // 섹션의 사각형 영역입니다.
                      int logicalIndex) const; // 섹션의 논리적 인덱스입니다.

    void mousePressEvent(QMouseEvent* event); // 마우스 클릭 이벤트를 처리하는 함수입니다.

private: // private 멤버: 클래스 내부에서만 접근 가능합니다.
    bool isChecked_; // 체크 박스의 체크 상태를 저장하는 변수입니다.

    void redrawCheckBox(); // 체크 박스를 다시 그리는 함수입니다.
};
#endif // CHECKBOXHEADER_H

 

[widget.h]

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QHeaderView>
#include <QPainter>
#include <QCheckBox>
#include <QDebug>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;

public slots:
    void checkBoxClicked(bool state);
};



#endif // WIDGET_H


[checkboxhead.cpp]

#include "checkboxheader.h" // checkboxheader.h 헤더 파일을 포함합니다.

CheckBoxHeader::CheckBoxHeader(Qt::Orientation orientation, // 생성자: 헤더의 방향(수평 또는 수직)을 지정합니다.
                             QWidget* parent) // 부모 위젯을 지정합니다.
    : QHeaderView(orientation, parent) // 부모 클래스인 QHeaderView의 생성자를 호출합니다.
{
    isChecked_ = true; // 체크 박스의 초기 상태를 체크된 상태로 설정합니다.
}

void CheckBoxHeader::paintSection(QPainter* painter, // 섹션을 그리는 함수입니다.
                                  const QRect& rect, // 섹션의 사각형 영역입니다.
                                  int logicalIndex) const // 섹션의 논리적 인덱스입니다.
{
    painter->save(); // 현재 painter 상태를 저장합니다.
    QHeaderView::paintSection(painter, rect, logicalIndex); // 부모 클래스의 paintSection 함수를 호출하여 기본적인 헤더 섹션을 그립니다.
    painter->restore(); // 저장된 painter 상태를 복원합니다.

    if (logicalIndex == 0) // 첫 번째 섹션(logicalIndex 0)에만 체크 박스를 그립니다.
    {
        QStyleOptionButton option; // 체크 박스를 그리기 위한 스타일 옵션입니다.

        option.rect = QRect(1, 3, 20, 20); // 체크 박스의 위치와 크기를 설정합니다.

        option.state = QStyle::State_Enabled | QStyle::State_Active; // 체크 박스를 활성화된 상태로 설정합니다.

        if (isChecked_) // 체크 박스의 상태에 따라 스타일 옵션을 설정합니다.
            option.state |= QStyle::State_On; // 체크된 상태
        else
            option.state |= QStyle::State_Off; // 체크되지 않은 상태
        option.state |= QStyle::State_Off; // 이 부분은 불필요한 코드로 보입니다.

        style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, // 체크 박스를 그립니다.
                              &option, // 스타일 옵션을 전달합니다.
                              painter); // painter를 전달합니다.
    }
}

void CheckBoxHeader::mousePressEvent(QMouseEvent* event) // 마우스 클릭 이벤트를 처리하는 함수입니다.
{
    Q_UNUSED(event) // event 변수를 사용하지 않음을 나타냅니다.
    setIsChecked(!isChecked()); // 체크 박스의 상태를 반전시킵니다.

    emit checkBoxClicked(isChecked()); // checkBoxClicked 시그널을 방출하여 체크 박스 상태 변경을 알립니다.
}

void CheckBoxHeader::redrawCheckBox() // 체크 박스를 다시 그리는 함수입니다.
{
    viewport()->update(); // 뷰포트를 업데이트하여 다시 그리도록 합니다.
}

void CheckBoxHeader::setIsChecked(bool val) // 체크 박스의 체크 상태를 설정하는 함수입니다.
{
    if (isChecked_ != val) // 현재 상태와 설정하려는 상태가 다를 경우에만 업데이트합니다.
    {
        isChecked_ = val; // 체크 박스 상태를 업데이트합니다.
        redrawCheckBox(); // 체크 박스를 다시 그립니다.
    }
}


[widget.cpp]

#include "widget.h" // widget.h 헤더 파일을 포함합니다.
#include "ui_widget.h" // ui_widget.h 헤더 파일을 포함합니다. (Qt Designer로 생성된 UI 파일)
#include "checkboxheader.h" // checkboxheader.h 헤더 파일을 포함합니다.
#include <QDebug> // 디버깅 메시지를 출력하기 위한 헤더 파일을 포함합니다.

Widget::Widget(QWidget *parent) : // Widget 클래스의 생성자입니다. 부모 위젯을 인자로 받습니다.
    QWidget(parent), // 부모 클래스인 QWidget의 생성자를 호출합니다.
    ui(new Ui::Widget) // Ui::Widget 클래스의 객체를 생성합니다. (Qt Designer로 생성된 UI)
{
    ui->setupUi(this); // UI를 설정합니다.

    ui->tableWidget->setRowCount(5); // 테이블 위젯의 행 개수를 5로 설정합니다.
    ui->tableWidget->setColumnCount(2); // 테이블 위젯의 열 개수를 2로 설정합니다.

    CheckBoxHeader* header = new CheckBoxHeader(Qt::Horizontal, ui->tableWidget); // CheckBoxHeader 객체를 생성합니다. (수평 방향)
    ui->tableWidget->setHorizontalHeader(header); // 테이블 위젯의 헤더를 CheckBoxHeader로 설정합니다.
    connect(header, &CheckBoxHeader::checkBoxClicked, // CheckBoxHeader의 checkBoxClicked 시그널을 Widget의 checkBoxClicked 슬롯에 연결합니다.
            this,  &Widget::checkBoxClicked); // 이렇게 하면 헤더의 체크 박스를 클릭했을 때 checkBoxClicked 슬롯이 호출됩니다.

    QStringList nameList; // QStringList를 생성합니다.
    nameList << "Notebook" << "Mobile" << "Desktop" << "Keyboard" << "Monitor"; // QStringList에 아이템을 추가합니다.

    for(int i = 0; i < 5 ; i++) // 5개의 행을 생성하고 데이터를 추가합니다.
    {
        ui->tableWidget->insertRow(i); // i번째 행을 삽입합니다.
        QTableWidgetItem *dateItem = new QTableWidgetItem("2025.05.07"); // "2025.05.07" 텍스트를 가진 QTableWidgetItem을 생성합니다.
        dateItem->setCheckState(Qt::Checked); // 아이템의 체크 상태를 Qt::Checked로 설정합니다.

        ui->tableWidget->setItem(i, 0, dateItem); // (i, 0) 위치에 dateItem을 설정합니다.
        ui->tableWidget->setItem(i, 1, new QTableWidgetItem(nameList.at(i))); // (i, 1) 위치에 nameList의 i번째 아이템을 설정합니다.
    }
}

void Widget::checkBoxClicked(bool state) // 헤더의 체크 박스 클릭 시 호출되는 슬롯입니다.
{
    for(int i = 0 ; i < 5 ; i++) // 모든 행의 첫 번째 열 아이템의 체크 상태를 변경합니다.
    {
        QTableWidgetItem *item = ui->tableWidget->item(i, 0); // (i, 0) 위치의 아이템을 가져옵니다.
        if(state) // state가 true이면 (헤더 체크 박스가 체크된 상태)
            item->setCheckState(Qt::Checked); // 아이템의 체크 상태를 Qt::Checked로 설정합니다.
        else // state가 false이면 (헤더 체크 박스가 체크되지 않은 상태)
            item->setCheckState(Qt::Unchecked); // 아이템의 체크 상태를 Qt::Unchecked로 설정합니다.
    }
}

Widget::~Widget() // Widget 클래스의 소멸자입니다.
{
    delete ui; // ui 객체를 삭제합니다.
}

=> Model 을 사용하지 않고 QTableWidget 을 이용해 데이터를 삽입하는 예제를 다루어 볼 것이다. 그리고 첫 번째 Column 에 QCheckBox 위젯을 삽입하는 방법

 

[main.cpp]

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();

    return a.exec();
}