Evgenii Legotckoi
Қаз. 15, 2015, 10:05 Т.Қ.

QML - Сабақ 004. Qt QML-дегі сигналдар мен слоттар

Сонымен, біз QML деңгейі мен C ++ деңгейі арасында деректерді тасымалдауға келдік. Шынымды айтсам, принцип бірдей C++ қабатындағы сигналдар мен слоттарды пайдалану сияқты қарапайым. Әсіресе Qt 5.5.

Мысал тілқатысу терезесін жасаған алдыңғы оқулық кодының негізінде көрсетіледі. Бірақ Android жүйесіндегі жұмыс үлгісінің скриншоттары көрсетілмейді, бірақ мен сізді сендіремін - Барлығы швейцариялық сағаттар сияқты жұмыс істейді.

QML көмегімен жоба құрылымы

qml бар жоба құрылымы Өткен сабақпен салыстырғанда бізде біраз өзгерістер бар. Атап айтқанда, қосымшаның өзегі болатын жаңа сынып қосылды.

  • appcore.h - қолданбаның негізгі тақырып файлы;
  • appcore.cpp - қолданбаның негізгі бастапқы код файлы.

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


appcore.h

Біздің сыныптың тақырып файлы үш пенни сияқты қарапайым. Оның бір ғана есептегіші ( int түріндегі айнымалы), санауышты бір көбейтетін және сигнал беретін ұясы бар, ол да класта бір және санауыш мәнін * санағышына береді. QML интерфейсі. *

  1. #ifndef APPCORE\_H
  2. #define APPCORE\_H
  3.  
  4. #include <QObject>
  5.  
  6. class AppCore : public QObject
  7. {
  8. Q\_OBJECT
  9. public:
  10. explicit AppCore(QObject *parent = 0);
  11.  
  12. signals:
  13. // Сигнал для передачи данных в qml-интерфейс
  14. void sendToQml(int count);
  15.  
  16. public slots:
  17. // Слот для приёма данных из qml-интерфейса
  18. void receiveFromQml();
  19.  
  20. private:
  21. int count; // Счетчик, которым будем оперировать
  22. };
  23.  
  24. #endif // APPCORE\_H

appcore.cpp

Ал, оның бастапқы кодындағы сыныптың логикасы.

  1. #include "appcore.h"
  2.  
  3. AppCore::AppCore(QObject *parent) : QObject(parent)
  4. {
  5. count = 0;
  6. }
  7.  
  8. void AppCore::receiveFromQml()
  9. {
  10. count++;
  11. emit sendToQml(count);
  12. }

main.cpp

Мұнда біз QML тілінде жазылған интерфейсті өз класымызға қосамыз.

  1. #include <QApplication>
  2. #include <QQmlApplicationEngine>
  3. #include <QQmlContext>
  4. #include "appcore.h"
  5.  
  6. int main(int argc, char *argv[])
  7. {
  8. QApplication app(argc, argv);
  9.  
  10. QQmlApplicationEngine engine; // Создаём движок qml
  11.  
  12. AppCore appCore; // Создаём ядро приложения
  13. QQmlContext *context = engine.rootContext(); // Создаём корневой контекст
  14. /* Загружаем объект в контекст для установки соединения,
  15. * а также определяем имя, по которому будет происходить соединение
  16. * */
  17. context->setContextProperty("appCore", &appCore);
  18.  
  19. // И загружаем в него исходники qml
  20. engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
  21.  
  22. return app.exec();
  23. }

main.qml

Мен сізді басты нәрседен алшақтатпау үшін кодтың бір бөлігін беремін. Атап айтқанда, QML қабатындағы қосылым Connections нысанын пайдалану арқылы жүзеге асырылады, оның мақсаты контексте орнатылған сынып болып табылады. Анықтама нысанның өзімен бірге QML қозғалтқышының контекстіне жүктелетін мәтін атауы арқылы жүзеге асырылады.

C ++ деңгейінен сигналдарды қабылдау үшін Қосылымдар ішінде мақсатты нысан сигналымен бірдей дерлік аталатын, бірақ on, содан кейін сигнал атауы бас әріппен басталатын функцияны жазу керек. Яғни, логика келесідей

  • signalToQml - C++ тілінде
  • onSignalToQml - QML-ге

