[QT]example3-QML+C++(1)

๐Ÿ‘‰ QML์„ ํ†ตํ•ด์„œ ์ž…๋ ฅ๋œ ๋‚ด์šฉ์„ ์ง์ ‘ ํ™”๋ฉด์— ์ถœ๋ ฅ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
You can directly display the content entered through QML on the screen.

๐Ÿ‘‰ ์ด ๋ถ€๋ถ„์€ ์ถœ๋ ฅ์€ C++์ด ์ˆ˜ํ–‰ํ•˜๊ณ  C++์ฝ”๋“œ๋ฅผ QML์— ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ์„ค๋ช…์ž…๋‹ˆ๋‹ค.
This part explains how the output is done in C++ and how to connect the C++ code to QML.

๐Ÿ“1.ํ”„๋กœ์ ํŠธ / Project


๐Ÿ“ 2.C++ ๊ณผ qml ์—ฐ๊ฒฐ / Connecting C++ and QML

โœ”๏ธ myconnector.h์™€ myconnector.cppํŒŒ์ผ์— qml์—์„œ ์‚ฌ์šฉํ•  ์‹œ๊ทธ๋„๊ณผ ํ•จ์ˆ˜๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
Define the signals and functions to be used in QML within the myconnector.h and myconnector.cpp files.

โœ”๏ธ ์•„๋ž˜์˜ ์ฝ”๋“œ๋Š” ํ—ค๋”ํŒŒ์ผ์˜ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.
The code below is from the header file.

#ifndef MYCONNECTOR_H
#define MYCONNECTOR_H

// MyConnector.h

#include <QObject>
#include <QString>

class MyConnector : public QObject {
    Q_OBJECT

public:
    explicit MyConnector(QObject *parent = nullptr);

    Q_INVOKABLE void updateText(const QString &text1, const QString &text2);

signals:
    void resultReady(const QString &result);
};

#endif // MYCONNECTOR_H

โœ”๏ธ updateText๋Š” qml(Screen01.qml)์—์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
updateText is used in QML (Screen01.qml) as shown below.

        Button {
            id: button
            text: qsTr("Button")
            // === ์ฝ”๋“œ์ถ”๊ฐ€:ํด๋ฆญ ์ด๋ฒคํŠธ ===
            // === Add code:Click Event ===
            onClicked: myConnector.updateText(textEdit.text, textEdit2.text)

        }

โœ”๏ธ QML์— ๊ฐ์ฒด ๋“ฑ๋กํ•˜๊ธฐ(C++๊ณผ QML ์—ฐ๊ฒฐ)
Registering Objects in QML (Connecting C++ and QML)

— Main.cpp์— ์•„๋ž˜์ฒ˜๋Ÿผ myConnectorํด๋ž˜์Šค๋ฅผ QML๊ฐ์ฒด๋กœ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
You can register the myConnector class as a QML object in Main.cpp as shown below.

//โœ… QML์— C++ ๊ฐ์ฒด๋ฅผ ๋“ฑ๋กํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”
// Required to register C++ objects in QML
#include <QQmlContext>

//โœ… MyConnector ํด๋ž˜์Šค ์„ ์–ธ ํฌํ•จ
// Include MyConnector class declaration
#include "MyConnector.h"

... ์ค‘๋žต / Omitted ...    

MyConnector connector;
engine.rootContext()->setContextProperty("myConnector", &connector);

โœ”๏ธ QML์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ / Using in QML

— onResultReady๋Š” myConnector๋ผ๋Š” ๊ฐ์ฒด๊ฐ€ ๊ฐ€์ง„ resultReady๋ผ๋Š” ์‹œ๊ทธ๋„(Signal) ์ด๋ฆ„ ์•ž์— on์„ ๋ถ™์—ฌ์„œ ๋งŒ๋“  ์‹œ๊ทธ๋„ ํ•ธ๋“ค๋Ÿฌ(์Šฌ๋กฏ)์ž…๋‹ˆ๋‹ค.
onResultReady is a signal handler (or slot) created by prefixing “on” to the name of the resultReady signal belonging to the object named myConnector.

1)Screen01.qml(on+ResultReady)

        Connections {
            target: myConnector
            function onResultReady(result) {
                text1.text = result
            }
        }

