2025년 11월 12일

QML에서 C++ 객체를 사용하려면?
- C++ 클래스를 QML 타입으로 등록한다.
- QML_ELEMENT 매크로 등록을 통해 QML 타입으로 등록하면, QML에서 C++의 함수나 변수에 접근이 가능하다.
- 헤더에서 함수 원형을 선언할 때, Q_INVOKABLE 매크로를 앞에 붙여주면 QML에서 직접 해당 함수를 호출할 수 있다.
- QT5 에서는
main.cpp의 main 함수에서 qmlRegister가 필요하다
...
qmlRegisterType<cpp 클래스명>("프로젝트명", 0, 1, "{QML에서 사용할 네임}");
qmlRegisterType<MessageProcessor>("Course", 0, 1, "MessageProcessor");
...
이후, 해당 cpp 파일을 쓸 qml파일에서
import {프로젝트명} {버전}을 통해 QML에서 cpp클래스에 정의된 함수를 사용할 수 있다.
import Course 0.1
Q_PROPERTY
C++ 클래스의 속성을 QML이나 다른 C++ 클래스에서 사용할 수 있게 노출
QML에서 해당 속성의 값을 읽고 쓸 수 있게 되며, 속성 값이 변경될 때 알림을 받을 수 있음
Q_PROPERTY(Type name READ readFunction WRITE writeFunction NOTIFY notifySignal) //Type: 속성(프로퍼티)의 데이터타입 //name: 속성의 이름 //READ: 속성 값을 읽는 함수 //WRITE: 속성 값을 설정하는 함수 (선택) //NOTIFY: 속성 값이 변경될 때 발생하는 시그널 (선택)CONST 키워드
- 해당 속성이 상수임을 나타냄.
Q_PROPERTY(QString version READ version CONSTANT)
- 해당 속성이 상수임을 나타냄.
MEMBER 키워드
- 별도의 읽기/쓰기 함수를 정의하지 않아도 자동으로 읽기/쓰기 제공
Q_PROPERTY(QString name MEMBER m_name NOTIFY nameChanged)
- 별도의 읽기/쓰기 함수를 정의하지 않아도 자동으로 읽기/쓰기 제공
NOTIFY 시그널
- 속성 값이 변경될 때 발생하는 시그널을 지정
- 속성 값의 변경을 모니터링 할 수 있음
- 데이터 바인딩이나 모델-뷰 아키텍처에서 매우 유용
Q_INVOKABLE
- C++ 클래스의 멤버 함수를 QML에서 호출할 수 있는 메소드로 노출
- 비즈니스 로직은 C++ 에서 처리하고, QML에서는 View만 제공하도록 함
QObject
- QObject는 부모-자식 관계를 통해 객체들을 트리 형태로 관리함
- 부모 객체가 소멸되면 자식 객체도 모두 소멸되어 메모리 누수를 방지함
- 스레드를 사용할 때는 객체가 생성된 스레드와 동일한 스레드에서 접근해야 함
QAbstractItemModel
- 프록시 모델, 리스트 모델, 테이블 모델 등을 제공하는 모델 원형
- 이 클래스를 상속받아 만들어진 여러 모델이나 커스텀 모델을 통해 C++에서 데이터를 관리하고, QML의 UI에서 표현할 수 있음
QAbstractListModel
- Custom Model을 통한 ListView 생성 예제
/* MyModel.h */
#pragma once
#include <QAbstractListModel>
class MyItem {
public:
QString name;
QString description;
};
class MyModel : public QAbstractListModel
{
Q_OBJECT
public:
enum ItemRoles {
NameRole = Qt::UserRole + 1,
DestiptionRole,
};
explicit MyModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
//데이터 추가 함수
Q_INVOKABLE void addItem(const QString &name, const QString &description);
protected:
QHash<int, QByteArray> roleNames() const override;
private:
QList<MyItem> m_items;
};/* MyModel.cpp */
#include "mymodel.h"
MyModel::MyModel(QObject *parent)
: QAbstractListModel{parent}
{
}
QVariant MyModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() < 0 || index.row() >= m_items.count())
{
return QVariant();
}
const MyItem &item = m_items[index.row()];
switch(role) {
case NameRole:
return item.name;
case DestiptionRole:
return item.description;
default:
return QVariant();
}
}
int MyModel::rowCount(const QModelIndex &parent) const
{
return m_items.count();
}
void MyModel::addItem(const QString &name, const QString &description)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
MyItem item;
item.name = name;
item.description = description;
m_items << item;
endInsertRows();
}
// beginInsertRows(), endInsertRows() 사이에 있는 아이템에 변화가 생길 경우 UI가 업데이트됨
//RoleEnum 값과 매칭되는 name(qml에서 접근할) 매핑
QHash<int, QByteArray> MyModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[NameRole] = "name";
roles[DestiptionRole] = "description";
return roles;
}/* main.qml */
MyModel {
id: myModel
}
ListView {
id: listView
anchors.fill: parent
model: myModel
ScrollBar.vertical: ScrollBar {
id: verticalScrollBar
width: 14
policy: ScrollBar.AlwaysOn
}
//List Item Preset
delegate: Item {
height: column.height + 10
Column {
id: column
Text {
text:model.name
font.bold: true
}
Text {
text:model.description
color: "lightpink"
}
}
}
Component.onCompleted: {
// 어플리케이션이 시작될 때 항목 추가
myModel.addItem("Item1", "This is the first item.")
for (var i = 0; i < 20; i++)
{
myModel.addItem("item" + i, "This is " +i+ " item.")
}
}
}