Evgenii Legotckoi
Қар. 4, 2019, 4:15 Т.Қ.

QML - Сабақ 036. QML-де сигналдармен және слоттармен жұмыс

Бұл мақала осы сайттағы барлық алдыңғы мақалалармен салыстырғанда QML жүйесіндегі сигналдар мен слоттардың ең толық сипаттамасы болып табылады.

Бұл мақалада мен Qt/QML + Qt/C++ бағдарламаларымен жұмыс істегенде төмендегілерді түсіндіруге тырысамын:

  • QML деңгейінде тіркелетін C++ класындағы әдістер деп те аталатын сигналдар мен слоттарды жариялау тәсілдері
  • контекст ретінде C++-де жарияланған кластардың сигналдарына қосылу жолдары
  • Q_PROPERTY-мен жұмыс істеу, ол да сигналдар мен слоттарды қажет етеді
  • QML-де сигналдар мен слоттарды қосу тәсілдері
  • және т.б.

C++ сыныбының сигналдары мен слоттары

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

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

Қолданбаның сыртқы түрі келесідей болады

AppCore.h

Сигналдарды және слоттарды C++ кодында жариялау классикалық Qt/C++ тілінен онша ерекшеленбейді.

  1. #ifndef APPCORE_H
  2. #define APPCORE_H
  3.  
  4. #include <QObject>
  5.  
  6.  
  7. class AppCore : public QObject
  8. {
  9. Q_OBJECT
  10. public:
  11. explicit AppCore(QObject *parent = nullptr);
  12.  
  13. signals:
  14. void sendToQml(int count);
  15.  
  16. public slots:
  17. void receiveFromQml();
  18.  
  19. private:
  20. int m_counter {0};
  21. };
  22.  
  23. #endif // APPCORE_H
  24.  

AppCore.cpp

Сондай-ақ әдістердің өзін жүзеге асыру.

  1. #include "AppCore.h"
  2.  
  3. AppCore::AppCore(QObject* parent) : QObject(parent)
  4. {
  5. }
  6.  
  7. void AppCore::receiveFromQml()
  8. {
  9. // We increase the counter and send a signal with its value
  10. ++m_counter;
  11. emit sendToQml(m_counter);
  12. }
  13.  

main.cpp

  1. #include <QGuiApplication>
  2. #include <QQmlApplicationEngine>
  3. #include <QQmlContext>
  4. #include "AppCore.h"
  5.  
  6. int main(int argc, char *argv[])
  7. {
  8. QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
  9.  
  10. QGuiApplication app(argc, argv);
  11.  
  12. AppCore appCore; // Create the application core with signals and slots
  13.  
  14. QQmlApplicationEngine engine;
  15. QQmlContext *context = engine.rootContext();
  16.  
  17. /* We load the object into the context to establish the connection,
  18.      * and also define the name "appCore" by which the connection will occur
  19. * */
  20. context->setContextProperty("appCore", &appCore);
  21.  
  22. const QUrl url(QStringLiteral("qrc:/main.qml"));
  23. QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
  24. &app, [url](QObject *obj, const QUrl &objUrl) {
  25. if (!obj && url == objUrl)
  26. QCoreApplication::exit(-1);
  27. }, Qt::QueuedConnection);
  28. engine.load(url);
  29.  
  30. return app.exec();
  31. }
  32.  

main.qml

Ал енді ең қызығы. QML контекстінде жүктелген нысанды қалай пайдалану керек және оның сигналдарына қосылу жолы.

Естеріңізде болса, біз нысанды QML контекстіне appCore атауымен жүктеген болатынбыз, оған қол жеткізу үшін осы нысанды қолданамыз. Бірақ сигналға қосылу үшін QML Connections түрін пайдалануымыз керек.

  1. import QtQuick 2.12
  2. import QtQuick.Controls 2.12
  3. import QtQuick.Window 2.12
  4.  
  5. Window {
  6. visible: true
  7. width: 640
  8. height: 480
  9. title: qsTr("QML Signals and Slots")
  10.  
  11. /* Using the Connections Object
  12.      * Establish a connection with the application core object
  13.      * */
  14. Connections {
  15. target: appCore // Specify the target to connect
  16.         /* Declare and implement the function as a parameter
  17.          * object and with a name similar to the name of the signal
  18.          * The difference is that we add on at the beginning and then write
  19.          * capitalized
  20.          * */
  21. onSendToQml: {
  22. labelCount.text = count // Set the counter to the text label
  23. }
  24. }
  25.  
  26.  
  27. Label {
  28. id: labelCount
  29. text: "0"
  30.  
  31. anchors.bottom: parent.verticalCenter
  32. anchors.horizontalCenter: parent.horizontalCenter
  33. anchors.bottomMargin: 15
  34. }
  35.  
  36. Button {
  37. text: qsTr("Increase counter")
  38. onClicked: appCore.receiveFromQml() // Вызов слота
  39.  
  40. anchors.top: parent.verticalCenter
  41. anchors.horizontalCenter: parent.horizontalCenter
  42. }
  43. }
  44.  

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

