© EVILEG 2015-2018
Рекомендует хостинг
TIMEWEB

QML - Урок 004. Сигналы и слоты в Qt QML

connections, example, qml, qml урок, qt, qt qml, qt урок, signal qml, slot qml, сигналы и слоты qml

А вот мы и добрались до передачи данных между слоем QML и слоем C++. Честно говоря, принцип настолько же простой, как и просто использование сигналов и слотов в одном слое C++. Особенно в Qt 5.5.

Пример будет показан на основе программного кода из предыдущего урока , где мы создали диалоговое окно. Но скриншотов примера работы на Android не будет показано, но заверяю Вас - Всё работает как швейцарские часы.

Структура проекта с QML

структура проекта с qml

По сравнению с предыдущим уроком, у нас появились некоторые изменения. А именно, добавился новый класс, который будет являться ядром приложения.

  • appcore.h - заголовочный файл ядра приложения;
  • appcore.cpp - файл исходных кодов ядра приложения.

А продолжать работать мы также будем с QQMLApplicationEngine. Нужно будет просто взять от движка QML контекст и загрузить в него объект нового класса, от которого будут поступать сигналы и в который будут передаваться данные.

appcore.h

Заголовочный файл нашего класса прост, как три копейки. В нём есть один один счётчик (переменная типа int ), слот, который будет увеличивать счётчик на один и запускать сигнал, который также один в классе и который будет передавать значение счетчика в QML-интерфейс.

#ifndef APPCORE_H
#define APPCORE_H

#include <QObject>

class AppCore : public QObject
{
    Q_OBJECT
public:
    explicit AppCore(QObject *parent = 0);

signals:
    // Сигнал для передачи данных в qml-интерфейс
    void sendToQml(int count);

public slots:
    // Слот для приёма данных из qml-интерфейса
    void receiveFromQml();

private:
    int count;  // Счетчик, которым будем оперировать
};

#endif // APPCORE_H

appcore.cpp

Ну и логика класса в его исходном коде.

#include "appcore.h"

AppCore::AppCore(QObject *parent) : QObject(parent)
{
    count = 0;
}

void AppCore::receiveFromQml()
{
    count++;
    emit sendToQml(count);
}

main.cpp

А вот здесь уже будем подключать к нашему классу интерфейс, написанный на QML.

#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "appcore.h"

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

    QQmlApplicationEngine engine; // Создаём движок qml

    AppCore appCore;    // Создаём ядро приложения
    QQmlContext *context = engine.rootContext();    // Создаём корневой контекст
    /* Загружаем объект в контекст для установки соединения,
     * а также определяем имя, по которому будет происходить соединение
     * */
    context->setContextProperty("appCore", &appCore);

    // И загружаем в него исходники qml
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

main.qml

Приведу часть кода, чтобы не отвлекать Вас от главного. А именно, соединение в QML слое осуществляется с помощью объекта Connections , в качестве target которого указывается Ваш класс установленный в контекст. Обращение осуществляется по текстовому наименованию, которое загружается в контекст движка QML вместе с самим объектом.

Чтобы получать сигналы от слоя C++ необходимо в Connections прописать функцию, которая будет именоваться практически так же, как и сигнал целевого объекта, но начинаться будет с on и далее имя сигнала с заглавной буквы. То есть логика следующая

  • signalToQml - на C++
  • onSignalToQml - на QML

А вот вызов Слота будет происходить несколько иначе. Например, у нас имеется объект класса в C++ , к которому мы обращаемся по имени appCore (объявлен целью в Connections ). А далее вызываем функцию слот. То есть следующим образом: appCore.slotSomething(count).

Вызов слота в данном коде осуществляется по нажатию кнопки OK в диалоге, а по Cancel вызова не происходит. При этом, объект appCore в C++ коде увеличивает счётчик на один и вызывает сигнал, чтобы поместить значение счётчика в текстовый лейбл приложения.