Бірақ Slot шақыруы сәл басқаша өтеді. Мысалы, бізде C++ ішінде appCore деп аталатын сынып нысаны бар ( Connections ішінде мақсат ретінде жарияланған). Содан кейін біз слот функциясын шақырамыз. Яғни, келесідей: appCore.slotSomething(count).

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

  1. import QtQuick 2.5
  2. import QtQuick.Controls 1.4
  3. import QtQuick.Controls.Styles 1.4
  4. import QtQuick.Dialogs 1.2
  5.  
  6. ApplicationWindow {
  7. visible: true
  8. width: 640
  9. height: 480
  10. title: qsTr("Hello World") // Ну как же без Приветствия Миру
  11. color: "white"
  12.  
  13. /* С помощью объекта Connections
  14. * Устанавливаем соединение с классом ядра приложения
  15. * */
  16. Connections {
  17. target: appCore // Указываем целевое соединение
  18. /* Объявляем и реализуем функцию, как параметр
  19. * объекта и с имененем похожим на название сигнала
  20. * Разница в том, что добавляем в начале on и далее пишем
  21. * с заглавной буквы
  22. * */
  23. onSendToQml: {
  24. labelCount.text = count // Устанавливаем счётчик в текстовый лейбл
  25. }
  26. }
  27.  
  28. MainForm {
  29. // Растягиваем объект главного окна по всему родительскому элементу
  30. anchors.fill: parent
  31.  
  32. // Создадим текстовый лейбл
  33. Text {
  34. id: labelCount
  35. // А также установим его визуальные параметры
  36. anchors.left: parent.left
  37. anchors.right: parent.right
  38. anchors.top: parent.top
  39. height: 300
  40. verticalAlignment: Text.AlignVCenter
  41. horizontalAlignment: Text.AlignHCenter
  42. // Первым текстом будет традиционный Хеллоу Ворлд!
  43. text: "Hello, World !!!"
  44. }
  45.  
  46. // Стилизуем первую кнопку
  47. button1.style: ButtonStyle {
  48. // Программный код из предыдущего урока
  49. }
  50.  
  51. // Стилизуем вторую кнопку
  52. button2.style: ButtonStyle {
  53. // Программный код из предыдущего урока
  54. }
  55.  
  56. // Запускаем диалог по нажатию любой из кнопок в главном окне
  57. button1.onClicked: dialogAndroid.open();
  58. button2.onClicked: dialogAndroid.open();
  59.  
  60. // Создаём объект диалогового окна
  61. Dialog {
  62. id: dialogAndroid
  63. /* Когда деплоите под Android-устройства,
  64. * обязательно закоментируйте эти две строки,
  65. * иначе словите глюки в работе устройства
  66. */
  67. width: 600 // Задаём ширину диалога, работает на десктопе, но на Android не сработает
  68. height: 500 // Задаём высоту диалога, работает на декстопе, но на Android не сработает
  69.  
  70. // Создаём содержимое диалогового окна
  71. contentItem: Rectangle {
  72. width: 600 // Устанавливаем ширину, необходимо для Android-устройства
  73. height: 500 // Устанавливаем высоту, необходимо для Android-устройства
  74. color: "#f7f7f7" // Задаём цвет
  75.  
  76. // Область для сообщения диалогового окна
  77. Rectangle {
  78. // Программный код из предыдущего урока
  79. }
  80.  
  81. // Создаём горизонтальный разделитель с помощью Rectangle
  82. Rectangle {
  83. id: dividerHorizontal
  84. // Программный код из предыдущего урока
  85. }
  86.  
  87. /* Создаём подложку для кнопок в виде объекта Строки
  88. * В данном объекте для объектов детей не работают некоторые параметры
  89. * anchors, кроме параметров anchors.top и anchors.bottom
  90. */
  91. Row {
  92. id: row
  93. // Программный код из предыдущего урока
  94.  
  95. Button {
  96. id: dialogButtonCancel
  97.  
  98. // Программный код из предыдущего урока
  99.  
  100. // По нажатию кнопки закрываем диалог
  101. onClicked: dialogAndroid.close()
  102. }
  103.  
  104. // Создаём разделитель между кнопками шириной в 2 пикселя
  105. Rectangle {
  106. id: dividerVertical
  107. // Программный код из предыдущего урока
  108. }
  109.  
  110. Button {
  111. id: dialogButtonOk
  112.  
  113. // Программный код из предыдущего урока
  114.  
  115. // По нажатию кнопки закрываем диалог
  116. onClicked: {
  117. /* Прежде, чем закрывать диалог по OK кнопке,
  118. * отправим данные в слот ядра приложения
  119. * */
  120. appCore.receiveFromQml()
  121. // А потом и закроем диалог
  122. dialogAndroid.close()
  123. }
  124. }
  125. }
  126. }
  127. }
  128. }
  129. }

