Evgenii Legotckoi
Evgenii LegotckoiҚар. 27, 2017, 2:42 Т.Ж.

QML - Сабақ 034. С++ қолданбалы деңгейінен QML деңгейіне деректер құрылымдарын беру

Qt-дегі QML-дің сөзсіз артықшылықтарының бірі - бұл бағдарлама интерфейсінен серверлік логиканы айтарлықтай күрт бөлуге мүмкіндік береді. Яғни, біз бүкіл серверді C ++ тілінде жазамыз, ал QML тілінде біз тек қажетті нәтижені көрсетеміз.

Сонымен қатар, біз C ++ кодының болуын барынша азайта отырып, ішкі логиканы тек QML тілінде жаза аламыз немесе керісінше OpenGL мүмкіндіктерін пайдалана отырып, C ++ тілінде кейбір элементтердің дизайнын белгілей аламыз. Кодтың QML және C++ бөліктерін backend және frontend қолданбаларына бөлуге қарамастан, бізде үлкен шектеулер жоқ.

Бірақ кейде кодтың QML бөлігіне ақпараты бар бірнеше өрістерді тасымалдайтын кейбір деректер құрылымын тасымалдау қажет болады. Мысалы, мұндай құрылым.

struct Structure 
{
    int m_number;
    QString m_message;
};

Мұндай құрылымды QML-ге көшіру мүмкін емес, бірінші ойға келетін нәрсе - бірнеше дәлелдер жіберетін сигнал sendToQml жасау. Әрбір аргумент құрылымның белгілі бір өрісіне жауапты болады.

void sendToQml(int number, QString message);

Бұл сигнал сіздің класыңызда орналасады, ол белгілі бір ақпаратты QML-ге сандармен, хабарламалармен және т.б. жібереді. Бұл тәсілдің кемшілігі анық, өйткені мұндай өрістер көп болуы мүмкін және бірнеше ондаған дәлелдермен сигнал беру ақымақтық болар еді. Бұл тәсіл деректер құрылымдары үшін емес, шағын ақпаратты жіберу үшін пайдалы болады.


QVariantMap пайдаланудың тағы бір мүмкіндігі бар. Ол QML массивіне жақсы түрлендірілгендіктен, деректерді кілт арқылы алуға болады. Қолдану мысалы ресми Qt құжаттамасында берілген.

Ақпарат QML-ге осылай қабылданады

// MyItem.qml
Item {
    function readValues(anArray, anObject) {
        for (var i=0; i<anArray.length; i++)
            console.log("Array item:", anArray[i])

        for (var prop in anObject) {
            console.log("Object item:", prop, "=", anObject[prop])
        }
    }
}

Осылайша бұл ақпарат C++ тіліне қосылады

// C++
QQuickView view(QUrl::fromLocalFile("MyItem.qml"));

QVariantList list;
list << 10 << QColor(Qt::green) << "bottles";

QVariantMap map;
map.insert("language", "QML");
map.insert("released", QDate(2010, 9, 21));

QMetaObject::invokeMethod(view.rootObject(), "readValues",
        Q_ARG(QVariant, QVariant::fromValue(list)),
        Q_ARG(QVariant, QVariant::fromValue(map)));

Бірақ маған бұл тәсіл ұнамайды, себебі құрылым тасымалдайтын деректермен белгілі бір әрекеттерді орындау қажет болған жағдайда жағдай туындауы мүмкін және QVariantMap-те мұндай әдістер принципті түрде бола алмайды. Иә, мен кілттер мен мәндер жиынтығынан гөрі ақпараттың тұтас көрінісін қалаймын. Егер бұл C ++ кодының ішінде жіберілетін хабардың немесе деректер нысанының қандай да бір түрі болса және ол интегралдық нысан ретінде қарастырылса, онда неге біз оны QVariantMap ішінде тұратын кілт-мән жұптарының жиынына айналдыруымыз керек?