2)Myconnector.h์— ์‹œ๊ทธ๋„ ์„ ์–ธ
Declare the signal in Myconnector.h

signals:
    void resultReady(const QString &result);
};

3)Myconnector.cpp์— ์‹œ๊ทธ๋„ ๋ฐœ์ƒ
Signal generation in Myconnector.cpp

void MyConnector::updateText(const QString &text1, const QString &text2) {
    QString combined = text1 + " " + text2;
    emit resultReady(combined);
}

๐Ÿ“3.์ฝ”๋“œ์„ค๋ช… / Code Description

โœ”๏ธMyconnector.h

#ifndef MYCONNECTOR_H
#define MYCONNECTOR_H

// MyConnector.h

#include <QObject>
#include <QString>

class MyConnector : public QObject {
    Q_OBJECT

public:
    explicit MyConnector(QObject *parent = nullptr);

    Q_INVOKABLE void updateText(const QString &text1, const QString &text2);

signals:
    void resultReady(const QString &result);
};

#endif // MYCONNECTOR_H

— ํ—ค๋”๊ฐ€ ๋‹ค๋ฅธ ํด๋ž˜์Šค ํŒŒ์ผ์— ๋‘๋ฒˆ ์‚ฝ์ž…๋˜๋ฉด ์ปดํŒŒ์ผ ์˜ค๋ฅ˜๊ฐ€ ๋‚ฉ๋‹ˆ๋‹ค.
If a header is inserted twice in different class files, a compilation error occurs.

— ์œ„์˜ ์ฝ”๋“œ์—์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•˜๋ฉด ์ค‘๋ณต ์‚ฝ์ž… ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
If you write the code above as follows, a duplicate insertion error will not occur.

— ์ด๋ฏธ ํ—ค๋”๊ฐ€ C++ ํด๋ž˜์Šค ํŒŒ์ผ์— ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉด ๋ฌด์‹œํ•ฉ๋‹ˆ๋‹ค.
If the header is already included in the C++ class file, it is ignored.

 #ifndef MYCONNECTOR_H
 #define MYCONNECTOR_H

  ์ฝ”๋“œ / CODE

 #endif // MYCONNECTOR_H 

โœ”๏ธqml/Screen01.qml


import QtQuick
import QtQuick.Controls


Rectangle {
    // id: rectangle
    // width: Constants.width
    // height: Constants.height

    // color: Constants.backgroundColor

    // ์ƒ์ˆ˜ ์ง์ ‘ ์„ ์–ธ. / Direct declaration of constants
    property int widthConst: 1920
    property int heightConst: 1080
    property color backgroundColorConst: "#EAEAEA"

    width: widthConst
    height: heightConst
    color: backgroundColorConst

    Column {
        id: column
        width: 200
        anchors.centerIn: parent
        spacing: 10

        Rectangle {
            width: 180
            height: 30
            color: "white"
            border.color: "black"

            TextEdit {
                id: textEdit
                anchors.fill: parent
                width: 80
                height: 20
                text: qsTr("์ž…๋ ฅ1/input1")
                font.pixelSize: 12
            }
        }

        Rectangle {
            width: 180
            height: 30
            color: "white"
            border.color: "black"

            TextEdit {
                id: textEdit2
                width: 80
                height: 20
                text: qsTr("์ž…๋ ฅ2/input2")
                font.pixelSize: 12
            }
        }

        Text {
            id: text1
            text: qsTr("๊ฐ’์ถœ๋ ฅ/Print Value")
            font.pixelSize: 12
        }

        Button {
            id: button
            text: qsTr("Button")
            // === ์ฝ”๋“œ์ถ”๊ฐ€:ํด๋ฆญ ์ด๋ฒคํŠธ ===
            // === Add code:Click Event ===
            onClicked: myConnector.updateText(textEdit.text, textEdit2.text)

        }
        // === ์ฝ”๋“œ์ถ”๊ฐ€:์—ฐ๊ฒฐ ===
        // === Add code:connection ===
        Connections {
            target: myConnector
            function onResultReady(result) {
                text1.text = result
            }
        }

    }
}

โœ”๏ธ main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>

