Яндекс.Метрика

    Песочница

    QML и C++. Простой пример связки

    image
    QML технология красивая и радует глаз. Меня она очень заинтересовала, и я решил ее освоить. Но не тут то было, ибо я оказался тупым и беспомощным. Нигде в сети не нашел примера «для чайников» (наверно плохо искал), чтобы с нуля построить простейшее приложение QML и C++ в связке. Везде чего-то не хватало: или не учитывался Qt Creator, или код выдавал ошибки, или отсутствовали целые моменты, которые пользователи должны были сами знать. Официальная документация и примеры здесь на хабре также были с этими недостатками. Вот и решил после долгих попыток и ошибок написать такую статью для начинающих с подробнейшим описанием.

    Задача. Нужно написать программу QML в связке с С++, где
    1. На форме располагается кнопка, строка ввода, и поле вывода.
    2. Требуется считать из строки ввода число, прибавляется 1, и ответ выводится в поле вывода.
    3. Интерфейс написан на QML.
    4. Функционал на С++, то есть нам нужно обеспечить взаимосвязь между QML и C++: кнопка QML вызывает С++ функцию, а функция меняет свойства QML объектов.

    Создание базового приложение QML


    Используем Qt Creator. Я использовал версию 2.3 с Qt 4.7.4
    image
    Создание QML приложения

    1. Создаем GUI Приложение: Файл -> Новый файл или проект.... Там слева выбираем Проект Qt Widget, с справа GUI приложение Qt. Потом жмем внизу кнопку "Выбрать...".


    2. В следующем окне выбираем название нашего проекта (без пробелов и русских букв). Например, в нашем случае это "Example".


    3. В следующем окне у Вас должна стоять галочка у "Desktop". Если ее у Вас ее нет, то Вы неверно установили Qt Creator (или Вы намерено не хотите создавать десктопные приложения). Та сборка Qt Creator официальная, которую я ставил (2.3), по умолчанию почему-то десктопные части не устанавливала.


    4. В следующем окне снимите галочку с пункта "Создать форму".


    5. В следующем окне можно ничего не менять. И жмем кнопку "Завершить".


    Редактирование файла проекта


    6. Отредактируем файл проекта (у нас это Example.pro):


    И добавим к строчке "QT += core gui" слово "declarative". В итоге получим строчку:

    QT       += core gui declarative


    Создание QML проекта

    7. По папке с проектом в Qt Creator щелкаем правой кнопкой и идем к пункту "Добавить новый..."


    8. Выбираем слева "QML", а справа "Файл QML".


    9. Назовем его "main".


    10. Следующее окно без изменений.


    11. В результате получим файл "main.qml" с текстом:

    import QtQuick 1.0
    
    Rectangle {
        width: 100
        height: 62
    }


    Создаем файл ресурсов

    12. По папке с проектом в Qt Creator щелкаем правой кнопкой и идем к пункту "Добавить новый..."



    13. Выбираем слева "Qt", а справа "Файл ресурсов Qt".


    14. Назовем его "res".


    15. Следующее окно без изменений.


    В результате получим файд "res.qrc"

    16. Добавим префикс. Для этого щелкнем по кнопке "Добавить", а там щелкнуть "Добавить префикс".


    17. Измените текст префикса на "/".


    18. Добавим наш QML файл. Для этого щелкнем по кнопке "Добавить", а там щелкнуть "Добавить файлы".


    19. И выберем наш файл "main.qml". И файл добавится к ресурсам нашего приложения:


    Если сейчас запустим наше приложение, то qml пока не увидим:


    Редактирование исходников

    Теперь займемся подключением qml, чтобы он заработал.

    20. Перейдем к редактированию файла "mainwindow.h" (находится в заголовочных). Он имеет пока вид:
    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QtGui/QMainWindow>
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = 0);
        ~MainWindow();
    };
    
    #endif // MAINWINDOW_H


    Поменяем его на такой вид:

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include <QtDeclarative/QDeclarativeView>
    #include <QGraphicsObject>
    #include <QtGui>
    #include <QDeclarativeContext>
    
    namespace Ui {
        class MainWindow;
    }
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
    
    private:
        QDeclarativeView *ui;
    };
    
    #endif // MAINWINDOW_H
    


    Мы добавили #include <QtDeclarative/QDeclarativeView>, #include <QGraphicsObject> и др, добавили namespace, добавили ключевое слово explicit, и главное добавили QDeclarativeView *ui.

    21. Теперь займемся редактированием файла mainwindow.cpp. Он имеет пока вид:

    #include "mainwindow.h"
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
    {
    }
    
    MainWindow::~MainWindow()
    {
    
    }
    


    Поменяем на такой:

    #include "mainwindow.h"
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent)
    {
        //Включаем наш QML
        ui = new QDeclarativeView;
        ui->setSource(QUrl("qrc:/main.qml"));
        setCentralWidget(ui);
        ui->setResizeMode(QDeclarativeView::SizeRootObjectToView);
    }
    
    MainWindow::~MainWindow()
    {
        //Удаляем QML
        delete ui;
    }


    22. Запускаем наше приложение и получаем белое окно. Наш QML заработал.


    Написание приложения


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

    Построение интерфейса.

    23. На данный момент main.qml выглядит так:

    import QtQuick 1.0
    
    Rectangle {
        width: 100
        height: 62
    }




    Отредактируем его, изменив главный прямоугольник-окно:

    import QtQuick 1.0
    
    //Главное окно
    Rectangle {
        width: 300
        height: 300
        anchors.fill: parent
    
    }


    24. Добавим простейшую кнопку на нашу форму.

        //Кнопка
        Rectangle {
            id: button //Имя кнопки
    
            //Размещаем в центре
            x: parent.width / 2 - button.width / 2;
            y: parent.height / 2 - button.height / 2;
    
            //Размеры кнопки
            width: 100
            height: 30
    
            //Цвет кнопки
            color: "gray"
    
            //Текст кнопки
            Text {
                id: buttonLabel
                text: "Пуск"
                anchors.centerIn: parent;
            }
    
            //Действие мыши
            MouseArea {
                anchors.fill: parent
                id: mouseArea
            }
        }


    В результате main.qml примет вид:

    import QtQuick 1.0
    
    Rectangle {
        width: 300
        height: 300
        anchors.fill: parent
        
        //Кнопка
        Rectangle {
            id: button //Имя кнопки
    
            //Размещаем в центре
            x: parent.width / 2 - button.width / 2;
            y: parent.height / 2 - button.height / 2;
    
            //Размеры кнопки
            width: 100
            height: 30
    
            //Цвет кнопки
            color: "gray"
    
            //Текст кнопки
            Text {
                id: buttonLabel
                text: "Пуск"
                anchors.centerIn: parent;
            }
    
            //Действие мыши
            MouseArea {
                anchors.fill: parent
                id: mouseArea
            }
        }
    }


    Если мы запустим приложение,

    то получим следующее:


    25. Добавим строку ввода, куда пользователь будет вводить информацию с именем textinput.

    //Строка ввода
        Rectangle {
            id: textinputRect //Имя строки ввода
    
            //Размещаем ниже
            x: parent.width / 2 - button.width / 2;
            y: parent.height / 2 - button.height / 2+40;
    
            //Размеры строки ввода
            width: 100
            height: 18
    
            //цвет строки ввода
            color: "gray"
    
            TextInput {
                id: textinput
                objectName: "textinput"
                color: "#151515";
                selectionColor: "blue"
                font.pixelSize: 12;
                width: parent.width-4
                anchors.centerIn: parent
                focus: true
                text:"1"
                }
        }


    В результате main.qml примет вид:

    import QtQuick 1.0
    
    Rectangle {
        width: 300
        height: 300
        anchors.fill: parent
    
        //Кнопка
        Rectangle {
            id: button //Имя кнопки
    
            //Размещаем в центре
            x: parent.width / 2 - button.width / 2;
            y: parent.height / 2 - button.height / 2;
    
            //Размеры кнопки
            width: 100
            height: 30
    
            //Цвет кнопки
            color: "gray"
    
            //Текст кнопки
            Text {
                id: buttonLabel
                text: "Пуск"
                anchors.centerIn: parent;
            }
    
            //Действие мыши
            MouseArea {
                anchors.fill: parent
                id: mouseArea
            }
        }
        
        //Строка ввода
            Rectangle {
                id: textinputRect //Имя строки ввода
        
                //Размещаем ниже
                x: parent.width / 2 - button.width / 2;
                y: parent.height / 2 - button.height / 2+40;
        
                //Размеры строки ввода
                width: 100
                height: 18
        
                //цвет строки ввода
                color: "gray"
        
                TextInput {
                    id: textinput
                    objectName: "textinput"
                    color: "#151515";
                    selectionColor: "blue"
                    font.pixelSize: 12;
                    width: parent.width-4
                    anchors.centerIn: parent
                    focus: true
                    text:"1"
                    }
            }
    }

    Обратите внимание на следующее:
    • Для того, чтобы мы смогли обращаться к строке ввода из С++ у нас кроме параметра id есть еще параметр objectName, который заключается в двойные кавычки.
    • Информация, которая нам потом будет нужна, содержится в параметре text.


    При запуске получим следующее:


    26. Добавим поле вывода, куда программа будет выводить ответ с именем memo.

        //Поле вывода
        Rectangle {
            id: memoRect //Имя поля вывода
    
            //Размещаем ниже
            x: parent.width / 2 - button.width / 2;
            y: parent.height / 2 - button.height / 2+70;
    
            //Размеры поле вывода
            width: 100
            height: 35
    
            //Цвет поля вывода
            color: "gray"
    
            TextEdit{
                id: memo
                objectName: "memo"
                wrapMode: TextEdit.Wrap
                width:parent.width;
                readOnly:true
            }
        }


    В результате main.qml примет вид:

    import QtQuick 1.0
    
    Rectangle {
        width: 300
        height: 300
        anchors.fill: parent
    
        //Кнопка
        Rectangle {
            id: button //Имя кнопки
    
            //Размещаем в центре
            x: parent.width / 2 - button.width / 2;
            y: parent.height / 2 - button.height / 2;
    
            //Размеры кнопки
            width: 100
            height: 30
    
            //Цвет кнопки
            color: "gray"
    
            //Текст кнопки
            Text {
                id: buttonLabel
                text: "Пуск"
                anchors.centerIn: parent;
            }
    
            //Действие мыши
            MouseArea {
                anchors.fill: parent
                id: mouseArea
            }
        }
    
        //Строка ввода
            Rectangle {
                id: textinputRect //Имя строки ввода
    
                //Размещаем ниже
                x: parent.width / 2 - button.width / 2;
                y: parent.height / 2 - button.height / 2+40;
    
                //Размеры строки ввода
                width: 100
                height: 18
    
                //цвет строки ввода
                color: "gray"
    
                TextInput {
                    id: textinput
                    objectName: "textinput"
                    color: "#151515";
                    selectionColor: "blue"
                    font.pixelSize: 12;
                    width: parent.width-4
                    anchors.centerIn: parent
                    focus: true
                    text:"1"
                    }
            }
    
            //Поле вывода
            Rectangle {
                id: memoRect //Имя поля вывода
    
                //Размещаем ниже
                x: parent.width / 2 - button.width / 2;
                y: parent.height / 2 - button.height / 2+70;
    
                //Размеры поле вывода
                width: 100
                height: 35
    
                //Цвет поля вывода
                color: "gray"
    
                TextEdit{
                    id: memo
                    objectName: "memo"
                    wrapMode: TextEdit.Wrap
                    width:parent.width;
                    readOnly:true
                }
            }
    }
    


    При запуске получим следующее:


    Итак, мы описали интерфейс нашей программы.

    C++ часть

    27. При нажатии на кнопку пока ничего не происходит. Исправим это. Для начала установим взаимосвязь между QML моделью и C++ кодом. Для этого отредактируем файл mainwindow.cpp, а именно функцию MainWindow, добавив строчки:

     //Находим корневой элемент
        Root = ui->rootObject();
        //Соединяем C++ и QML, делая видимым функции С++ через элемент window
        ui->rootContext()->setContextProperty("window", this);


    Получим следующее:

    #include "mainwindow.h"
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent)
    {
        //Включаем наш QML
        ui = new QDeclarativeView;
        ui->setSource(QUrl("qrc:/main.qml"));
        setCentralWidget(ui);
        ui->setResizeMode(QDeclarativeView::SizeRootObjectToView);
        
        //Находим корневой элемент
        Root = ui->rootObject();
        //Соединяем C++ и QML, делая видимым функции С++ через элемент window
        ui->rootContext()->setContextProperty("window", this);
    }
    
    MainWindow::~MainWindow()
    {
        //Удаляем QML
        delete ui;
    }


    28. В добавленном коде у нас присутствует необъявленная переменная Root . Через нее мы потом будем производить поиск всех других дочерних элементов. Объявим ее в mainwindow.h в классе в разделе private:

    QObject *Root;//корневой элемент QML модели


    В результате получим:

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include <QtDeclarative/QDeclarativeView>
    #include <QGraphicsObject>
    #include <QtGui>
    #include <QDeclarativeContext>
    
    namespace Ui {
        class MainWindow;
    }
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
    
    private:
        QDeclarativeView *ui;
        QObject *Root;//корневой элемент QML модели
    };
    
    #endif // MAINWINDOW_H


    29. Пусть у нас кнопка из QML будет вызывать С++ функцию под произвольным именем FunctionC. Объявим ее в mainwindow.h в классе в разделе public:

    Q_INVOKABLE void FunctionC();//Функция C++ вызываемая из QML


    В результате получим:

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include <QtDeclarative/QDeclarativeView>
    #include <QGraphicsObject>
    #include <QtGui>
    #include <QDeclarativeContext>
    
    namespace Ui {
        class MainWindow;
    }
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
        
        Q_INVOKABLE void FunctionC();//Функция C++ вызываемая из QML
    
    private:
        QDeclarativeView *ui;
        QObject *Root;//корневой элемент QML модели
    };
    
    #endif // MAINWINDOW_H


    Обратите внимание на ключевое слово Q_INVOKABLE. Именно оно делает видимой функцию для QML.

    30. Теперь опишем нашу функцию в mainwindow.cpp:

    void MainWindow::FunctionC()
    {
        //Найдем строку ввода
        QObject* textinput = Root->findChild<QObject*>("textinput");
        
        //Найдем поле вывода 
        QObject* memo = Root->findChild<QObject*>("memo");
        
        QString str;//Создадим новую строковую переменную
        
        //Считаем информацию со строки ввода через свойство text
        str=(textinput->property("text")).toString();
        
        int a;
        a=str.toInt();//Переведем строку в число
        a++;//Добавим к числу 1
        
        QString str2;//Создадим еще одну строковую переменную
        str2=QString::number(a);//Переведем число в строку
        
        //Ну и наконец выведем в поле вывода нашу информацию
        memo->setProperty("text", str+"+1="+str2);
    }


    31. Ну и наконец добавим нашу функцию в обработчике нашей кнопки QML в файле main.qml. Обработчик действий мыши сейчас выглядит так:

    //Действие мыши
            MouseArea {
                anchors.fill: parent
                id: mouseArea
            }


    Теперь же он станет таким:

            //Действие мыши
            MouseArea {
                anchors.fill: parent
                id: mouseArea
                //При нажатии вызвать фугкцию window.FunctionC()
                onClicked: window.FunctionC()
            }


    В итоге получим:

    import QtQuick 1.0
    
    Rectangle {
        width: 300
        height: 300
        anchors.fill: parent
    
        //Кнопка
        Rectangle {
            id: button //Имя кнопки
    
            //Размещаем в центре
            x: parent.width / 2 - button.width / 2;
            y: parent.height / 2 - button.height / 2;
    
            //Размеры кнопки
            width: 100
            height: 30
    
            //Цвет кнопки
            color: "gray"
    
            //Текст кнопки
            Text {
                id: buttonLabel
                text: "Пуск"
                anchors.centerIn: parent;
            }
    
            //Действие мыши
            MouseArea {
                anchors.fill: parent
                id: mouseArea
                //При нажатии вызвать фугкцию window.FunctionC()
                onClicked: window.FunctionC()
            }
        }
    
        //Строка ввода
            Rectangle {
                id: textinputRect //Имя строки ввода
    
                //Размещаем ниже
                x: parent.width / 2 - button.width / 2;
                y: parent.height / 2 - button.height / 2+40;
    
                //Размеры строки ввода
                width: 100
                height: 18
    
                //цвет строки ввода
                color: "gray"
    
                TextInput {
                    id: textinput
                    objectName: "textinput"
                    color: "#151515";
                    selectionColor: "blue"
                    font.pixelSize: 12;
                    width: parent.width-4
                    anchors.centerIn: parent
                    focus: true
                    text:"1"
                    }
            }
    
            //Поле вывода
            Rectangle {
                id: memoRect //Имя поля вывода
    
                //Размещаем ниже
                x: parent.width / 2 - button.width / 2;
                y: parent.height / 2 - button.height / 2+70;
    
                //Размеры поле вывода
                width: 100
                height: 35
    
                //Цвет поля вывода
                color: "gray"
    
                TextEdit{
                    id: memo
                    objectName: "memo"
                    wrapMode: TextEdit.Wrap
                    width:parent.width;
                    readOnly:true
                }
            }
    }


    Обратите внимание на то, что функцию вызываем через window.

    Вот и всё! Теперь запускаем программу и нажимаем на кнопку. Если Вы всё сделали правильно и я не допустил ошибок, то Вы увидете:

    null

    Ссылка на исходники: Скачать.
    Ссылка на исполняемый файл: Скачать.