IT Notes

Qt Script: Введение

В Qt предусмотрен мощный механизм расширения функциональности приложений с помощью JavaScript-подобного языка сценариев, поддержка которого реализована в модуле Qt Script.

Рассмотрим пример использования этой технологии, в котором задействуем следующие возможности Qt Script:

  1. Считывание и запись свойств Qt-объектов;
  2. Установление соединения между сигналом и слотом Qt-объектов;
  3. Создание функции-слота внутри скрипта и его подключение к сигналу Qt-объекта.

У нас должно получиться следующее:

qt-script-demo-sample

Мы обеспечим доступ скрипта к полям ввода/вывода (QLineEdit). Для редактирования скрипта предусмотрим текстовое поле QTextEdit. При этом в ресурсах приложения мы поместим три заранее подготовленных простых скрипта, о которых скоро поговорим.

Реализация

Обратите внимание , что для работы с Qt Script в файле проекта необходимо подключить соответствующую зависимость в дополнение к остальным:

QT += core gui script

Файл mainwidget.h:

#ifndef MAINWIDGET_H
#define MAINWIDGET_H

#include <QWidget>
#include <QtScript/QScriptEngine>

namespace Ui {
class MainWidget;
}

class MainWidget : public QWidget {
    Q_OBJECT

public:
    explicit MainWidget( QWidget* parent = 0 );
    ~MainWidget();

private slots:
    void loadScript();
    void runScript();

private:
    Ui::MainWidget* ui;

    QScriptEngine m_engine;

};

#endif // MAINWIDGET_H

В основе Qt Script лежит класс QScriptEngine, который и занимается интерпретацией кода скрипта. Именно его мы определили в качестве одного из полей нашего класса.

Файл mainwidget.cpp:

#include "mainwidget.h"
#include "ui_mainwidget.h"

#include <QFile>

MainWidget::MainWidget( QWidget* parent ) :
    QWidget( parent ),
    ui( new Ui::MainWidget ) {
    ui->setupUi( this );

    connect( ui->bnRun, SIGNAL( clicked( bool ) ), SLOT( runScript() ) );
    foreach( QRadioButton* rb, findChildren< QRadioButton* >() ) {
        connect( rb, SIGNAL( toggled( bool ) ), SLOT( loadScript() ) );
    }

    QScriptValue inputVal = m_engine.newQObject( ui->edInput );
    m_engine.globalObject().setProperty( "input", inputVal );

    QScriptValue outputVal = m_engine.newQObject( ui->edOutput );
    m_engine.globalObject().setProperty( "output", outputVal );

    loadScript();
}

MainWidget::~MainWidget() {
    delete ui;
}

void MainWidget::loadScript() {
    QString fileName = "";
    if( ui->rbUpperDemo->isChecked() ) {
        fileName = "upper_demo.js";
    } else if( ui->rbConnectDemo->isChecked() ) {
        fileName = "connect_demo.js";
    } else if( ui->rbConnectFuncDemo->isChecked() ) {
        fileName = "connect_func_demo.js";
    }

    QFile f( ":/" + fileName );
    if( f.open( QIODevice::ReadOnly ) ) {
        ui->txtScript->setText( f.readAll() );
    }
}

void MainWidget::runScript() {
    ui->edInput->disconnect();
    ui->edOutput->disconnect();
    ui->edOutput->clear();

    QScriptValue result = m_engine.evaluate( ui->txtScript->toPlainText() );
    if( result.isError() ) {
        ui->edOutput->setText( result.toString() );
    }
}

Особый интерес представляет функция-член:

void MainWidget::runScript() {
    ui->edInput->disconnect();
    ui->edOutput->disconnect();
    ui->edOutput->clear();

    QScriptValue result = m_engine.evaluate( ui->txtScript->toPlainText() );
    if( result.isError() ) {
        ui->edOutput->setText( result.toString() );
    }
}

Сначала мы подчищаем следы запуска прошлых скриптов (отключаем сигналы и очищаем содержимое поля вывода). Затем следует вызов скрипта с помощью QScriptEngine::evaluate(). В случае синтаксических ошибок мы выводим текст сообщения о проблеме в поле вывода.

Также важно заметить, что скрипт не имеет прямого доступа к элементам Qt-приложения. Поэтому мы должны передать ему все, что нужно, самостоятельно. Что мы и делаем для полей ввода/вывода в конструкторе класса:

QScriptValue inputVal = m_engine.newQObject( ui->edInput );
m_engine.globalObject().setProperty( "input", inputVal );

QScriptValue outputVal = m_engine.newQObject( ui->edOutput );
m_engine.globalObject().setProperty( "output", outputVal );

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

Замечание: мы несколько превышаем потребности скрипта и даем ему больше, чем нужно. Например, скрипт сможет изменять содержимое поля input, хотя должен уметь только читать его. Но пока что не будем обращать на это внимания.

Примеры скриптов

Первый скрипт upper_demo.js копирует текст из input в output, преобразуя его к верхнему регистру:

output.text = input.text.toUpperCase();

Второй скрипт connect_demo.js выполняет соединение сигнала textChanged() поля input со слотом setText() поля output:

input.textChanged.connect( output.setText );

Запуск этого скрипта равносилен выполнению обычной привязки сигналов-слотов в Qt:

connect( ui->edInput, SIGNAL( textChanged( QString ) ), ui->edOutput, SLOT( setText( QString ) ) );

Последний скрипт connect_func_demo.js:

function onTextChanged( text ) {
    output.text = text.toUpperCase();
}

input.textChanged.connect( onTextChanged );

В нем мы определяем свою функцию onTextChanged(), которая принимает параметр text. В теле функции мы делаем то же самое, что и ранее в скрипте upper_demo.js.

Само подключение нашей функции в качестве слота аналогично тому, что мы делали в connect_demo.js.

Исходники

 Скачать пример использования Qt Script

Понравилась статья?
Не забудь поделиться ей с друзьями!

Похожие публикации

Комментарии

Вы, случайно, не знаете алгоритм разбора SVGZ файлов? По документации это просто архив, но, совершенно непонятно, как достать все SVG файлы из него по одиночке.

Anonymous:

Вы, случайно, не знаете алгоритм разбора SVGZ файлов? По документации это просто архив, но, совершенно непонятно, как достать все SVG файлы из него по одиночке.

Здравствуйте. Если можете, то пришлите сюда ссылку на пример конкретного SVGZ-файла, с которым хотите работать

Проще посмотреть в своем дистрибутиве. Допустим, Ancient_Egyptians.svgz Это - из пакета kde-apps/libkdegames. Просто группа карт. Хотелось бы разобрать, но не знаю как. Можно, конечно, изучить, как их оттуда извлекают сами игры, типа KPatience, но, вдруг, есть готовое описание или пример.

Anonymous:

Проще посмотреть в своем дистрибутиве. Допустим, Ancient_Egyptians.svgz Это - из пакета kde-apps/libkdegames. Просто группа карт. Хотелось бы разобрать, но не знаю как. Можно, конечно, изучить, как их оттуда извлекают сами игры, типа KPatience, но, вдруг, есть готовое описание или пример.

Вероятно, они извлекаются именно так, как это сделано в следующем коде (на Java): https://github.com/b0n541/jskat-cardextractor/blob/master/src/main/java/org/jskat/extract/CardExtractor.java.

Суть заключается в вырезании нужных областей (с картами) из большой SVG-картинки, которые затем сохраняются на диск в каталог tmp/ в формате PNG. Хотя их вполне можно держать и в памяти.

RSS RSS-рассылка

Популярное

Дешевый хостинг