Бұл жағдайда receiveFromQml() ұяшығы ретінде жариялаудың да қажеті жоқ. Бұл әдісті Q_INVOKABLE әдісі ретінде де жариялауға болады.

  1. public:
  2. explicit AppCore(QObject *parent = nullptr);
  3. Q_INVOKABLE void receiveFromQml();

Q_PROPERTY пайдалану

Келесі опция Q_PROPERTY макросын пайдалану болып табылады. Біздің тапсырмамыз үшін Qt классикалық сипат келесідей болуы мүмкін

  1. Q_PROPERTY(int counter READ counter WRITE setCounter NOTIFY counterChanged)

Бұл сипат келесі компоненттерден тұрады:

  • сипат түрі, сонымен қатар оның атауы: int m_counter айнымалысына класс ішіндегі байланыстырылған int counter , бұл Qt ішіндегі кодты генерациялау логикасы.
  • оқу әдісі атауы, сипат атымен бірдей: санауыш
  • мәнді орнату әдісінің атауы: setCounter
  • сипаттың өзгеруін көрсететін сигнал: counterChanged

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

Енді Q_PROPERTY арқылы толық кодты қараңыз.

AppCore.h

  1. #ifndef APPCORE_H
  2. #define APPCORE_H
  3.  
  4. #include <QObject>
  5.  
  6.  
  7. class AppCore : public QObject
  8. {
  9. Q_OBJECT
  10. public:
  11. Q_PROPERTY(int counter READ counter WRITE setCounter NOTIFY counterChanged)
  12. explicit AppCore(QObject *parent = nullptr);
  13.  
  14. int counter() const;
  15.  
  16. public slots:
  17. void setCounter(int counter);
  18.  
  19. signals:
  20. void counterChanged(int counter);
  21.  
  22. private:
  23. int m_counter {0};
  24. };
  25.  
  26. #endif // APPCORE_H
  27.  

AppCore.cpp

  1. #include "AppCore.h"
  2.  
  3. AppCore::AppCore(QObject* parent) : QObject(parent)
  4. {
  5.  
  6. }
  7.  
  8. int AppCore::counter() const
  9. {
  10. return m_counter;
  11. }
  12.  
  13. void AppCore::setCounter(int counter)
  14. {
  15. if (m_counter == counter)
  16. return;
  17.  
  18. m_counter = counter;
  19. emit counterChanged(m_counter);
  20. }
  21.  

main.qml

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

  1. import QtQuick 2.12
  2. import QtQuick.Controls 2.12
  3. import QtQuick.Window 2.12
  4.  
  5. Window {
  6. visible: true
  7. width: 640
  8. height: 480
  9. title: qsTr("QML Signals and Slots")
  10.  
  11. Label {
  12. id: labelCount
  13. text: appCore.counter
  14.  
  15. anchors.bottom: parent.verticalCenter
  16. anchors.horizontalCenter: parent.horizontalCenter
  17. anchors.bottomMargin: 15
  18. }
  19.  
  20. Button {
  21. text: qsTr("Increase counter")
  22. onClicked: ++appCore.counter
  23.  
  24. anchors.top: parent.verticalCenter
  25. anchors.horizontalCenter: parent.horizontalCenter
  26. }
  27. }
  28.  

QML файлдарының ішіндегі сигналдарды қосу

Енді QML файлдарының ішіндегі сигналдар мен слоттарды (функцияларды) қосу опциясын қарастырыңыз. Енді C++ коды болмайды.

  1. import QtQuick 2.12
  2. import QtQuick.Controls 2.12
  3. import QtQuick.Window 2.12
  4.  
  5. Window {
  6. id: mainWindow
  7. visible: true
  8. width: 640
  9. height: 480
  10. title: qsTr("QML Signals and Slots")
  11.  
  12. // Counter property
  13. property int counter: 0
  14.  
  15. // Method for counter manipulation
  16. function inrementCounter()
  17. {
  18. ++counter;
  19. }
  20.  
  21. Label {
  22. id: labelCount
  23. text: mainWindow.counter
  24.  
  25. anchors.bottom: parent.verticalCenter
  26. anchors.horizontalCenter: parent.horizontalCenter
  27. anchors.bottomMargin: 15
  28. }
  29.  
  30. Button {
  31. id: button
  32. text: qsTr("Increase counter")
  33.  
  34. anchors.top: parent.verticalCenter
  35. anchors.horizontalCenter: parent.horizontalCenter
  36.  
  37. Component.onCompleted: {
  38. // When the button is created, then connect the click signal on the button
  39.             // to the method for increasing the counter in the application window
  40. button.clicked.connect(mainWindow.inrementCounter)
  41. }
  42. }
  43. }
  44.  

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

  1. button.clicked.disconnect(mainWindow.inrementCounter)