Дұрыс, бұл міндетті емес. Өйткені, C++ тілінен QObject класының объектісіне көрсеткішті QML-ге беруге болады. Және полиморфизм мүмкіндіктерін ескере отырып, мұрагерлік және т.б. сіз QObject сыныбынан мұра ала аласыз және осы нысанға көрсеткішті QObject базалық класына көрсеткіш ретінде QML -ге бере аласыз. Сонымен қатар, бұл жаңа классты Qt мета-нысан жүйесінде MetaType ретінде тіркемей-ақ.

Бұл бізге не береді? Егер сіз қандай да бір сыныптан мұра алсаңыз және мұраланған сыныпқа кейбір әдістер мен өрістерді қоссаңыз, олармен жұмыс істеуге және оларға қоңырау шалуға болатынын түсінеміз, бірақ сізде бір сыныпқа көрсеткіш болуы керек. сәйкес класстың объектісі, яғни егер базалық классқа көрсеткішті алып, оны мұраланған классқа көрсеткіш тағайындасақ, онда код ішінде біз жұмыс істегенде мұраланған класс әдісінің шақыруын жаза алмаймыз. базалық класс көрсеткішімен. Яғни, келесі код компиляцияланбайды.

class A 
{
public:
    A() {}
};

class B : public A
{
public:
    B() {}
    void someMethod();
};

int main(int argc, char *argv[])
{
    A *a = new B();  // Ok. Можно присвоить указатель на наследованный класс указателю на базовый класс
    a->someMethod(); // Ошибка. А вот вызвать метод наследованного класса уже нельзя, базовый класс об это ничего не знает
    return 0;
}

Ал логикалық тұрғыдан алғанда, классты qmlRegisterType арқылы тіркемей, QObject мұрағатының мағынасы жоқ. Алайда мұнда бәрі біз үйренген әдіс емес. Мәселе мынада, егер әдіс сигнал , слот немесе Q_INVOKABLE әдісі немесе кез келген өріс ретінде белгіленсе Q_PROPERTY ретінде белгіленген болса, Qt мета-нысан жүйесі бұл әдіс немесе өріс туралы бұрыннан біледі. Ал әдісті, мысалы, QMetaObject класының статикалық әдісі арқылы шақыруға болады. Осыған байланысты, QObject базалық класының көрсеткіші болса да, бұл әдістерді QML-де шақыруға болады.

Ал енді QObject-тен мұраланған және QML-ге берілетін құрылым мен класстың мысалын қарастырайық.

Container.h

#ifndef CONTAINER_H
#define CONTAINER_H

#include <QObject>

struct Structure : public QObject
{
    explicit Structure(QObject *parent = nullptr);

    int m_number;
    QString m_message;

private:
    Q_OBJECT
    // Сможем обращаться к полям из QML
    // параметр MEMBER указывает, что имеется возможность работать с этим полем и отслеживать его изменение в QML
    Q_PROPERTY(int number MEMBER m_number)
    Q_PROPERTY(QString message MEMBER m_message)

public:
    // А также вызвать в QML этот метод
    Q_INVOKABLE QString getFullInfo() const;
};

class Container : public QObject
{
    Q_OBJECT
    // Сможем обращаться к полям из QML
    Q_PROPERTY(int number READ number WRITE setNumber NOTIFY numberChanged)
    Q_PROPERTY(QString message READ message WRITE setMessage NOTIFY messageChanged)

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

    // А также вызвать в QML этот метод
    Q_INVOKABLE QString getFullInfo() const;

    int number() const;
    QString message() const;

public slots:
    void setNumber(int number);
    void setMessage(QString message);

signals:
    void numberChanged(int number);
    void messageChanged(QString message);

private:
    int m_number;
    QString m_message;
};

#endif // CONTAINER_H

