Данный пример построен на примере из книги М.Шлее «Qt профессиональное программирование на Qt» «Черепашья графика». Для лучшего понимания работы советую почитать раздел «Язык сценариев Qt Scripts».

В примере будет реализован простой терминал, в который можно вводить команды. Результат выполнения команд будет отображаться в этом же терминале. Пользовательский интерфейс будет реализован на QML.

Создадим проект Qt Quick

image

Опишем форму. Файл main.qml:

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.0

Window {
    id: window
    visible: true
    width: Screen.width/2
    height: Screen.height/2
    title: qsTr("Тест jsEnjine")

    property string consoleFontFamily: "Consolas"
    property int fontPixelSize: 14

    TextArea {
        id: textAreaLog
        anchors.bottom: rectangle.top
        anchors.bottomMargin: 3
        anchors.right: parent.right
        anchors.rightMargin: 3
        anchors.left: parent.left
        anchors.leftMargin: 3
        anchors.top: parent.top
        anchors.topMargin: 3
        readOnly: true
    }

    Rectangle {
        id: rectangle
        height: 25
        anchors.right: parent.right
        anchors.rightMargin: 3
        anchors.left: parent.left
        anchors.leftMargin: 3
        anchors.bottom: parent.bottom
        anchors.bottomMargin: 3
        border.color: "#0c0a0a"

        TextEdit {
            id: textEditInput
            anchors.right: parent.right
            anchors.rightMargin: 5
            anchors.left: parent.left
            anchors.leftMargin: 5
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 5
            anchors.top: parent.top
            anchors.topMargin: 5
            font.pixelSize: fontPixelSize
        }
    }
}

Форма

image

Добавим в проект классы AppCore и Console, немного допишем main.c

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

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();
}

appcore.h


#ifndef APPCORE_H
#define APPCORE_H

#include <QObject>
#include <QJSEngine>
#include "console.h"

class AppCore : public QObject
{
    Q_OBJECT
public:
    explicit AppCore(QObject *parent = nullptr);
private:
    QJSEngine   appScriptEngine;
    Console     *userConsole;
signals:
    Q_INVOKABLE void appEndTextArea(const QString& text);
    Q_INVOKABLE void clearTextArea();
public slots:
    Q_INVOKABLE void slotEvaluate(const QString& code);
};
#endif // APPCORE_H

appcore.c


#include "appcore.h"

AppCore::AppCore(QObject *parent) : QObject(parent)
{
    userConsole = new Console(this);
    QJSValue val = appScriptEngine.newQObject(userConsole);
    appScriptEngine.globalObject().setProperty("console",val);

    connect(userConsole, SIGNAL(appEndTextArea(QString)),this,SIGNAL(appEndTextArea(QString)));
    connect(userConsole, SIGNAL(clearTextArea()),this,SIGNAL(clearTextArea()));
}

void AppCore::slotEvaluate(const QString& code)
{
    QJSValue result = appScriptEngine.evaluate(code);
    if(result.isError()){
        QString er = QString("Ошибка в строке %1: %2").arg(result.property("lineNumber").toInt()).arg(result.toString());
        emit appEndTextArea(er);
    }
}

console.h


#ifndef CONSOLE_H
#define CONSOLE_H

#include <QObject>

class Console : public QObject
{
    Q_OBJECT
public:
    explicit Console(QObject *parent = nullptr);

    Q_INVOKABLE void log(const QString& message);
    Q_INVOKABLE void clear();
signals:
    Q_INVOKABLE void appEndTextArea(const QString& text);
    Q_INVOKABLE void clearTextArea();

};

#endif // CONSOLE_H

console.cpp


#include "console.h"

Console::Console(QObject *parent) : QObject(parent)
{

}

void Console::log(const QString& message)
{
    emit appEndTextArea(message);
}

void Console::clear()
{
    emit clearTextArea();
}

В конструкторе класса AppCore мы добавили экземпляр класса Console в QJSEngine, так же мы определили что будем обращаться к методам этого класса через «console»


QJSValue val = appScriptEngine.newQObject(userConsole);
appScriptEngine.globalObject().setProperty("console",val);

Сигнал appEndTextArea(const QString& text) — добавление текста в textAreaLog в
main.qml.

Сигнал clearTextArea() — очистить область вывода текста textAreaLog в main.qml.

Слот slotEvaluate(const QString& code) — выполнение js кода, введенного в textEditInput в main.qml.

Допишем в main.qml обработчики сигналов:


Connections{
        target: appCore
        onAppEndTextArea:{
            textAreaLog.cursorPosition = textAreaLog.length;
            textAreaLog.append(text);
        }
        onClearTextArea:{
            textAreaLog.cursorPosition=0;
            textAreaLog.text = "";
        }
    }

Так же допишем в textEditInput сигнал, вызова слота void slotEvaluate(const QString& code):


TextEdit {
            id: textEditInput
            anchors.right: parent.right
            anchors.rightMargin: 5
            anchors.left: parent.left
            anchors.leftMargin: 5
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 5
            anchors.top: parent.top
            anchors.topMargin: 5
            font.pixelSize: fontPixelSize

            Keys.onReturnPressed:{
                if(textEditInput.text == "")return;
                appCore.slotEvaluate(text)
                clear()
            }
            Keys.onEscapePressed: clear()
        }

Теперь при вводе команды в textEditInput и нажатии Enter команда будет передана в slotEvaluate в AppCore. При нажатии ESC поле textEditInput очистится.

При запуске приложения, если мы введем в textEditInput команду console.log(«Hello world»), то в поле textAreaLog увидим Hello world. Если ввести команду неизвестную QJSEngine, то в терминале высветится ошибка.

image

Таким образом, мы написали приложение, в котором мы можем вызывать методы подключенных к QJSEngine классов и выводить результаты выполнения этих методов. Кто то скажет «А при чем тут JS? Ведь классы написаны на С++?», но это уже другая история…

Ссылка проекта на github

Комментарии (0)