한국어
Qt
 

이전에 간단한 QML 어플리케이션 프로젝트를 생성하고 실행하는 방법을 소개한적이 있다. (QML로 만드는 Hello World)

 

최근 QML 엔진의 최적화가 많이 이뤄지긴 했지만 QML만을 이용해서 어플리케이션을 개발하는 것은 현실적으로 어려운일이며 특히 성능을 중요시 하는 시스템이거나 C++로 작성된 기존 백엔드 API를 이용해야 한다면 C++과의 통합은 꼭 알아야하는 부분이다.

 

아래 다이어그램은 QML과 C++로 작성된 어플리케이션 구조를 설명하는데 Persentation Layer는 QML를 이용한 구현, Business Layer는 Native C++ 또는 Qt C++ API를 통해 구현한다. Business Layer 측에서는 Persentation Layer를 위해 API를 제공해야하며 바로 이부분에 대한 주제를 가지고 이글에서 설명하고자 한다.

qml+cpp.png

 

 

예제를 통해 QML과 C++로 개발하는 방법을 알아보자.

 

아래 소스코드는 기본으로 생성되는 QML 프로젝트의 main.cpp 코드이다.

#include <QGuiApplication>
#include <QQmlApplicationEngine>

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

 

QQmlApplicationEngine 객체를 생성하고 main.qml을 로드시키고 있다.

 

양측 통합의 일반적인 방법은 QQmlApplicationEngine의 객체 즉, QQmlEngine의 context에 QObject를 서브클래싱한 C++클래스 객체를 등록하여 QML에서 등록된 객체에 접근할 수 있게 한다.

 

QQmlApplicationEngine클래스는 QQmlEngine를 서브클래싱하고 있는데 QQmlEngine 클래스는 QML엔진의 root context를 얻을 수 있도록 rootContext() 메서드를 제공한다. 

 

rootContext()는 QQmlContext 포인터를 반환하고 QQmlContext의 setContextProperty()메서드를 통해 C++ 인스턴스를 등록할 수 있다.

 

아래 예제 소스코드를 보자.

QML에서 C++객체의 메서드를 호출하거나 비동기로 어떤 시그널을 처리하는 방법이다. Phonebook 클래스는 QObject를 서브클래싱하였고 setter, getter 메서드를 선언하였다. 여기서 Q_INVOKABLE 매크로에 주목하자. 컴파일 시점에 moc에 의해 표준 C++코드로 재생성되고 Q_INVOKABLE 으로 선언된 클래스의 함수들은 QML에서 호출 할 수 있도록 해준다. 

phonebook.h

#include <QObject>

class Phonebook: public QObject{
    Q_OBJECT
public:
    explicit Phonebook(QObject *parent = nullptr);
    virtual ~Phonebook();

    Q_INVOKABLE void setName(const QString &name);
    Q_INVOKABLE QString name() const;

signals:
    void nameChanged();

private:
    QString m_name;
};

 

함수의 구현부분은 단순하다. setName함수는 새로 입력된 name을 바꾸고 시그널을 방출한다.

phonebook.cpp

#include "phonebook.h"

Phonebook::Phonebook(QObject *parent)
    :QObject(parent),
      m_name("")
{

}

Phonebook::~Phonebook()
{

}

void Phonebook::setName(const QString &name)
{
    if(name != m_name){
        m_name = name;
        emit this->nameChanged();
    }
}

QString Phonebook::name() const
{
    return m_name;
}

 

main함수에서는 Phonebook 객체를 생성하고 QML 엔진 context에 "phonebook" 이라는 이름으로 등록한다.

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "phonebook.h"

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    Phonebook pb;
    engine.rootContext()->setContextProperty("phonebook", &pb);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

 

이제 QML에서 "phonebook" 이라는 이름으로 C++객체의 메서드를 호출 할 수 있게된다.

 