import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Dialogs 1.2

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World") // Ну как же без Приветствия Миру
    color: "white"

    /* С помощью объекта Connections
     * Устанавливаем соединение с классом ядра приложения
     * */
    Connections {
        target: appCore // Указываем целевое соединение
        /* Объявляем и реализуем функцию, как параметр
         * объекта и с имененем похожим на название сигнала
         * Разница в том, что добавляем в начале on и далее пишем
         * с заглавной буквы
         * */
        onSendToQml: {
            labelCount.text = count // Устанавливаем счётчик в текстовый лейбл
        }
    }

    MainForm {
        // Растягиваем объект главного окна по всему родительскому элементу
        anchors.fill: parent

        // Создадим текстовый лейбл
        Text {
            id: labelCount
            // А также установим его визуальные параметры
            anchors.left: parent.left
            anchors.right: parent.right
            anchors.top: parent.top
            height: 300
            verticalAlignment: Text.AlignVCenter
            horizontalAlignment: Text.AlignHCenter
            // Первым текстом будет традиционный Хеллоу Ворлд!
            text: "Hello, World !!!"
        }

        // Стилизуем первую кнопку
        button1.style: ButtonStyle {
            // Программный код из предыдущего урока
        }

        // Стилизуем вторую кнопку
        button2.style: ButtonStyle {
            // Программный код из предыдущего урока
        }

        // Запускаем диалог по нажатию любой из кнопок в главном окне
        button1.onClicked: dialogAndroid.open();
        button2.onClicked: dialogAndroid.open();

        // Создаём объект диалогового окна
        Dialog {
            id: dialogAndroid
            /* Когда деплоите под Android-устройства,
             * обязательно закоментируйте эти две строки,
             * иначе словите глюки в работе устройства
             */
            width: 600  // Задаём ширину диалога, работает на десктопе, но на Android не сработает
            height: 500 // Задаём высоту диалога, работает на декстопе, но на Android не сработает

            // Создаём содержимое диалогового окна
            contentItem: Rectangle {
                width: 600          // Устанавливаем ширину, необходимо для Android-устройства
                height: 500         // Устанавливаем высоту, необходимо для Android-устройства
                color: "#f7f7f7"    // Задаём цвет

                // Область для сообщения диалогового окна
                Rectangle {
                    // Программный код из предыдущего урока
                }

                // Создаём горизонтальный разделитель с помощью Rectangle
                Rectangle {
                    id: dividerHorizontal
                    // Программный код из предыдущего урока
                }

                /* Создаём подложку для кнопок в виде объекта Строки
                 * В данном объекте для объектов детей не работают некоторые параметры
                 * anchors, кроме параметров anchors.top и anchors.bottom
                 */
                Row {
                    id: row
                    // Программный код из предыдущего урока

                    Button {
                        id: dialogButtonCancel

                        // Программный код из предыдущего урока

                        // По нажатию кнопки закрываем диалог
                        onClicked: dialogAndroid.close()
                    }

                    // Создаём разделитель между кнопками шириной в 2 пикселя
                    Rectangle {
                        id: dividerVertical
                        // Программный код из предыдущего урока
                    }

                    Button {
                        id: dialogButtonOk

                        // Программный код из предыдущего урока

                        // По нажатию кнопки закрываем диалог
                        onClicked: {
                            /* Прежде, чем закрывать диалог по OK кнопке,
                             * отправим данные в слот ядра приложения
                             * */
                            appCore.receiveFromQml()
                            // А потом и закроем диалог
                            dialogAndroid.close()
                        }
                    }
                }
            }
        }
    }
}

Итог

В результате получаем простое взаимодействие между C++ и QML на основе всё тех же сигналов и слотов. Ну  а результат работы приложения Вы можете увидеть в видеоуроке.

Комментарии

22 января 2018 г. 8:43

Не понял, как будет передаваться значение count в QML, если нигде он не описан через Q_PROPERTY

22 января 2018 г. 8:56

Так и будет передаваться. Это аргумент сигнала.

void sendToQml(int count);
Видите сигнатуру? аргумент называется count . Вот он и передаётся.
А описывать в Q_PROPERTY его не нужно. Не обязательно всё описывать через Q_PROPERTY . Зачастую достаточно объявлять метод сигналом, слотом или Q_INVOKABLE.
5 марта 2018 г. 17:19

Статья интересная, как и реализация, но в упор ругается на

Qt\untitled1\main.cpp:21: ошибка: 'appCore' was not declared in this scope
appCore appCore;
^
5 марта 2018 г. 20:47

А почему у вас название класса и имя переменной одинаковые?

appCore appCore;
В моём примере название класса с заглавной буквы AppCore
6 марта 2018 г. 16:52
AppCore appCore;
QQmlContext *context = engine.rootContext();
context->setContextProperty("appCore", &appCore);
Вставил эти строки, ошибка никуда не делась, заголовочный файл подключен. Почему не видит класс?
6 марта 2018 г. 18:49

Проект ваш смотреть нужно, что ещё не доделали, либо просто пересобрать билд для начала, возможно, что при создании файлов класса не обновилась информация для qmake. То есть как минимум перезапустить qmake требуется.

6 июля 2018 г. 19:46

Огромное спасибо за статью.

В очередной раз уже выручают ваши подсказки. Если не знаю как реализовать - знаю у кого найти ответ.
Дизайнеры показали проект интерфейса, была попытка через StyleSheet, но возможностей там меньше, в итоге изучил новую для себя технологию, и проект представленный дизайнерами выглядит и работает именно так как они его себе вообразили. Из меня дизайнер фиговый и таких красивых интерфейсов я бы не нарисовал никогда. В итоге технология показала свою замечательность на 90% (остальное еще стоит изучить и возрадоваться).




10 июля 2018 г. 16:53

Спасибо за отзыв!

Комментарии

Только авторизованные пользователи могут оставлять комментарии.
Пожалуйста, Авторизуйтесь или Зарегистрируйтесь
15 июля 2018 г. 20:20
igorpodoba

C++ - Тест 003. Условия и циклы

  • Результат 92баллов,
  • Очки рейтинга8
15 июля 2018 г. 20:17
igorpodoba

C++ - Тест 002. Константы

  • Результат 91баллов,
  • Очки рейтинга8
14 июля 2018 г. 7:47
igorpodoba

C++ - Тест 003. Условия и циклы

  • Результат 71баллов,
  • Очки рейтинга1
Последние комментарии
14 июля 2018 г. 18:49
Евгений Легоцкой

Qt/C++ - Урок 049. QTranslator - динамический перевод мультиязычного приложения на Qt

У меня на руках есть один проект, где какие-то потуги с переводами и подключением этого добра в CMAKE делались.Но там файл перевода добавляется прямо в ресурсы проекта. То есть бинарных qm файл...
14 июля 2018 г. 18:35
Евгений Легоцкой

Django - Урок 036. Как добавить аутентификацию через социальные сети. ВКонтакте

Не, не будет, в данной батарейке уже есть необходимый функционал по разрулированию этой проблемы. Аутентификации из разных социальных сетей будут сливаться на один аккаунт. Так что всё нормаль...
14 июля 2018 г. 4:17
Gerych

Django - Урок 036. Как добавить аутентификацию через социальные сети. ВКонтакте

Мне интересно что будет если в обеих сетях в авторизации одинаковый еmail. Не выведет ли ошибку ?
13 июля 2018 г. 11:55
Arrow

Qt/C++ - Урок 049. QTranslator - динамический перевод мультиязычного приложения на Qt

Хорошая статья. Только один вопрос как это сделать для CMake? Интересует именно запись в CMakeList TRANSLATIONS += QtLanguage_ru.ts CODECFORSRC = UTF-8 П...
Сейчас обсуждают на форуме
14 июля 2018 г. 18:56
Евгений Легоцкой

Как сделать пустое поле в QDateEdit

Слишком много возни, чтобы подробно объяснить, что нужно сделать.... тем более, что у вас ещё зависимость на базу данных... Для начала нужно наследоваться от QCalendarWidget, посколь...
12 июля 2018 г. 15:02
незнаток

Перенос значений таблицы в другую таблицу

void Opisanie::perevod(){ QString mil; int mf = ui->table1->rowCount(); for(int ik = 0; ik < mf; ik++) { QString tu = ui->table1->model()->data(ui...
12 июля 2018 г. 7:46
Евгений Легоцкой

OpenSSL на Windows10

Совсем забыл. Вот в этом посте есть ссылка на скачивание openssl библиотек для msvc-2015
11 июля 2018 г. 16:05
Ruslan Polupan

Наследование от QLineEdit

Из опыта разработки в нашей конторе (для программирование хобби я техподдержка): Если есть возможность переложить логику приложения на базу данных то это лутший вариант. Т.е. использовать по м...

Рекомендуемые страницы