Барлығы

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

Бейне оқулық

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

x
  • Қаң. 22, 2018, 2:43 Т.Қ.

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

Evgenii Legotckoi
  • Қаң. 22, 2018, 2:56 Т.Қ.

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

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

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

Qt\untitled1\main.cpp:21: ошибка: 'appCore' was not declared in this scope
appCore appCore;
^
Evgenii Legotckoi
  • Наурыз 6, 2018, 2:47 Т.Ж.

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

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

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

Docent
  • Шілде 7, 2018, 1:46 Т.Ж.

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

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




Evgenii Legotckoi
  • Шілде 10, 2018, 10:53 Т.Қ.

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

V
  • Қаң. 26, 2019, 7:19 Т.Қ.

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

main.cpp

  1. #include <QGuiApplication>
  2. #include <QQmlApplicationEngine>
  3.  
  4. #include <QtGui/QGuiApplication>
  5. #include <QScreen>
  6.  
  7. #include "appcore.h"
  8. #include <QQmlContext>
  9.  
  10.  
  11.  
  12. int main(int argc, char *argv[])
  13. {
  14.  
  15. QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
  16.  
  17. QGuiApplication app(argc, argv);
  18.  
  19. AppCore appCore;
  20.  
  21.  
  22. QQmlApplicationEngine engine;
  23. QQmlContext *context = engine.rootContext();
  24. context->setContextProperty("appCore", &appCore);
  25.  
  26.  
  27. engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
  28. if (engine.rootObjects().isEmpty())
  29. return -1;
  30.  
  31.  
  32.  
  33. return app.exec();
  34. }

вот main.qml

V
  • Қаң. 26, 2019, 7:19 Т.Қ.
  1. import QtQuick.Controls 1.4
  2. import QtQuick.Controls.Styles 1.4
  3. import QtQuick.Dialogs 1.2
  4.  
  5. import QtQuick 2.0
  6. import QtQuick.Window 2.2
  7.  
  8. Window {
  9. flags: Qt.ToolTip | Qt.FramelessWindowHint | Qt.WA_TintedBackground |Qt.WindowStaysOnTopHint
  10.  
  11. color: "#00000000"
  12.  
  13.  
  14.  
  15.  
  16. visible: true
  17. width: 100
  18. height: 460
  19. x: (Screen.width - width)
  20. y: (Screen.height - height)
  21.  
  22.  
  23.  
  24.  
  25.  
  26. Rectangle {
  27. anchors.fill: parent
  28. color: "transparent"
  29.  
  30.  
  31. }
  32. AnimatedSprite {
  33.  
  34.  
  35. Connections {
  36. target: appCore // Указываем целевое соединение
  37. /* Объявляем и реализуем функцию, как параметр
  38. * объекта и с имененем похожим на название сигнала
  39. * Разница в том, что добавляем в начале on и далее пишем
  40. * с заглавной буквы
  41. * */
  42. onSendToQml: {
  43. Count = count // Устанавливаем счётчик в текстовый лейбл
  44. }
  45. }
  46.  
  47.  
  48.  
  49.  
  50. id: sprite;
  51. width: 100;
  52. height: 100;
  53. anchors.centerIn: parent;
  54. source: "Donald.png"
  55. frameX: 0
  56. frameY: Number (count)
  57.  
  58.  
  59.  
  60.  
  61. frameRate: 18;
  62. frameWidth: 100
  63. frameHeight: 100
  64. frameCount: 6
  65. running: folse;
  66.  
  67. }
  68.  
  69.  
  70.  
  71.  
  72. }
  73.  
  74.  
