使用脚本拓展CPP应用程序之——JS脚本

使用脚本拓展CPP应用程序之——JS脚本

原文链接:https://blog.csdn.net/weixin_39568531/article/details/106303558

在前面的一篇文章中,向大家介绍过《基于Qt实现的自定义简易Lua脚本编辑器》,实际上就是使用Lua脚本拓展cpp应用程序。本文着重向大家分享使用javaScript脚本拓展CPP应用程序的方法。在接下来的多语言混合编程系列博文中,我还将和大家一起分享使用python、go以及shell(windows脚本和Linux脚本)拓展CPP应用程序的方法。

开始正题前,咱们先演示下效果。
在这里插入图片描述

一、项目准备

编程环境:Qt4.8.3 + MingW

编程语言:C++ 及JavaScript

二、项目实现

1、新建一个Qt应用程序WxCPP,在pro文件中修改,添加 scipt 模块

#-------------------------------------------------
#
# Project created by QtCreator 2020-05-23T12:11:00
#
#-------------------------------------------------
 
QT       += core gui script
 
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
 
TARGET = WxCPP
TEMPLATE = app
 
INCLUDEPATH += ./
 
SOURCES +=\
        wxcpp.cpp \
    wxcpp_main.cpp \
    wxform.cpp
 
HEADERS  += wxcpp.h \
    wxform.h
 
FORMS += \
    wxform.ui

2、添加一个ui界面类,并完成主体的ui设计部分。参照文章开始的演示图片。

3、实现js脚本拓展CPP应用程序的主要逻辑

// WxForm.h
 
#ifndef WXFORM_H
#define WXFORM_H
 
#include <QWidget>
#include <QString>
#include <wxcpp.h>
#include <QVariant>
 
 
namespace Ui {
class WxForm;
}
 
class WxForm : public QWidget
{
    Q_OBJECT
 
public:
    explicit WxForm(QWidget *parent = 0);
    ~WxForm();
 
    bool showScriptContext(QString fileName);
private slots:
    void on_openScript_pushButton_clicked();
 
    void on_execScript_pushButton_clicked();
 
    void on_getValueFromJs(QVariant variant);
 
    void on_closeScriptExec_pushButton_clicked();
 
    void on_save_pushButton_clicked();
 
    void on_saveAs_pushButton_clicked();
 
    void on_scriptFinished();
 
private:
    Ui::WxForm *ui;
    QString m_curScriptFilePath;
    WxCPP wxcpp;
 
    QVariant m_ret; 
};
 
#endif // WXFORM_H
// WxForm.cpp
 
#include "wxform.h"
#include "ui_wxform.h"
#include <QFileDialog>
#include <QFile>
#include <QTextStream>
#include <QTranslator>
#include <QtScript>
#include <iostream>
#include <time.h>
#include <stdio.h>
#ifdef Q_OS_WIN
#include <windows.h>
#endif
#include <QMutex>
#include <QTextStream>
 
WxForm::WxForm(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::WxForm)
{
    ui->setupUi(this);
    ui->spinBox->setRange(1000,10000);
    ui->spinBox->setSingleStep(1000);
 
    m_ret = QVariant(0);
    QObject::connect(ui->scriptExecType_comboBox,SIGNAL(currentIndexChanged(int)),&wxcpp,SLOT(setLoopFlag(int)));
    QObject::connect(&wxcpp,SIGNAL(getValFromJs(QVariant)),this,SLOT(on_getValueFromJs(QVariant)));
    QObject::connect(&wxcpp,SIGNAL(scriptFinished()),this,SLOT(on_scriptFinished()));
    QObject::connect(ui->spinBox,SIGNAL(valueChanged(int)),&wxcpp,SLOT(setLoopInterval(int)));
 
 
}
 