Мұнда біз [ QML ішінде жұмыс істейтін теңшелетін нысан] үшін өрістер мен әдістерді жариялаудың классикалық әдісін көреміз (https://evileg.com/post/296/). Яғни, Q_PROPERTY және Q_INVOKABLE макростары, сондай-ақ сигналдар мен слоттар бар.

Container.cpp

Бастапқы код файлының мазмұны ешқандай сұрақ тудырмауы керек.

#include "Container.h"

Structure::Structure(QObject *parent) : QObject(parent)
{

}

QString Structure::getFullInfo() const
{
    return QString("Full information from Structure %1").arg(m_number);
}

Container::Container(QObject *parent) : QObject(parent)
{

}

QString Container::getFullInfo() const
{
    return QString("Full information from Container %1").arg(m_number);
}

int Container::number() const
{
    return m_number;
}

QString Container::message() const
{
    return m_message;
}

void Container::setNumber(int number)
{
    if (m_number == number)
        return;

    m_number = number;
    emit numberChanged(m_number);
}

void Container::setMessage(QString message)
{
    if (m_message == message)
        return;

    m_message = message;
    emit messageChanged(m_message);
}

Зауыт нысаны

C ++ кодында объектілерді құру және QML-ге көрсеткішті беру ыңғайлылығы үшін мен QML контекстінде тіркелетін арнайы зауыт жасаймын, ол ақпаратпен құрылымдар мен контейнерлерді жылдам жасауға және көрсеткішті қайтаруға мүмкіндік береді. бұл нысандар QML.

Factory.h

#ifndef FACTORY_H
#define FACTORY_H

#include <QObject>

class Factory : public QObject
{
    Q_OBJECT
public:
    explicit Factory(QObject *parent = nullptr);

    Q_INVOKABLE QObject* createContainer(); // Для создания контейнеров
    Q_INVOKABLE QObject* createStructure(); // Для создания структур

private:
    int m_count {0};
    int m_structureCount {0};
};

#endif // FACTORY_H

Factory.cpp

Менің ата-ананы зауытқа орнатқанымды байқайсыз. Көрсеткіштермен жұмыс істегенде және оларды C++ тілінен QML-ге бергенде, көрсеткішті иеленумен нюанстар бар. Егер нысанның ата-анасы болмаса, онда оны QML-де қоқыс жинаушы жоюы мүмкін, нәтижесінде біз C ++ тілінде жарамсыз көрсеткішті аламыз. Толығырақ осы мақалада .

#include "Factory.h"

#include "Container.h"

Factory::Factory(QObject *parent) : QObject(parent)
{

}

QObject* Factory::createContainer()
{
    Container* container = new Container(this);
    container->setNumber(++m_count);
    container->setMessage(QString("Container %1").arg(m_count));
    return container;
}

QObject* Factory::createStructure()
{
    Structure* structure = new Structure(this);
    structure->m_number = ++m_structureCount;
    structure->m_message = QString("Structure %1").arg(m_structureCount);
    return structure;
}

main.cpp

QML контекстінде зауытты тіркеу қалай көрінетінін көрейік. Бұл QML кодының кез келген жерінде зауытқа кіруге мүмкіндік береді.

#include <QGuiApplication>
#include <QQmlContext>
#include <QQmlApplicationEngine>

#include "Factory.h"

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

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

    Factory factory;
    engine.rootContext()->setContextProperty("factory", &factory);

    return app.exec();
}

main.qml

Барлық дайындық операцияларын орындағаннан кейін, бірнеше құрылымдар мен контейнерлерді жасауға тырысайық және олардан ақпарат алу үшін олардың өрістері мен әдістеріне сілтеме жасайық.

import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3

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

    // массив QML, в который можно поместить что угодно, в C++ это будет либо QVariantMap, либо QVariantList
    property var objectsArray: []

    Text {
        id: textView
        clip: true
        anchors {
            top: parent.top
            left: parent.left
            right: parent.right
            bottom: parent.verticalCenter
            margins: 5
        }
    }

    Button {
        id: addOBjectStructure
        text: qsTr("Add Structure")
        anchors {
            right: parent.horizontalCenter
            left: parent.left
            bottom: addOBjectButton.top
            margins: 5
        }

        onClicked: {
            // Добавляем структуру в массив
            objectsArray.push(factory.createStructure())
        }
    }

    Button {
        id: addOBjectButton
        text: qsTr("Add Object")
        anchors {
            right: parent.horizontalCenter
            left: parent.left
            bottom: parent.bottom
            margins: 5
        }

        onClicked: {
            // Добавляем контейнер в массив
            objectsArray.push(factory.createContainer())
        }
    }

    Button {
        text: qsTr("Read info from Objects")
        anchors {
            right: parent.right
            left: parent.horizontalCenter
            bottom: parent.bottom
            margins: 5
        }

        onClicked: {
            // выводим текст из всех объектов массива
            textView.text = ""
            for (var i = 0; i < objectsArray.length; ++i)
            {
                // главное, чтобы все объекты имели методы с одинаковыми названиями
                var str = objectsArray[i].number + " " + objectsArray[i].message + "\n" + objectsArray[i].getFullInfo() + "\n"
                textView.text += str
            }
        }
    }
}