V
  • Қаң. 26, 2019, 7:22 Т.Қ.

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

frame Y:

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

Evgenii Legotckoi
  • Қаң. 28, 2019, 1:45 Т.Қ.
  • (өңделген)

Добрый день

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

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

и это

  1. frameY: Number (count) // откуда взялся Number?
V
  • Қаң. 29, 2019, 11:36 Т.Қ.

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

frameY: Number (count)

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

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

Evgenii Legotckoi
  • Қаң. 30, 2019, 2:03 Т.Қ.

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

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

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

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

  1. Connections {
  2. target: appCore
  3. onSendToQml: {
  4. sprite.frameY = count
  5. }
  6. }
V
  • Қаң. 31, 2019, 2:23 Т.Ж.

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

  1. #include "appcore.h"
  2.  
  3. AppCore::AppCore(QObject *parent) : QObject(parent)
  4. {
  5. count = 200;
  6. }
  7.  
  8. void AppCore::receiveFromQml()
  9. {
  10. count++;
  11. emit sendToQml(count);
  12. }

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

Evgenii Legotckoi
  • Ақп. 4, 2019, 2:18 Т.Қ.

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

  1. emit sendToQml(count);

V
  • Ақп. 15, 2019, 12:41 Т.Ж.

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

m
  • Маусым 2, 2019, 1:22 Т.Ж.

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

Evgenii Legotckoi
  • Маусым 4, 2019, 4:03 Т.Қ.

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

m
  • Маусым 4, 2019, 10:07 Т.Қ.

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

  1. Item {
  2. id: mainTabLayout
  3. property alias spinBoxPlusMinus:spinBoxPlusMinus
  4. clip: true
  5. ListView {
  6. id: view
  7. anchors.fill: parent
  8. cacheBuffer: 100 * 10
  9. anchors.margins: 10
  10. spacing: 10
  11. model: productListModel
  12. delegate: Rectangle {
  13. id: delegateproduct
  14. width: view.width
  15. height: 50
  16. Item {
  17. property alias spinBoxPlusMinus: spinBoxPlusMinus
  18. id: row
  19. width: parent.width
  20. anchors.margins: 10
  21.  
  22. Image {
  23. id: image
  24. width: 50
  25. height: 50
  26. anchors.left: parent.left
  27. anchors.leftMargin: 0
  28. source: model.imgresurs
  29.  
  30.  
  31. }
  32.  
  33. SpinBox {
  34. anchors.right: parent.right
  35. anchors.rightMargin: 10
  36. id: spinBoxPlusMinus
  37. visible: false
  38. value: 1
  39. /*property alias spinBoxPlusMinus: spinBoxPlusMinus*/
  40. }
  41.  
  42. }
  43.  
  44. }
  45.  
  46. }

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

Evgenii Legotckoi
  • Маусым 5, 2019, 1:22 Т.Қ.

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

  1. property alias spinBoxPlusMinus:spinBoxPlusMinus

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

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

m
  • Маусым 5, 2019, 9:14 Т.Қ.

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

МБ
  • Маусым 21, 2019, 12:23 Т.Ж.
  • (өңделген)

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

Evgenii Legotckoi
  • Маусым 24, 2019, 3:52 Т.Қ.

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

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

МБ
  • Маусым 24, 2019, 4:21 Т.Қ.

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

Evgenii Legotckoi
  • Маусым 24, 2019, 4:23 Т.Қ.

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

Андрей Никольский
  • Шілде 30, 2019, 7:44 Т.Қ.

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

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

Evgenii Legotckoi
  • Шілде 30, 2019, 7:48 Т.Қ.

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

Андрей Никольский
  • Шілде 30, 2019, 8:04 Т.Қ.

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

Внедрен.

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

Внедрен.

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

Evgenii Legotckoi
  • Шілде 31, 2019, 1:10 Т.Қ.

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

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

AlezZgo
  • Қаң. 5, 2020, 1:34 Т.Қ.

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

Y
  • Қар. 15, 2021, 2:33 Т.Ж.

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

n
  • Там. 31, 2023, 7:47 Т.Қ.

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

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз