Evgenii Legotckoi
Evgenii LegotckoiOct. 15, 2015, 12:05 p.m.

QML - Lesson 004. Signals and Slots in Qt QML

And we got to transfer data between a layer of QML and C ++ layer. Frankly speaking, the principle is as simple as just using signals and slots in a single layer C ++. Especially in Qt 5.5.

An example will be shown on the basis of the code from the previous tutorial , where we have created a dialog box. But screenshots example works on Android will not be shown, but I assure you - Everything works like a Swiss watch.

Project Structure

Compared to the previous lesson, we had some changes. Namely, it adds a new class, which will be the core of the application.

  • appcore.h - application core file header;
  • appcore.cpp - application core source file.

And we continue to work and be with QQMLApplicationEngine. You will need to just take from the engine QML context and download it to a new class of object from which the signals in which data will be transmitted will be received.


appcore.h

Our class header file is as simple as a penny. In it there is a one meter (variable of type int), a slot that will increase the counter by one and run signal, which is also one in the class and which will transmit the counter value in QML-interface.

#ifndef APPCORE_H
#define APPCORE_H

#include <QObject>

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

signals:
    // Signal to transmit data to interface qml-interface
    void sendToQml(int count);

public slots:
    // The slot for the receiving of data from the QML-interface
    void receiveFromQml();

private:
    int count;  // Counter
};

#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

And here we have connected to our class interface written in QML.

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

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

    QQmlApplicationEngine engine; 

    AppCore appCore;    
    QQmlContext *context = engine.rootContext();   
    /* Load the object in context to establish a connection,
     * also define the name by which the connection will be
     * */
    context->setContextProperty("appCore", &appCore);

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

main.qml

Here is part of the code, so as not to distract you from the main. Namely, a compound layer in QML Connections by means of the object as a target is set your class is indicated in the context. Treatment is carried out on the text name that is loaded into the QML engine context, together with the object itself.

To receive signals from the C ++ layer is necessary in the Connections register a function that will be called in much the same way as the target signal, but will start on a signal and then the name with a capital letter. That is, the following logic

  • signalToQml - on C++
  • onSignalToQml - on QML

But the call Slot will be somewhat different. For example, we have a class object in C ++, to which we refer by name appCore (declared aim in Connections). And then we call the slot. That is as follows: appCore.slotSomething (count).

Call slot in this code is done by pressing the OK button in the dialog, and in Cancel the call does not occur. At the same time, appCore object in C ++ code, the counter increases by one and causes the signal to put counter value in a text label applications.

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"

    /* With Connections object
     * Set up a connection to the core application class
     * */
    Connections {
        target: appCore // Specify the target connection
        /* We declare and implement a function as an object parameter and 
         * first name similar to the name of the signal. 
         * The difference is that we add at the beginning on and 
         * then write with a capital letter
         * */
        onSendToQml: {
            labelCount.text = count // Set the counter to a text label
        }
    }

    MainForm {
        // Stretch the object of the main window over the parent element
        anchors.fill: parent

        // Create text label
        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 {
            // The code from the previous lesson
        }

        // Стилизуем вторую кнопку
        button2.style: ButtonStyle {
            // The code from the previous lesson
        }

        // Start a dialogue by pressing any of the buttons in the main window
        button1.onClicked: dialogAndroid.open();
        button2.onClicked: dialogAndroid.open();

        Dialog {
            id: dialogAndroid
            width: 600  // set the width of the dialog works on the desktop, but it does not work on Android
            height: 500 // set the height of the dialog works on the desktop, but it does not work on Android

            contentItem: Rectangle {
                width: 600          // Set the width, necessary for Android-devices
                height: 500         // Set the width, necessary for Android-devices
                color: "#f7f7f7"    // 

                // The area for the dialog box message
                Rectangle {
                    // The code from the previous lesson
                }

                Rectangle {
                    id: dividerHorizontal
                    // The code from the previous lesson
                }

                Row {
                    id: row
                    // The code from the previous lesson

                    Button {
                        id: dialogButtonCancel

                        // The code from the previous lesson
                        onClicked: dialogAndroid.close()
                    }

                    Rectangle {
                        id: dividerVertical
                        // The code from the previous lesson
                    }

                    Button {
                        id: dialogButtonOk

                        // The code from the previous lesson

                        onClicked: {
                            /* Before you close the dialog by clicking on OK,
                             * Send the data in the slot application core
                             * */
                            appCore.receiveFromQml()
                            // And then close the dialogue
                            dialogAndroid.close()
                        }
                    }
                }
            }
        }
    }
}

Result

The result is a simple interaction between the C ++ and QML based on all of the same signals and slots. But the result of the application you can see in the video tutorial.

Video tutorial

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.

Do you like it? Share on social networks!

x
  • Jan. 22, 2018, 3:43 a.m.

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

Evgenii Legotckoi
  • Jan. 22, 2018, 3:56 a.m.

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

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

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

Qt\untitled1\main.cpp:21: ошибка: 'appCore' was not declared in this scope
appCore appCore;
^
Evgenii Legotckoi
  • March 5, 2018, 3:47 p.m.

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

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

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