WxForm::~WxForm()
{
    if(ui->scriptExecType_comboBox->currentIndex() == 1){ // 优雅的结束线程
        qDebug() << __FUNCTION__;
        wxcpp.setLoopFlag(0);
#ifdef Q_OS_WIN
        ::Sleep(1000);
#else
        sleep(1);
#endif
    }
 
    delete ui;
}
 
 
void WxForm::on_openScript_pushButton_clicked()
{
    QString fileName = QFileDialog::getOpenFileName(this,
                                                    tr("Open Script"), qApp->applicationDirPath(), tr("Script Files (*.js *.py *.lua)"));
    ui->scriptPath_lineEdit->setText(fileName);
    m_curScriptFilePath = fileName;
    showScriptContext(fileName);
}
 
 
bool WxForm::showScriptContext(QString fileName)
{
    QMutex mutex;
    mutex.lock();
    QFile file(fileName);
    if(!file.open(QIODevice::Text | QIODevice::ReadWrite)){
        mutex.unlock();
        return false;
    }
    ui->plainTextEdit->clear();
    ui->plainTextEdit->appendPlainText(file.readAll());
    file.close();
    mutex.unlock();
    return true;
}
 
 
void WxForm::on_execScript_pushButton_clicked()
{
    if(m_curScriptFilePath.isEmpty()){
        ui->textEdit->append("[error]: Please select one javaScript file at first!");
        return;
    }
 
    ui->execScript_pushButton->setEnabled(false);
    ui->scriptType_comboBox->setEnabled(false);
    ui->openScript_pushButton->setEnabled(false);
 
    wxcpp.setFileName(m_curScriptFilePath);
    wxcpp.setValue(m_ret);
    wxcpp.setLoopFlag(ui->scriptExecType_comboBox->currentIndex());
    wxcpp.setLoopInterval(ui->spinBox->value());
    wxcpp.setScriptType(ui->scriptType_comboBox->currentIndex());
 
    wxcpp.start();
}
 
void WxForm::on_getValueFromJs(QVariant variant)
{
    m_ret = variant;
    ui->textEdit->append(QString("[javaScript]: %1").arg(m_ret.toInt()));
}
 
 
void WxForm::on_closeScriptExec_pushButton_clicked()
{
    wxcpp.setLoopFlag(0);
}
 
 
 
void saveTextToFile(QString& text,QString filename){
    QMutex mutex;
    mutex.lock();
    QFile file(filename);
    if(!file.open(QIODevice::Text | QIODevice::WriteOnly)) {
        mutex.unlock();
        return;
    }
    QTextStream os(&file);
    os << text;
    file.close();
    mutex.unlock();
}
 
void WxForm::on_save_pushButton_clicked()
{
    QString context = ui->plainTextEdit->toPlainText();
    saveTextToFile(context,m_curScriptFilePath);
}
 
void WxForm::on_saveAs_pushButton_clicked()
{
    QString context = ui->plainTextEdit->toPlainText();
    QString fileName = QFileDialog::getSaveFileName(this,
                                                    tr("Save Script"), qApp->applicationDirPath(), tr("Script Files (*.js *.py *.lua)"));
 
    if(fileName.isEmpty()) return;
    saveTextToFile(context,fileName);
 
}
 
void WxForm::on_scriptFinished()
{
    ui->execScript_pushButton->setEnabled(true);
    ui->scriptType_comboBox->setEnabled(true);
    ui->openScript_pushButton->setEnabled(true);
}

脚本执行部分,放在独立的线程中执行,线程中将执行结果传递给GUI主线程,并在输出控件中显示输出结果。

// wxcpp.h
 
#ifndef WXCPP_H
#define WXCPP_H
 
#include <QObject>
#include <QThread>
#include <QString>
#include <QVariant>
 
class WxCPP : public QThread
{
    Q_OBJECT
 
public:
    WxCPP(QObject *parent = 0);
    ~WxCPP();
 
    void run();
    static int loadJavaScript(QString filename, QVariant &ret);
 