Біз құрылымдар мен контейнерлерді бір массивке орналастырамыз. Мен QVariantMap және QVariantList QML тілінде JavaScript массивіне түрлендірілетінін жоғарыда айттым, бұл оған қандай ақпарат орналастырылғанына мән бермейді. Сондықтан, біз массивтің барлық элементтерін қайталауға тырысқанда және number , хабарлама және getFullInfo() әдістерін шақырғанда, бізде ешқандай проблемалар болмайды. Бұл жағдайда барлық осы әдістер QMetaObject::invokeMethod әдісі арқылы шақырылады, ал егер әдіс тіркелген болса, онда ол шақырылады. Ал массив екі түрлі кластың объектілерін қамтитындықтан, мұнда ең бастысы әдістердің атаулары бірдей болуы керек. Бұл Python тіліндегі үйрек теру әрекетіне өте ұқсас. Әрине, бұл үйрек теру емес. Бұл Qt мета нысандары жүйесінің ерекшелігі.

Нәтиже келесідей болады

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Ол саған ұнайды ма? Әлеуметтік желілерде бөлісіңіз!

BlinCT
  • Қар. 27, 2017, 6:12 Т.Ж.

Отличный пример удобной работы с передачей данных из крестов.

ilya.guzikov
  • Мамыр 20, 2021, 6:33 Т.Ж.

Добрый день. Полезный урок, подскажите как можно передать структуру данных из qml в c++ при помощи QVariantMap. Со стороны qml упаковываю в Map, добавляю несколько пар ключ значение и вызываю матод(с++) в аргумент Q_INVOKABLE метода (c++) вставляю Map. В методе с++ QvariantMap пустой. Подскажите может что-то не так делаю.

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз
OI
  • Ora Iro
  • Жел. 24, 2024, 6:38 Т.Ж.

C++ - Тест 001. Первая программа и типы данных

  • Нәтиже:40ұпай,
  • Бағалау ұпайлары-8
AD

C++ - Тест 004. Указатели, Массивы и Циклы

  • Нәтиже:50ұпай,
  • Бағалау ұпайлары-4
m
  • molni99
  • Қаз. 26, 2024, 1:37 Т.Ж.

C++ - Тест 004. Указатели, Массивы и Циклы

  • Нәтиже:80ұпай,
  • Бағалау ұпайлары4
Соңғы пікірлер
ИМ
Игорь МаксимовҚар. 22, 2024, 11:51 Т.Ж.
Django - Оқулық 017. Теңшелген Django кіру беті Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii LegotckoiҚаз. 31, 2024, 2:37 Т.Қ.
Django - Сабақ 064. Python Markdown кеңейтімін қалай жазуға болады Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZEҚаз. 19, 2024, 8:19 Т.Ж.
Qt Creator көмегімен fb3 файл оқу құралы Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь МаксимовҚаз. 5, 2024, 7:51 Т.Ж.
Django - Сабақ 064. Python Markdown кеңейтімін қалай жазуға болады Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas5Шілде 5, 2024, 11:02 Т.Ж.
QML - Сабақ 016. SQLite деректер қоры және онымен QML Qt-та жұмыс істеу Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Енді форумда талқылаңыз
Evgenii Legotckoi
Evgenii LegotckoiМаусым 24, 2024, 3:11 Т.Қ.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey1Қар. 15, 2024, 6:04 Т.Ж.
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProjectМаусым 4, 2022, 3:49 Т.Ж.
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9AnonimҚаз. 25, 2024, 9:10 Т.Ж.
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Бізді әлеуметтік желілерде бақылаңыз