// QML์— C++ ๊ฐ์ฒด๋ฅผ ๋“ฑ๋กํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”
// Required to register C++ objects in QML
#include <QQmlContext>

// MyConnector ํด๋ž˜์Šค ์„ ์–ธ ํฌํ•จ
// Include MyConnector class declaration
#include "MyConnector.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;

    QObject::connect(
        &engine,
        &QQmlApplicationEngine::objectCreationFailed,
        &app,
        []() { QCoreApplication::exit(-1); },
        Qt::QueuedConnection);

    // === ์ฝ”๋“œ ์ถ”๊ฐ€ / Add code ===
    MyConnector connector;
    engine.rootContext()->setContextProperty("myConnector", &connector);

    // === ์—ฌ๊ธฐ๊นŒ์ง€ / Up to here ===
    engine.loadFromModule("example3", "Main");

    return app.exec();
}

โœ”๏ธ main.qml

import QtQuick

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

    // UI ํŒŒ์ผ ๋กœ๋”ฉ / UI file loading
    Loader {
        anchors.fill: parent
        source: "qml/Screen01.qml"
    }
}

โœ”๏ธ Myconnector.cpp

// MyConnector.cpp
#include "MyConnector.h"

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

void MyConnector::updateText(const QString &text1, const QString &text2) {
    QString combined = text1 + " " + text2;
    emit resultReady(combined);
}

โœ”๏ธ CmakeLists.txt

โœ”๏ธ ์•„๋ž˜์˜ ๊ตต์€ ๊ธ€์”จ๊ฐ€ ํŒŒ์ผ ์ถ”๊ฐ€๋œ ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.
The bold text below is the added portion of the file.

โœ”๏ธ ๋ฐ”๋กœ ์•„๋ž˜์˜ ์ฝ”๋“œ๋Š” ์ง์ ‘ ์ถ”๊ฐ€ํ•œ ์ฝ”๋“œ ์ž…๋‹ˆ๋‹ค.
The code right below is the code I added myself.

qml/Screen01.qml

โœ”๏ธ๋ฐ”๋กœ ์•„๋ž˜์˜ ์ฝ”๋“œ๋Š” ์ž๋™์œผ๋กœ ์ˆ˜์ •๋œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค
The code directly below is automatically corrected.

โœ”๏ธQT Creator์˜ Souce Files์—์„œ ๋งˆ์šฐ์Šค ์˜ค๋ฅธ์ชฝ ํด๋ฆญ,Add New๋ฅผ ์‚ฌ์šฉํ•œ ๊ฒฝ์šฐ ์ž๋™์œผ๋กœ ์•„๋ž˜์˜ ๋‚ด์šฉ์ด ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค.
If you right-click on Source Files in QT Creator and use Add New, the following will be added automatically.

โœ”๏ธ์—ฌ๊ธฐ์„œ๋Š” ํด๋ž˜์Šค ํŒŒ์ผ๊ณผ ํ—ค๋”ํŒŒ์ผ์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
Here we have added the class file and header file.

SOURCES MyConnector.h
SOURCES MyConnector.cpp

โœ”๏ธ CmakeLists์ „์ฒด ์ฝ”๋“œ / CmakeLists Full Code

cmake_minimum_required(VERSION 3.16)

project(example3 VERSION 0.1 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Qt6 REQUIRED COMPONENTS Quick)

qt_standard_project_setup(REQUIRES 6.8)

qt_add_executable(appexample3
    main.cpp
)

qt_add_qml_module(appexample3
    URI example3
    VERSION 1.0
    QML_FILES
        Main.qml
        qml/Screen01.qml
        SOURCES MyConnector.h
        SOURCES MyConnector.cpp
)

# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
# If you are developing for iOS or macOS you should consider setting an
# explicit, fixed bundle identifier manually though.
set_target_properties(appexample3 PROPERTIES
#    MACOSX_BUNDLE_GUI_IDENTIFIER com.example.appexample3
    MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
    MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
    MACOSX_BUNDLE TRUE
    WIN32_EXECUTABLE TRUE
)

target_link_libraries(appexample3
    PRIVATE Qt6::Quick
)

include(GNUInstallDirs)
install(TARGETS appexample3
    BUNDLE DESTINATION .
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

3.์‹คํ–‰ / Run

Leave a Reply