    void setFileName(QString filename){ m_fileName = filename; }
    void setValue(QVariant& variant){ m_variant = variant;}
    void setScriptType(int type = 0){ m_scriptype = type;}
public slots:
    void setLoopFlag(int flag = 0){ m_execFlag = flag;}
    void setLoopInterval(int interval = 1000){m_interval = interval ;}
signals:
    void getValFromJs(QVariant variant);
    void scriptFinished();
public:
    QString m_fileName;
    QVariant m_variant;
    int m_scriptype;
    int m_execFlag;
    int m_interval;
};
 
#endif // WXCPP_H
 
// wxcpp.cpp
 
 
#include "wxcpp.h"
#include <QTextStream>
#include <QTranslator>
#include <QtScript>
#include <QMessageBox>
#include <QDebug>
#include <stdlib.h>
#include <iostream>
#include <time.h>
#include <stdio.h>
#ifdef Q_OS_WIN
#include <windows.h>
#endif
#include <QMutex>
 
WxCPP::WxCPP(QObject *parent)
    : QThread(parent)
{
 
}
 
WxCPP::~WxCPP()
{
 
}
 
void WxCPP::run()
{
    do{
        switch (m_scriptype) {
        case 0: // javascript
             loadJavaScript(m_fileName,m_variant);
            break;
        default:
            break;
        }
 
        emit getValFromJs(m_variant);
        if(m_execFlag){
#ifdef Q_OS_WIN
            ::Sleep(m_interval);
#else
            sleep(m_interval/1000);
#endif
        }
    }while(m_execFlag);
 
    emit scriptFinished();
}
 
int WxCPP::loadJavaScript(QString filename,QVariant& ret)
{
    QScriptEngine engine;
    engine.installTranslatorFunctions();
    //! [1]
 
    //! [2]
    QScriptValue iValue = engine.newVariant(ret);
    engine.globalObject().setProperty("i", iValue);
 
    //! [2]
 
    //! [3]
    QMutex mutex;
    mutex.lock();
    QFile scriptFile(filename);
    scriptFile.open(QIODevice::ReadOnly);
    QTextStream stream(&scriptFile);
    QString contents = stream.readAll();
    scriptFile.close();
    mutex.unlock();
 
    //! [3]
 
 
    //! [4]
    QScriptValue result = engine.evaluate(contents, filename);
    ret = result.toVariant();
    qDebug() << "javaScript exec result:" << ret.toInt();
    //! [4]
 
    //! [5]
    if (result.isError()) {
        QMessageBox::critical(0, "Java Script",
                              QString::fromLatin1("%0:%1: %2")
                              .arg(filename)
                              .arg(result.property("lineNumber").toInt32())
                              .arg(result.toString()));
        return -1;
    }
    engine.abortEvaluation();
}

三、项目总结

1、本篇核心为JS脚本拓展CPP应用程序,即CPP应用程序(后面简称应用程序)在运行过程中,可动态检测js脚本,执行脚本文件,并将执行结果传递给CPP程序。本文以传递一个QVariant类型的变量为例,实际上QtScript模块可用于与JS交互的还可以是一个对象(包括自定义的继承于QObject类的对象),并在JS中对对象进行操作。

2、拓展脚本的运行我们考虑放在独立的线程中执行,可以避免主GUI线程卡界面的问题,软件更加流畅,效率更好。其次实现脚本的两种运行模式:普通脚本(单次执行),循环脚本(指定间隔时间循环执行),状态可动态切换,这里也运用了主流的优雅开启和关闭Qt线程的方法之一。

3、JS脚本动态修改,程序动态执行和运行。即可以在脚本运行过程中修改脚本内容,保存后,将自动执行最新的脚本程序。这里其实稍微重要的地方就是对文件的读写加锁。其他的小细节不做过多介绍了。有需要的朋友可以自行学习涉及到的功能模块。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值