Сигналды сигналға қосыңыз

Сондай-ақ QML-де Qt / C ++ сияқты сигналды сигналға қосуға болады. Келесі жасанды мысалды қараңыз.

  1. import QtQuick 2.12
  2. import QtQuick.Controls 2.12
  3. import QtQuick.Window 2.12
  4.  
  5. Window {
  6. id: mainWindow
  7. visible: true
  8. width: 640
  9. height: 480
  10. title: qsTr("QML Signals and Slots")
  11.  
  12. // Announcing a button click signal in the application window
  13. signal buttonClicked;
  14.  
  15. Label {
  16. id: labelCount
  17. text: counter
  18.  
  19. anchors.bottom: parent.verticalCenter
  20. anchors.horizontalCenter: parent.horizontalCenter
  21. anchors.bottomMargin: 15
  22.  
  23. // Counter property
  24. property int counter: 0
  25.  
  26. // Method for counter manipulation
  27. function inrementCounter()
  28. {
  29. ++counter;
  30. }
  31. }
  32.  
  33. Button {
  34. id: button
  35. text: qsTr("Increase counter")
  36.  
  37. anchors.top: parent.verticalCenter
  38. anchors.horizontalCenter: parent.horizontalCenter
  39.  
  40. Component.onCompleted: {
  41. // When the button is created, then connect the click signal on the button
  42.             // to the method for increasing the counter in the application window
  43. button.clicked.connect(mainWindow.buttonClicked)
  44. }
  45. }
  46.  
  47. Component.onCompleted: {
  48. buttonClicked.connect(labelCount.inrementCounter)
  49. }
  50. }
  51.  

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

Сигналдарда айнымалы мәндерді пайдалану

QML сонымен қатар сигналдарда айнымалы мәндерді пайдалану мүмкіндігіне ие.

  1. import QtQuick 2.12
  2. import QtQuick.Controls 2.12
  3. import QtQuick.Window 2.12
  4.  
  5. Window {
  6. id: mainWindow
  7. visible: true
  8. width: 640
  9. height: 480
  10. title: qsTr("QML Signals and Slots")
  11.  
  12. // Signal with argument
  13. signal setCounter(var number);
  14.  
  15. Label {
  16. id: labelCount
  17. text: counter
  18.  
  19. anchors.bottom: parent.verticalCenter
  20. anchors.horizontalCenter: parent.horizontalCenter
  21. anchors.bottomMargin: 15
  22.  
  23. // Counter property
  24. property int counter: 0
  25.  
  26. // Method for counter manipulation, takes an argument
  27. function setCounter(number)
  28. {
  29. counter = number;
  30. }
  31. }
  32.  
  33. Button {
  34. id: button
  35. text: qsTr("Increase counter")
  36.  
  37. anchors.top: parent.verticalCenter
  38. anchors.horizontalCenter: parent.horizontalCenter
  39.  
  40. onClicked: {
  41. // We call the signal of the application window for installing the counter indicating the new counter value
  42. mainWindow.setCounter(labelCount.counter + 1)
  43. }
  44. }
  45.  
  46. Component.onCompleted: {
  47. setCounter.connect(labelCount.setCounter)
  48. }
  49. }
  50.  

Қорытынды

Көбінесе бұл мақала бірнеше тармақтарға сәйкес келеді:

  • C++ тілінде QML деңгейімен әрекеттесу үшін Q_PROPERTY макросын пайдаланып сигналдарды, ұяшықтарды, Q_INVOKABLE әдістерін пайдалануға және сипаттарды жасауға болады.
  • Нысандардан келетін сигналдарға жауап беру үшін QML қосылымдары түрін пайдалануға болады
  • Q_PROPERTY QML декларативті стилін ұстанады және егер сипат QML ішіндегі кез келген нысанға қосылған болса, сипат өзгерген кезде жаңа мәндерді автоматты түрде орната алады. Бұл жағдайда сигнал ұясының қосылымдары автоматты түрде орнатылады.
  • QML жүйесінде келесі синтаксисті пайдаланып сигнал/слот қосылымдарын қосуға және өшіруге болады:
  • object1.signal.connect (object2.slot)
  • object1.сигнал.ажырату (объект2.слот)
  • QML-дегі сигналдар Qt/C++ тілінде орындалғандай, басқа сигналдарға да қосылуы мүмкін
  • QML-дегі сигналдарда да дәлелдер болуы мүмкін

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

Пікірлер

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