Docent
  • July 6, 2018, 3:46 p.m.

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

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




Evgenii Legotckoi
  • July 10, 2018, 12:53 p.m.

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

V
  • Jan. 26, 2019, 8:19 a.m.

Здраствуйте сново обращаюсь к вам.
Ваш клас appcore.h и исходник appcore.cpp

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>

#include <QtGui/QGuiApplication>
#include <QScreen>

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



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

    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    AppCore appCore; 


    QQmlApplicationEngine engine;
    QQmlContext *context = engine.rootContext(); 
    context->setContextProperty("appCore", &appCore);


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



    return app.exec();
}

вот main.qml

V
  • Jan. 26, 2019, 8:19 a.m.
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Dialogs 1.2

import QtQuick 2.0
import QtQuick.Window 2.2

Window {
    flags: Qt.ToolTip | Qt.FramelessWindowHint | Qt.WA_TintedBackground |Qt.WindowStaysOnTopHint

    color: "#00000000"




visible: true
width: 100
height: 460
x: (Screen.width - width)
y: (Screen.height - height)





Rectangle {
    anchors.fill: parent
    color: "transparent"


}
AnimatedSprite {


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




    id: sprite;
    width: 100;
    height: 100;
    anchors.centerIn: parent;
     source: "Donald.png"
     frameX: 0
     frameY: Number (count)




     frameRate: 18;
     frameWidth: 100
     frameHeight: 100
     frameCount: 6
     running: folse;

}




}


V
  • Jan. 26, 2019, 8:22 a.m.

Вот этот момент

frame Y:

не получается перенести значения из С++

Evgenii Legotckoi
  • Jan. 28, 2019, 2:45 a.m.
  • (edited)

Добрый день

И что вы хотели сделать в этом коде?

    target: appCore
    onSendToQml: {
    Count = count // конкретно это, где Count в вашем коде
    }

и это

frameY: Number (count) // откуда взялся Number?
V
  • Jan. 29, 2019, 12:36 p.m.

Думал что через:

frameY: Number (count)

буду контролировать чередование анимаций, картинка 600 на 600 с кадром 100.
если сразу указать:
frameCount: 36
то вся анимация пролистается, а мне необходимо конкретная строка анимации
( ну например пролистать 6 кадров с Y= 200, а потом с Y= 400)

пока на сайтах искал наткнулся на Namber, но не получилось.

Evgenii Legotckoi
  • Jan. 30, 2019, 3:03 a.m.

Ну ок, такой тип в документации есть, но откуда-то это взяли?

Count = count // конкретно это, где Count в вашем коде

я и намёка не вижу на сам вот этот Count.

Впрочем, после пояснения уже немного яснее стало, думаю, что можете так попробовать сделать

Connections {
    target: appCore 
    onSendToQml: {
        sprite.frameY = count
    }
    }
V
  • Jan. 30, 2019, 3:23 p.m.

Нет. Не читает.
Попробывал сразу задать значение в appcore

#include "appcore.h"

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

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

не работает, хотя без ошибок.

Evgenii Legotckoi
  • Feb. 4, 2019, 3:18 a.m.

Чтобы работало, нужно сигнал высылать, то есть

emit sendToQml(count);

V
  • Feb. 14, 2019, 1:41 p.m.

Спасибо огромное! Заработало!

m
  • June 1, 2019, 3:22 p.m.

Доброй ночи. А почему вы не используете MainForm.ui.qml.? В нем я так понимаю надо верстать а в main.qml реализовывать логику?

Evgenii Legotckoi
  • June 4, 2019, 6:03 a.m.

Добрый день. Формы в QML на мой взгляд не так удобно реализованы, как в классических виджетах. Да, их удобно набрасывать в дизайнере, но при этом много функционала недоступно полноценно через дизайнер. Поэтому мне проще верстать и напрямую писать логику без дизайнера и форм QML.

m
  • June 4, 2019, 12:07 p.m.

Добрый день. У меня при реализации проекта по идеалогии описаной выше возникла проблема. Немогу достучатся до элемента ListView.

Item {
    id: mainTabLayout
    property alias spinBoxPlusMinus:spinBoxPlusMinus
    clip: true
    ListView {
        id: view
        anchors.fill: parent
        cacheBuffer: 100 * 10
        anchors.margins: 10
        spacing: 10
        model: productListModel
        delegate: Rectangle {
            id: delegateproduct
            width: view.width
            height: 50
            Item  {
                property alias spinBoxPlusMinus: spinBoxPlusMinus
                id: row
                width: parent.width
                anchors.margins: 10

             Image {
                    id: image
                    width: 50
                    height: 50
                    anchors.left: parent.left
                    anchors.leftMargin: 0
                    source: model.imgresurs


                }

                     SpinBox {
                         anchors.right: parent.right
                         anchors.rightMargin: 10
                         id: spinBoxPlusMinus
                         visible: false
                         value: 1
                        /*property alias spinBoxPlusMinus: spinBoxPlusMinus*/
                     }

        }

    }

}

Пишет : Invalid alias reference. Unable to find id "spinBoxPlusMinus"
Не поможите?) Все таки хочется разделить логику от дизайна)

Evgenii Legotckoi
  • June 5, 2019, 3:22 a.m.

Ну вы не сможете так прокинуть

property alias spinBoxPlusMinus:spinBoxPlusMinus

На самый верх из делегата. Делегат отвечает за внешнее представление элемента в ListView, а таких элементов могут быть сотни и тысячи. Поэтому в третьей строке вашего кода такой alias является бессмысленным. Поскольку QML не знает к какому именно элементу в списке ему нужно пробрасывать alias.

А вообще ваш вопрос тут немного не по теме. Здесь вопрос сигналов и слотов. А ващ вопрос о доступе к элементу через его парента. Лучше создайте на форуме отдельное обсуждение.

m
  • June 5, 2019, 11:14 a.m.

Спасибо. Так и сделаю.

МБ
  • June 20, 2019, 2:23 p.m.
  • (edited)

А если мне нужно сделать конект из дочернего qml?
Сигнал работает только из main.qml

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

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

Извиняюсь, все работает(из-за невнимательности).

Хорошо, ну будут проблемы помимо того, что касается статей, то не стесняйтесь задавать вопросы на форуме.

Андрей Никольский
  • July 30, 2019, 9:44 a.m.

Приветствую всех! Внедрил данный урок в свой проект, идеально никакой ругани на синтаксис, но...

QML Connections: Cannot assign to non-existent property "onSendToQml"

Добрый день.
У вас сигнал sendToQml в вашем классе объявлен в секции singals? Просто вы говорите о том, что внедрили в свой проект, поэтому следует вопрос о том, что чего-то у вас не хватает.

Андрей Никольский
  • July 30, 2019, 10:04 a.m.

Добрый день.
У вас сигнал sendToQml в вашем классе объявлен в секции singals? Просто вы говорите о том, что внедрили в свой проект, поэтому следует вопрос о том, что чего-то у вас не хватает.

Внедрен.

Единственное, что было измененно, это название класса.

Внедрен.

Единственное, что было измененно, это название класса.

Наверное, нужно смотреть ваш код, поскольку других мыслей у меня нет, что может быть не так. Можете создать тему на форуме и там показать код, касающийся внедрённой части?

И вообще, qml только ругается этой строчкой, но при этом работает, или тот слот вообще не срабатывает? (уверен, что не срабатывает, но мало ли)

AlezZgo
  • Jan. 5, 2020, 2:34 a.m.

Огромное спасибо вам! Очень понятно и наглядно

Y
  • Nov. 14, 2021, 3:33 p.m.

У связывания интерейса прогрммы с ядром через контекст (context->setContextProperty("appCore", &appCore);) есть один существенный недостаток, упоминание о котором я нигде не нашел, а выявился он мною империческим путём. А именно - при таком способе соединение сигнал-слот делается ИСКЛЮЧИТЕЛЬНО путем непосредственного доступа к функции (Qt::DirectConnection), задать соединение через событие (Qt::QueuedConnection) у меня не получилось, т.к. такой параметр просто некуда внести. Может это как-то в настройках компиляции можно задать не знаю. Отсюда серьёзная проблема - если интерфейс и тело программы крутятся в разных потоках (а это правильно), то получается, что интерфейсная часть обращается к телу не потокобезопасно. В итоге в моём случае пришлость вернуться к связыванию элементов интерфейса из .cpp фалов классическим connect().
Если знаете как это победить буду рад подсказке!

n
  • Aug. 31, 2023, 9:47 a.m.

Здравствуйте!
Прекрасный сайт, отличные статьи.
Не хватает только готовых проектов для скачивания.
Многих комментариев типа appCore != AppCore просто бы не было )))

Comments

Only authorized users can post comments.
Please, Log in or Sign up
e
  • ehot
  • March 31, 2024, 2:29 p.m.

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

  • Result:78points,
  • Rating points2
B

C++ - Test 002. Constants

  • Result:16points,
  • Rating points-10
B

C++ - Test 001. The first program and data types

  • Result:46points,
  • Rating points-6
Last comments
k
kmssrFeb. 8, 2024, 6:43 p.m.
Qt Linux - Lesson 001. Autorun Qt application under Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lesson 007. Working with ICMP Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVADec. 25, 2023, 10:30 a.m.
Boost - static linking in CMake project under Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJoDec. 25, 2023, 8:38 a.m.
Boost - static linking in CMake project under Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
GvozdikDec. 18, 2023, 9:01 p.m.
Qt/C++ - Lesson 056. Connecting the Boost library in Qt for MinGW and MSVC compilers Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Now discuss on the forum
a
a_vlasovApril 14, 2024, 6:41 a.m.
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
Павел Дорофеев
Павел ДорофеевApril 14, 2024, 2:35 a.m.
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
Mm
Mind minglesApril 12, 2024, 12:42 a.m.
ASO Service Forum: Enhancing App Visibility and Reach Welcome to the ASO Service Forum, your ultimate destination for insights, discussions, and strategies revolving around App Store Optimization. ASO (App Store Optimization) is paramoun…
f
fastrexApril 4, 2024, 4:47 a.m.
Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…

Follow us in social networks