main.qml

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Dialogs 1.3
import QtQuick.Controls 2.4

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    Dialog{
        id: inputDialog
        title: qsTr("이름을 입력하세요.")
        anchors.centerIn: parent
        height: 200
        width: 300
        modal: Qt.ApplicationModal
        contentItem: TextField{
            width: 100
            height: 50
            font.pixelSize: 25
            horizontalAlignment: TextInput.AlignHCenter
        }
        standardButtons: Dialog.Ok | Dialog.Cancel

        onAccepted: {
            if(contentItem.text.length){
                phonebook.setName(contentItem.text)
            }
        }
    }

    Connections{
        target: phonebook
        onNameChanged:{
            name.text = phonebook.name()
        }
    }

    Row{
        height: 50
        anchors.centerIn: parent

        Text {
            id: name
            width: 150
            font.pixelSize: 20
            font.bold: true
            anchors.verticalCenter: parent.verticalCenter
            elide: Text.ElideRight
        }
        Button{
            width: 70
            height: 50
            text: qsTr("입력")
            onClicked: {
                inputDialog.open()
            }
        }
    }
}

 

다이얼 로그를 통해 사용자로부터 문자열을 입력받고 setName() 메서드를 통해 값을 설정한다. 값이 바뀔때 발생하는 nameChanged시그널을 연결하여 값이 phonebook 의 name 값이 바뀌면 name() 메서드를 통해 값을 가져와 QML의 text에 새로운 값을 설정하게 된다.

번호 제목 글쓴이 날짜 조회 수
공지 Qt프로그래밍(QtQuick) Beginner를 위한 글 읽는 순서 운영자 2019.01.05 85358
76 [Qt News] Qt for Python을 위한 기술 비전 j2doll 2019.08.20 1615
75 열거형(enum)을 QML에서 사용하는 방법과 문자열(QString)로 얻기 makersweb 2019.08.20 3833
74 [Qt News] Qt 6 기술 비전 (Technical vision for Qt 6) [2] j2doll 2019.08.08 2096
73 [Qt News] Qt6 Git 개발 초기 단계 시작하기 j2doll 2019.08.02 2315
72 [Qt] Google Play의 향후 요구 사항을 준수하는 방법 [2] j2doll 2019.07.29 968
71 Qt기반의 오픈소스 프로젝트들 - 2 운영자 2019.07.21 3991
70 QML, 이미지, 폰트등을 바이너리 리소스로 만들기 makersweb 2019.06.24 3458
69 Qt Creator에서 임베디드 장치로 deploy설정(Custom Process Step) file makersweb 2019.06.15 2178
68 Qt Quick Controls 2사용 및 스타일 설정 file makersweb 2019.06.07 6218
67 QML 강좌 - 동적 Listing (ListView) file makersweb 2019.06.01 10029
66 QtInstallerFramework로 온라인 설치프로그램(Online Installer)만드는 방법 [4] file makersweb 2019.05.28 6226
65 QtCreator Design으로 GUI만들기 (QML로 만드는 Hello World -2) [1] file makersweb 2019.05.26 14772
64 QML에서 멀티 스레드(multithreading) 프로그래밍 file makersweb 2019.05.25 2640
63 QtSerialPort를 사용한 시리얼(Serial)통신 [3] makersweb 2019.05.21 11759
62 Qt기반의 오픈소스 프로젝트들 makersweb 2019.05.15 5370
61 Q_D매크로와 d-pointer file makersweb 2019.05.07 744
60 가상키보드(Qt Virtual Keyboard)를 사용하는 방법 [32] file makersweb 2019.05.03 219972
59 Windows에서 Qt D-Bus를 사용하여 프로세스간 통신(IPC) file makersweb 2019.05.02 4433
58 QML과 QtQuick 모듈 개념과 기본 타입들 makersweb 2019.04.26 13325
57 QML 전역 객체 (Global Object) file makersweb 2019.04.10 1039