QT - C++ ↔ QML connect

w99hyun·2025년 11월 12일

QT Framework

목록 보기
2/4

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.")
        }
    }
}

0개의 댓글